diff --git a/bin/app/src/plugin/mod.rs b/bin/app/src/plugin/mod.rs index c1a89257b..25c0f5796 100644 --- a/bin/app/src/plugin/mod.rs +++ b/bin/app/src/plugin/mod.rs @@ -241,10 +241,6 @@ impl PluginSettings { "net.outbound_peer_discovery_cooloff_time", PropertyValue::Uint32(p2p_settings.outbound_peer_discovery_cooloff_time as u32), ); - self.add_setting( - "net.transport_mixing", - PropertyValue::Bool(p2p_settings.transport_mixing), - ); self.add_setting("net.localnet", PropertyValue::Bool(p2p_settings.localnet)); self.add_setting( "net.greylist_refinery_interval", @@ -298,8 +294,6 @@ impl PluginSettings { .unwrap() .get_property_u32("value") .unwrap() as u64; - p2p_settings.transport_mixing = - self.get_setting("net.transport_mixing").unwrap().get_property_bool("value").unwrap(); p2p_settings.localnet = self.get_setting("net.localnet").unwrap().get_property_bool("value").unwrap(); p2p_settings.greylist_refinery_interval = self diff --git a/src/net/connector.rs b/src/net/connector.rs index 960598526..aae3fb1a3 100644 --- a/src/net/connector.rs +++ b/src/net/connector.rs @@ -64,7 +64,7 @@ impl Connector { let settings = self.settings.read().await; let transports = settings.allowed_transports.clone(); - let transport_mixing = settings.transport_mixing; + let mixed_transports = settings.mixed_transports.clone(); let datastore = settings.p2p_datastore.clone(); let outbound_connect_timeout = settings.outbound_connect_timeout; let i2p_socks5_proxy = settings.i2p_socks5_proxy.clone(); @@ -74,8 +74,26 @@ impl Connector { 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" { + if mixed_transports.contains(&scheme.to_string()) { + if transports.contains(&"socks5".to_string()) && (scheme == "tcp" || scheme == "tor") { + endpoint = tor_socks5_proxy; + endpoint.set_path(&format!( + "{}:{}", + endpoint.host().unwrap(), + endpoint.port().unwrap() + )); + endpoint.set_scheme("socks5")?; + } else if transports.contains(&"socks5+tls".to_string()) && + (scheme == "tcp+tls" || scheme == "tor+tls") + { + endpoint = tor_socks5_proxy; + endpoint.set_path(&format!( + "{}:{}", + endpoint.host().unwrap(), + endpoint.port().unwrap() + )); + endpoint.set_scheme("socks5+tls")?; + } else 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")?; @@ -83,32 +101,6 @@ impl Connector { 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")?; } } diff --git a/src/net/hosts.rs b/src/net/hosts.rs index 7d2d23e06..719d2ac6e 100644 --- a/src/net/hosts.rs +++ b/src/net/hosts.rs @@ -419,8 +419,8 @@ impl HostContainer { &self, color: HostColor, transports: &[String], - transport_mixing: bool, - tor_socks5_proxy: Url, + mixed_transports: &[String], + tor_socks5_proxy: Url ) -> Vec<(Url, u64)> { trace!(target: "net::hosts::fetch_addrs()", "[START] {color:?}"); let mut hosts = vec![]; @@ -431,7 +431,9 @@ impl HostContainer { // However, **do not** mix tor:// and tcp+tls://, nor tor+tls:// and tcp://. macro_rules! mix_transport { ($a:expr, $b:expr) => { - if transports.contains(&$a.to_string()) && transport_mixing { + if transports.contains(&$a.to_string()) && + mixed_transports.contains(&$b.to_string()) + { let mut a_to_b = self.fetch_with_schemes(index, &[$b.to_string()], None); for (addr, last_seen) in a_to_b.iter_mut() { addr.set_scheme($a).unwrap(); @@ -443,20 +445,19 @@ impl HostContainer { macro_rules! mix_socks5_transport { ($a:expr, $b:expr) => { - if transports.contains(&$a.to_string()) && transport_mixing { + if transports.contains(&$a.to_string()) && + mixed_transports.contains(&$b.to_string()) + { let mut a_to_b = self.fetch_with_schemes(index, &[$b.to_string()], None); for (addr, last_seen) in a_to_b.iter_mut() { - addr.set_path(&format!( + let mut endpoint = tor_socks5_proxy.clone(); + endpoint.set_path(&format!( "{}:{}", addr.host().unwrap(), addr.port().unwrap() )); - addr.set_host(tor_socks5_proxy.host_str()).unwrap(); - addr.set_port(tor_socks5_proxy.port()).unwrap(); - addr.set_username(tor_socks5_proxy.username()).unwrap(); - addr.set_password(tor_socks5_proxy.password()).unwrap(); - addr.set_scheme($a).unwrap(); - hosts.push((addr.clone(), last_seen.clone())); + endpoint.set_scheme($a).unwrap(); + hosts.push((endpoint, last_seen.clone())); } } }; @@ -471,8 +472,13 @@ impl HostContainer { mix_socks5_transport!("socks5", "tor"); mix_socks5_transport!("socks5+tls", "tor+tls"); + // Filter out a transport from requested transport if we set it to be mixed as + // we don't want to connect directly to that host + let transports: Vec = + transports.iter().filter(|tp| !mixed_transports.contains(tp)).cloned().collect(); + // And now the actual requested transports - for (addr, last_seen) in self.fetch_with_schemes(index, transports, None) { + for (addr, last_seen) in self.fetch_with_schemes(index, &transports, None) { hosts.push((addr, last_seen)); } @@ -1238,7 +1244,13 @@ impl Hosts { /// stored in the blacklist without a port, and if so, it will return /// true. pub(in crate::net) fn block_all_ports(&self, url: &Url) -> bool { - let host = url.host().unwrap(); + let host = url.host(); + if host.is_none() { + // the url is a unix socket or an invalid address so it won't be in hostlist + return false + } + + let host = host.unwrap(); self.container.hostlists[HostColor::Black as usize] .read() .unwrap() @@ -1385,13 +1397,8 @@ impl Hosts { let day = 86400; self.container.refresh(HostColor::Dark, day); - // If transport mixing is disabled or Socks5 transport is not allowed we will not connect to this host - if !settings.transport_mixing || - !settings - .allowed_transports - .iter() - .any(|t| t == "socks5" || t == "socks5+tls") - { + // If the scheme is not found in mixed_transports we can not connect to this host + if !settings.mixed_transports.contains(&addr_.scheme().to_string()) { continue; } } @@ -1904,4 +1911,79 @@ mod tests { assert!(Hosts::is_i2p_host("node.dark.fi.i2p")); assert!(!Hosts::is_i2p_host("node.dark.fi")); } + fn test_transport_tor_mixed_with_tcp_fetch() { + let host_container = HostContainer::new(); + host_container.store_or_update( + HostColor::Grey, + Url::parse("tcp://dark.fi:28880").unwrap(), + 0, + ); + + let fetched_hosts = host_container.fetch( + HostColor::Grey, + &["tor+tls".to_string(), "tcp".to_string(), "tor".to_string()], + &["tcp".to_string()], + Url::parse("socks5://127.0.0.1:9050").unwrap(), + ); + + // test tcp endpoint is changed to tor and tcp will not be used to + // connect to any host directly + assert_eq!(fetched_hosts.len(), 1); + assert_eq!(fetched_hosts[0].0.to_string(), "tor://dark.fi:28880/"); + } + + #[test] + fn test_transport_socks5_mixed_with_tor_fetch() { + let host_container = HostContainer::new(); + let addr = "eweiibe6tdjsdprb4px6rqrzzcsi22m4koia44kc5pcjr7nec2rlxyad.onion:23330"; + host_container.store_or_update( + HostColor::Grey, + Url::parse(&format!("tor://{}", addr)).unwrap(), + 0, + ); + let socks5_proxy_url = Url::parse("socks5://127.0.0.1:9050").unwrap(); + + let fetched_hosts = host_container.fetch( + HostColor::Grey, + &["socks5".to_string(), "socks5+tls".to_string(), "tor".to_string()], + &["tor".to_string()], + socks5_proxy_url.clone(), + ); + + // test tor endpoint is changed to socks5 and tor will not be used to + // connect to any host directly + assert_eq!(fetched_hosts.len(), 1); + let mixed_url = fetched_hosts[0].0.clone(); + assert_eq!(mixed_url.scheme(), socks5_proxy_url.scheme()); + assert_eq!(mixed_url.host(), socks5_proxy_url.host()); + assert_eq!(mixed_url.port(), socks5_proxy_url.port()); + assert_eq!(mixed_url.path_segments().unwrap().next(), Some(addr)); + } + + #[test] + fn test_transport_tor_and_socks5_mixed_with_tcp_fetch() { + let host_container = HostContainer::new(); + host_container.store_or_update( + HostColor::Grey, + Url::parse("tcp://dark.fi:28880").unwrap(), + 0, + ); + + let fetched_hosts = host_container.fetch( + HostColor::Grey, + &[ + "tor".to_string(), + "tor+tls".to_string(), + "socks5".to_string(), + "socks5+tls".to_string(), + ], + &["tcp".to_string()], + Url::parse("socks5://127.0.0.1:9050").unwrap(), + ); + + // test the tcp endpoint is changed to two endpoints socks5 and tor. + assert_eq!(fetched_hosts.len(), 2); + let endpoints: Vec<_> = fetched_hosts.iter().map(|item| item.0.scheme()).collect(); + assert!(endpoints.iter().all(|&scheme| scheme == "tor" || scheme == "socks5")); + } } diff --git a/src/net/session/outbound_session.rs b/src/net/session/outbound_session.rs index 8921c9c08..9082170a6 100644 --- a/src/net/session/outbound_session.rs +++ b/src/net/session/outbound_session.rs @@ -216,7 +216,7 @@ impl Slot { let gold_count = settings.gold_connect_count; let transports = settings.allowed_transports.clone(); - let transport_mixing = settings.transport_mixing; + let mixed_transports = settings.mixed_transports.clone(); let preference_strict = settings.slot_preference_strict; let tor_socks5_proxy = settings.tor_socks5_proxy.clone(); @@ -230,13 +230,13 @@ impl Slot { // If we only have grey entries, select from the greylist. Otherwise, // use the preference defined in settings. let addrs = if grey_only && !preference_strict { - container.fetch(HostColor::Grey, &transports, transport_mixing, tor_socks5_proxy) + container.fetch(HostColor::Grey, &transports, &mixed_transports, tor_socks5_proxy) } else if slot < gold_count { - container.fetch(HostColor::Gold, &transports, transport_mixing, tor_socks5_proxy) + container.fetch(HostColor::Gold, &transports, &mixed_transports, tor_socks5_proxy) } else if slot < white_count { - container.fetch(HostColor::White, &transports, transport_mixing, tor_socks5_proxy) + container.fetch(HostColor::White, &transports, &mixed_transports, tor_socks5_proxy) } else { - container.fetch(HostColor::Grey, &transports, transport_mixing, tor_socks5_proxy) + container.fetch(HostColor::Grey, &transports, &mixed_transports, tor_socks5_proxy) }; hosts.check_addrs(addrs).await diff --git a/src/net/settings.rs b/src/net/settings.rs index 4e8eff3cd..10e48ab4c 100644 --- a/src/net/settings.rs +++ b/src/net/settings.rs @@ -52,7 +52,7 @@ pub struct Settings { pub external_addrs: Vec, /// Peer nodes to manually connect to pub peers: Vec, - /// Seed nodes to connect to for peer discovery and/or adversising our + /// Seed nodes to connect to for peer discovery and/or advertising our /// own external addresses pub seeds: Vec, /// Magic bytes should be unique per P2P network. @@ -62,8 +62,18 @@ pub struct Settings { pub app_version: semver::Version, /// Whitelisted network transports for outbound connections pub allowed_transports: Vec, - /// Allow transport mixing (e.g. Tor would be allowed to connect to `tcp://`) - pub transport_mixing: bool, + /// Transports allowed to be mixed (tcp, tcp+tls, tor, tor+tls) + /// When transport is added to this list the corresponding transport + /// in allowed_transports is used to connect to the node. + /// Supported mixing scenarios include + /// allowed_transport | mixed_transport + /// tor | tcp + /// tor+tls | tcp+tls + /// socks5 | tor + /// socks5 | tcp + /// socks5+tls | tor+tls + /// socks5+tls | tcp+tls + pub mixed_transports: Vec, /// Tor socks5 proxy to connect to when socks5 or socks5+tls are added to allowed transports /// and transport mixing is enabled pub tor_socks5_proxy: Url, @@ -128,7 +138,7 @@ impl Default for Settings { seeds: vec![], app_version, allowed_transports: vec!["tcp+tls".to_string()], - transport_mixing: false, + mixed_transports: vec![], tor_socks5_proxy: Url::parse("socks5://127.0.0.1:9050").unwrap(), i2p_socks5_proxy: Url::parse("socks5://127.0.0.1:4447").unwrap(), outbound_connections: 8, @@ -228,9 +238,20 @@ pub struct SettingsOpt { #[structopt(long = "transports")] pub allowed_transports: Option>, - /// Allow transport mixing (e.g. Tor would be allowed to connect to `tcp://`) - #[structopt(long)] - pub transport_mixing: Option, + /// Transports allowed to be mixed (tcp, tcp+tls, tor, tor+tls) + /// When transport is added to this list the corresponding transport + /// in allowed_transports is used to connect to the node. + /// Supported mixing scenarios include + /// allowed_transport | mixed_transport + /// tor | tcp + /// tor+tls | tcp+tls + /// socks5 | tor + /// socks5 | tcp + /// socks5+tls | tor+tls + /// socks5+tls | tcp+tls + #[serde(default)] + #[structopt(long = "mixed-transports")] + pub mixed_transports: Option>, /// Tor socks5 proxy to connect to when socks5 or socks5+tls are added to allowed transports /// and transport mixing is enabled @@ -316,7 +337,7 @@ impl From for Settings { seeds: opt.seeds, app_version: def.app_version, allowed_transports: opt.allowed_transports.unwrap_or(def.allowed_transports), - transport_mixing: opt.transport_mixing.unwrap_or(def.transport_mixing), + mixed_transports: opt.mixed_transports.unwrap_or(def.mixed_transports), tor_socks5_proxy: opt.tor_socks5_proxy.unwrap_or(def.tor_socks5_proxy), i2p_socks5_proxy: opt.i2p_socks5_proxy.unwrap_or(def.i2p_socks5_proxy), outbound_connections: opt.outbound_connections.unwrap_or(def.outbound_connections),