| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use std::{
- sync::{atomic::Ordering, Arc},
- time::Duration,
- };
- use futures::{
- future::{select, Either},
- pin_mut,
- };
- use log::warn;
- use smol::lock::RwLock as AsyncRwLock;
- use url::Url;
- use super::{
- channel::{Channel, ChannelPtr},
- hosts::HostColor,
- session::SessionWeakPtr,
- settings::Settings,
- transport::Dialer,
- };
- use crate::{system::CondVar, Error, Result};
- /// Create outbound socket connections
- pub struct Connector {
- /// P2P settings
- settings: Arc<AsyncRwLock<Settings>>,
- /// Weak pointer to the session
- pub session: SessionWeakPtr,
- /// Stop signal that aborts the connector if received.
- stop_signal: CondVar,
- }
- impl Connector {
- /// Create a new connector with given network settings
- pub fn new(settings: Arc<AsyncRwLock<Settings>>, session: SessionWeakPtr) -> Self {
- Self { settings, session, stop_signal: CondVar::new() }
- }
- /// Establish an outbound connection
- pub async fn connect(&self, url: &Url) -> Result<(Url, ChannelPtr)> {
- let hosts = self.session.upgrade().unwrap().p2p().hosts();
- if hosts.container.contains(HostColor::Black as usize, url) || hosts.block_all_ports(url) {
- warn!(target: "net::connector::connect", "Peer {} is blacklisted", url);
- return Err(Error::ConnectFailed)
- }
- let settings = self.settings.read().await;
- let transports = settings.allowed_transports.clone();
- let transport_mixing = settings.transport_mixing;
- let datastore = settings.p2p_datastore.clone();
- let outbound_connect_timeout = settings.outbound_connect_timeout;
- let i2p_socks5_proxy = settings.i2p_socks5_proxy.clone();
- let tor_socks5_proxy = settings.tor_socks5_proxy.clone();
- drop(settings);
- let mut endpoint = url.clone();
- let scheme = endpoint.scheme();
- if !transports.contains(&scheme.to_string()) && transport_mixing {
- if transports.contains(&"tor".to_string()) && scheme == "tcp" {
- endpoint.set_scheme("tor")?;
- } else if transports.contains(&"tor+tls".to_string()) && scheme == "tcp+tls" {
- endpoint.set_scheme("tor+tls")?;
- } else if transports.contains(&"nym".to_string()) && scheme == "tcp" {
- endpoint.set_scheme("nym")?;
- } else if transports.contains(&"nym+tls".to_string()) && scheme == "tcp+tls" {
- endpoint.set_scheme("nym+tls")?;
- } else if transports.contains(&"socks5".to_string()) &&
- (scheme == "tcp" || scheme == "tor")
- {
- endpoint.set_path(&format!(
- "{}:{}",
- endpoint.host().unwrap(),
- endpoint.port().unwrap()
- ));
- endpoint.set_host(tor_socks5_proxy.host_str())?;
- endpoint.set_port(tor_socks5_proxy.port())?;
- endpoint.set_username(tor_socks5_proxy.username())?;
- endpoint.set_password(tor_socks5_proxy.password())?;
- endpoint.set_scheme("socks5")?;
- } else if transports.contains(&"socks5+tls".to_string()) &&
- (scheme == "tcp+tls" || scheme == "tor+tls")
- {
- endpoint.set_path(&format!(
- "{}:{}",
- endpoint.host().unwrap(),
- endpoint.port().unwrap()
- ));
- endpoint.set_host(tor_socks5_proxy.host_str())?;
- endpoint.set_port(tor_socks5_proxy.port())?;
- endpoint.set_username(tor_socks5_proxy.username())?;
- endpoint.set_password(tor_socks5_proxy.password())?;
- endpoint.set_scheme("socks5+tls")?;
- }
- }
- let dialer = Dialer::new(endpoint.clone(), datastore, Some(i2p_socks5_proxy)).await?;
- let timeout = Duration::from_secs(outbound_connect_timeout);
- let stop_fut = async {
- self.stop_signal.wait().await;
- };
- let dial_fut = async { dialer.dial(Some(timeout)).await };
- pin_mut!(stop_fut);
- pin_mut!(dial_fut);
- match select(dial_fut, stop_fut).await {
- Either::Left((Ok(ptstream), _)) => {
- let channel = Channel::new(
- ptstream,
- Some(endpoint.clone()),
- url.clone(),
- self.session.clone(),
- )
- .await;
- Ok((endpoint, channel))
- }
- Either::Left((Err(e), _)) => {
- // If we get ENETUNREACH, we don't have IPv6 connectivity so note it down.
- if e.raw_os_error() == Some(libc::ENETUNREACH) {
- self.session
- .upgrade()
- .unwrap()
- .p2p()
- .hosts()
- .ipv6_available
- .store(false, Ordering::SeqCst);
- }
- Err(e.into())
- }
- Either::Right((_, _)) => Err(Error::ConnectorStopped),
- }
- }
- pub(crate) fn stop(&self) {
- self.stop_signal.notify()
- }
- }
|