Преглед изворни кода

net: add ipv6 addrs to dark list if we do not support ipv6

If ipv6_available is set to false, filter_addrs will send ipv6 addresses
to the darklist.

This is necessary since otherwise they would be deleted by the refinery
and not propagated on the network, even if they are perfectly valid
hosts.

Once on the dark list, these addresses will not be connected to in
OutboundSession.

Note that because there may be a delay when we establish ipv6 connectivity
(since it requires Connector to fail with ENETUNREACH), there is a
possibility of the following happening:

* recv ipv6 addrs, add to greylist
* try to connect to ipv6 addr
* set ipv6_available == false
* recv ivp6 addrs, add to darklist
* we now have duplicate addrs on darklist and greylist.

This is not a problem because the refinery will eventually delete the
greylist entries and from then on the dark list will be the definitive
list for ipv6 addrs.
draoi пре 2 година
родитељ
комит
8d9510478e
3 измењених фајлова са 58 додато и 9 уклоњено
  1. 2 2
      src/net/connector.rs
  2. 56 4
      src/net/hosts.rs
  3. 0 3
      src/net/p2p.rs

+ 2 - 2
src/net/connector.rs

@@ -102,9 +102,9 @@ impl Connector {
             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().ipv6_available.lock().await = false;
+                    *self.session.upgrade().unwrap().p2p().hosts().ipv6_available.lock().await =
+                        false;
                 }
-
                 Err(e.into())
             }
 

+ 56 - 4
src/net/hosts.rs

@@ -20,7 +20,7 @@ use std::{collections::HashMap, fmt, fs, fs::File, sync::Arc, time::Instant};
 
 use log::{debug, error, info, trace, warn};
 use rand::{prelude::IteratorRandom, rngs::OsRng, Rng};
-use smol::lock::RwLock;
+use smol::lock::{Mutex, RwLock};
 use url::Url;
 
 use super::{settings::SettingsPtr, ChannelPtr};
@@ -796,6 +796,9 @@ pub struct Hosts {
     /// Keeps track of the last time a connection was made.
     pub(in crate::net) last_connection: RwLock<Instant>,
 
+    /// Marker for IPv6 availability
+    pub(in crate::net) ipv6_available: Mutex<bool>,
+
     /// Pointer to configured P2P settings
     settings: SettingsPtr,
 }
@@ -809,6 +812,7 @@ impl Hosts {
             store_subscriber: Subscriber::new(),
             channel_subscriber: Subscriber::new(),
             last_connection: RwLock::new(Instant::now()),
+            ipv6_available: Mutex::new(true),
             settings,
         })
     }
@@ -1030,6 +1034,22 @@ impl Hosts {
         false
     }
 
+    /// Check whether a URL is IPV6
+    pub async fn is_ipv6(&self, url: Url) -> bool {
+        // Reject Urls without host strings.
+        if url.host_str().is_none() {
+            return false
+        }
+
+        // We do this hack in order to parse IPs properly.
+        // https://github.com/whatwg/url/issues/749
+        let addr = Url::parse(&url.as_str().replace(url.scheme(), "http")).unwrap();
+        if let url::Host::Ipv6(_) = addr.host().unwrap() {
+            return true
+        }
+        false
+    }
+
     /// Import blacklisted peers specified in the config file.
     pub(in crate::net) async fn import_blacklist(&self) -> Result<()> {
         for (mut host, ports) in self.settings.blacklist.clone() {
@@ -1060,7 +1080,7 @@ impl Hosts {
     }
 
     /// Filter given addresses based on certain rulesets and validity. Strictly called only on
-    /// the first time learning of a new peer.
+    /// the first time learning of new peers.
     async fn filter_addresses(
         &self,
         settings: SettingsPtr,
@@ -1069,6 +1089,7 @@ impl Hosts {
         debug!(target: "net::hosts::filter_addresses()", "Filtering addrs: {:?}", addrs);
         let mut ret = vec![];
         let localnet = self.settings.localnet;
+        let ipv6_available = *self.ipv6_available.lock().await;
 
         'addr_loop: for (addr_, last_seen) in addrs {
             // Validate that the format is `scheme://host_str:port`
@@ -1157,10 +1178,13 @@ impl Hosts {
                 _ => continue,
             }
 
-            // Store this peer on Dark list if we do not support this transport.
+            // Store this peer on Dark list if we do not support this transport
+            // or if this peer is IPV6 and we do not support IPV6.
             // We will personally ignore this peer but still send it to others in
             // Protocol Addr to ensure all transports get propagated.
-            if !settings.allowed_transports.contains(&addr_.scheme().to_string()) {
+            if !settings.allowed_transports.contains(&addr_.scheme().to_string()) ||
+                (!ipv6_available && self.is_ipv6(addr_.clone()).await)
+            {
                 self.container.store_or_update(HostColor::Dark, addr_.clone(), *last_seen).await;
 
                 continue
@@ -1317,6 +1341,34 @@ mod tests {
         });
     }
 
+    #[test]
+    fn test_is_ipv6() {
+        smol::block_on(async {
+            let settings = Settings { ..Default::default() };
+            let hosts = Hosts::new(Arc::new(settings.clone()));
+
+            let ipv6_hosts: Vec<Url> = vec![
+                Url::parse("tcp+tls://[::1]").unwrap(),
+                Url::parse("tcp://[2001:0000:130F:0000:0000:09C0:876A:130B]").unwrap(),
+                Url::parse("tcp://[2345:0425:2CA1:0000:0000:0567:5673:23b5]").unwrap(),
+            ];
+
+            let ipv4_hosts: Vec<Url> = vec![
+                Url::parse("tcp://192.168.10.65").unwrap(),
+                Url::parse("https://dyne.org").unwrap(),
+                Url::parse("tcp+tls://agorism.xyz").unwrap(),
+            ];
+
+            for host in ipv6_hosts {
+                assert!(hosts.is_ipv6(host).await)
+            }
+
+            for host in ipv4_hosts {
+                assert!(!hosts.is_ipv6(host).await)
+            }
+        });
+    }
+
     #[test]
     fn test_block_all_ports() {
         smol::block_on(async {

+ 0 - 3
src/net/p2p.rs

@@ -63,8 +63,6 @@ pub struct P2p {
     session_refine: RefineSessionPtr,
     /// Reference to configured [`SeedSyncSession`]
     session_seedsync: SeedSyncSessionPtr,
-    /// Marker for IPv6 availability
-    pub(in crate::net) ipv6_available: Mutex<bool>,
     /// Enable network debugging
     pub dnet_enabled: Mutex<bool>,
     /// The subscriber for which we can give dnet info over
@@ -94,7 +92,6 @@ impl P2p {
             session_refine: RefineSession::new(),
             session_seedsync: SeedSyncSession::new(),
 
-            ipv6_available: Mutex::new(true),
             dnet_enabled: Mutex::new(false),
             dnet_subscriber: Subscriber::new(),
         });