connector.rs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. sync::{atomic::Ordering, Arc},
  20. time::Duration,
  21. };
  22. use futures::{
  23. future::{select, Either},
  24. pin_mut,
  25. };
  26. use log::warn;
  27. use smol::lock::RwLock as AsyncRwLock;
  28. use url::Url;
  29. use super::{
  30. channel::{Channel, ChannelPtr},
  31. hosts::HostColor,
  32. session::SessionWeakPtr,
  33. settings::Settings,
  34. transport::Dialer,
  35. };
  36. use crate::{system::CondVar, Error, Result};
  37. /// Create outbound socket connections
  38. pub struct Connector {
  39. /// P2P settings
  40. settings: Arc<AsyncRwLock<Settings>>,
  41. /// Weak pointer to the session
  42. pub session: SessionWeakPtr,
  43. /// Stop signal that aborts the connector if received.
  44. stop_signal: CondVar,
  45. }
  46. impl Connector {
  47. /// Create a new connector with given network settings
  48. pub fn new(settings: Arc<AsyncRwLock<Settings>>, session: SessionWeakPtr) -> Self {
  49. Self { settings, session, stop_signal: CondVar::new() }
  50. }
  51. /// Establish an outbound connection
  52. pub async fn connect(&self, url: &Url) -> Result<(Url, ChannelPtr)> {
  53. let hosts = self.session.upgrade().unwrap().p2p().hosts();
  54. if hosts.container.contains(HostColor::Black as usize, url) || hosts.block_all_ports(url) {
  55. warn!(target: "net::connector::connect", "Peer {} is blacklisted", url);
  56. return Err(Error::ConnectFailed)
  57. }
  58. let settings = self.settings.read().await;
  59. let transports = settings.allowed_transports.clone();
  60. let transport_mixing = settings.transport_mixing;
  61. let datastore = settings.p2p_datastore.clone();
  62. let outbound_connect_timeout = settings.outbound_connect_timeout;
  63. let i2p_socks5_proxy = settings.i2p_socks5_proxy.clone();
  64. let tor_socks5_proxy = settings.tor_socks5_proxy.clone();
  65. drop(settings);
  66. let mut endpoint = url.clone();
  67. let scheme = endpoint.scheme();
  68. if !transports.contains(&scheme.to_string()) && transport_mixing {
  69. if transports.contains(&"tor".to_string()) && scheme == "tcp" {
  70. endpoint.set_scheme("tor")?;
  71. } else if transports.contains(&"tor+tls".to_string()) && scheme == "tcp+tls" {
  72. endpoint.set_scheme("tor+tls")?;
  73. } else if transports.contains(&"nym".to_string()) && scheme == "tcp" {
  74. endpoint.set_scheme("nym")?;
  75. } else if transports.contains(&"nym+tls".to_string()) && scheme == "tcp+tls" {
  76. endpoint.set_scheme("nym+tls")?;
  77. } else if transports.contains(&"socks5".to_string()) &&
  78. (scheme == "tcp" || scheme == "tor")
  79. {
  80. endpoint.set_path(&format!(
  81. "{}:{}",
  82. endpoint.host().unwrap(),
  83. endpoint.port().unwrap()
  84. ));
  85. endpoint.set_host(tor_socks5_proxy.host_str())?;
  86. endpoint.set_port(tor_socks5_proxy.port())?;
  87. endpoint.set_username(tor_socks5_proxy.username())?;
  88. endpoint.set_password(tor_socks5_proxy.password())?;
  89. endpoint.set_scheme("socks5")?;
  90. } else if transports.contains(&"socks5+tls".to_string()) &&
  91. (scheme == "tcp+tls" || scheme == "tor+tls")
  92. {
  93. endpoint.set_path(&format!(
  94. "{}:{}",
  95. endpoint.host().unwrap(),
  96. endpoint.port().unwrap()
  97. ));
  98. endpoint.set_host(tor_socks5_proxy.host_str())?;
  99. endpoint.set_port(tor_socks5_proxy.port())?;
  100. endpoint.set_username(tor_socks5_proxy.username())?;
  101. endpoint.set_password(tor_socks5_proxy.password())?;
  102. endpoint.set_scheme("socks5+tls")?;
  103. }
  104. }
  105. let dialer = Dialer::new(endpoint.clone(), datastore, Some(i2p_socks5_proxy)).await?;
  106. let timeout = Duration::from_secs(outbound_connect_timeout);
  107. let stop_fut = async {
  108. self.stop_signal.wait().await;
  109. };
  110. let dial_fut = async { dialer.dial(Some(timeout)).await };
  111. pin_mut!(stop_fut);
  112. pin_mut!(dial_fut);
  113. match select(dial_fut, stop_fut).await {
  114. Either::Left((Ok(ptstream), _)) => {
  115. let channel = Channel::new(
  116. ptstream,
  117. Some(endpoint.clone()),
  118. url.clone(),
  119. self.session.clone(),
  120. )
  121. .await;
  122. Ok((endpoint, channel))
  123. }
  124. Either::Left((Err(e), _)) => {
  125. // If we get ENETUNREACH, we don't have IPv6 connectivity so note it down.
  126. if e.raw_os_error() == Some(libc::ENETUNREACH) {
  127. self.session
  128. .upgrade()
  129. .unwrap()
  130. .p2p()
  131. .hosts()
  132. .ipv6_available
  133. .store(false, Ordering::SeqCst);
  134. }
  135. Err(e.into())
  136. }
  137. Either::Right((_, _)) => Err(Error::ConnectorStopped),
  138. }
  139. }
  140. pub(crate) fn stop(&self) {
  141. self.stop_signal.notify()
  142. }
  143. }