Jelajahi Sumber

hosts: delete darklist entries older than one day

We introduce a new method called refresh() which deletes hosts older
than a specified period. This is applied to the darklist when we load it
on start(), and when we store hosts onto our darklist that are received by
other peers.

We do this to avoid the risk of passing faulty nodes around the network,
and assume that one day is sufficient for valid hosts to propagate to
hosts that support that transport.
draoi 2 tahun lalu
induk
melakukan
65e669f1a1
1 mengubah file dengan 67 tambahan dan 6 penghapusan
  1. 67 6
      src/net/hosts.rs

+ 67 - 6
src/net/hosts.rs

@@ -70,12 +70,12 @@ use crate::{
 ///
 ///     Black: hostile hosts that are strictly avoided for the duration of the program.
 ///
-///     Dark: hosts that do not match our transports, but that we continue
-///           to share with other peers. Once a day (every 86400 seconds) the darklist
-///           is cleared of all entries. This is to avoid peers propagating nodes that
-///           may be faulty. We assume that within the one day period, the nodes will
-///           be picked up by peers that accept the transports and can refine them to
-///           remove inactive peers. Dark list hosts are otherwise ignored.
+///     Dark: hosts that do not match our transports, but that we continue to share with
+///           other peers. We do not keep darklist entries that are older than one day.
+///           This is to avoid peers propagating nodes that may be faulty. We assume that
+///           within the one day period, the nodes will be picked up by peers that accept
+///           the transports and can refine them to remove inactive peers. Dark list hosts
+///           are otherwise ignored.
 ///
 /// `HostState`: a set of mutually exclusive states that can be Insert, Refine, Connect, Suspend
 ///  or Connected. The state is `None` when the corresponding host has been removed from the
@@ -713,6 +713,25 @@ impl HostContainer {
         }
     }
 
+    /// Delete items from a hostlist that are older than a specified maximum.
+    /// Maximum should be specified in seconds.
+    fn refresh(&self, color: HostColor, max_age: u64) {
+        let now = UNIX_EPOCH.elapsed().unwrap().as_secs();
+        let mut old_items = vec![];
+
+        let darklist = self.fetch_all(HostColor::Dark);
+        for (addr, last_seen) in darklist {
+            if (now - last_seen) > max_age {
+                old_items.push(addr);
+            }
+        }
+
+        for item in old_items {
+            debug!(target: "net::hosts::refresh()", "Removing {:?}", item);
+            self.remove_if_exists(color.clone(), &item);
+        }
+    }
+
     /// Load the hostlists from a file.
     pub(in crate::net) fn load_all(&self, path: &str) -> Result<()> {
         let path = expand_path(path)?;
@@ -769,6 +788,10 @@ impl HostContainer {
                     self.store(HostColor::Dark as usize, url, last_seen);
                     self.sort_by_last_seen(HostColor::Dark as usize);
                     self.resize(HostColor::Dark);
+
+                    // Delete darklist entries that are older than one day.
+                    let day = 86400;
+                    self.refresh(HostColor::Dark, day);
                 }
                 _ => {
                     debug!(target: "net::hosts::load_hosts()", "Malformed list name...");
@@ -1285,6 +1308,10 @@ impl Hosts {
                 self.container.sort_by_last_seen(HostColor::Dark as usize);
                 self.container.resize(HostColor::Dark);
 
+                // Delete darklist entries that are older than one day.
+                let day = 86400;
+                self.container.refresh(HostColor::Dark, day);
+
                 continue
             }
 
@@ -1549,6 +1576,40 @@ mod tests {
         assert!(hosts.container.contains(HostColor::Gold as usize, &gold_hosts[2]));
     }
 
+    #[test]
+    fn test_refresh() {
+        smol::block_on(async {
+            let settings = Settings { ..Default::default() };
+            let hosts = Hosts::new(Arc::new(AsyncRwLock::new(settings)));
+            let old_timestamp = 1720000000;
+
+            // Insert 5 items into the darklist with an old timestamp.
+            for i in 0..5 {
+                let last_seen = old_timestamp + i;
+                let url = Url::parse(&format!("tcp://old_darklist{}:123", i)).unwrap();
+                hosts.container.store(HostColor::Dark as usize, url.clone(), last_seen);
+            }
+
+            // Insert another 5 items into the darklist with a recent timestamp.
+            for i in 0..5 {
+                let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
+                let url = Url::parse(&format!("tcp://new_darklist{}:123", i)).unwrap();
+                hosts.container.store(HostColor::Dark as usize, url.clone(), last_seen);
+            }
+
+            // Delete all items that are older than a day.
+            let day = 86400;
+            hosts.container.refresh(HostColor::Dark, day);
+
+            let darklist = hosts.container.hostlists[HostColor::Dark as usize].read().unwrap();
+            assert!(darklist.len() == 5);
+
+            for (_, last_seen) in darklist.iter() {
+                assert!(*last_seen > old_timestamp);
+            }
+        });
+    }
+
     #[test]
     fn test_get_last() {
         smol::block_on(async {