Browse Source

net: make blacklist settings more configurable + other fixes

1. Settings blacklist is now a Vec<(Url, Vec<u16>)> and if ports are left empty,
   we block all the ports of the given host.

2. We read from the settings blacklist on RefineSession::start(), and
   also move other save() and load() host functions into refine session
   since it's more logical than doing in GreylistRefinery.

3. Last but not least, we fix a really BAD BUG which would send blacklist peers to the Gold list instead of Black list when blacklisting peers in move_host() !!!
draoi 2 years ago
parent
commit
6b29e8c659
5 changed files with 88 additions and 44 deletions
  1. 3 10
      src/net/acceptor.rs
  2. 3 10
      src/net/connector.rs
  3. 48 2
      src/net/hosts.rs
  4. 27 17
      src/net/session/refine_session.rs
  5. 7 5
      src/net/settings.rs

+ 3 - 10
src/net/acceptor.rs

@@ -99,6 +99,7 @@ impl Acceptor {
         // CondVar used to notify the loop to recheck if new connections can
         // be accepted by the listener.
         let cv = Arc::new(CondVar::new());
+        let hosts = self.session.upgrade().unwrap().p2p().hosts();
 
         loop {
             // Refuse new connections if we're up to the connection limit
@@ -118,16 +119,8 @@ impl Acceptor {
             match listener.next().await {
                 Ok((stream, url)) => {
                     // Check if we reject this peer
-                    if self
-                        .session
-                        .upgrade()
-                        .unwrap()
-                        .p2p()
-                        .hosts()
-                        .container
-                        .contains(HostColor::Black as usize, &url)
-                        .await ||
-                        self.session.upgrade().unwrap().p2p().settings().blacklist.contains(&url)
+                    if hosts.container.contains(HostColor::Black as usize, &url).await ||
+                        hosts.block_all_ports(url.host_str().unwrap().to_string()).await
                     {
                         warn!(target: "net::acceptor::run_accept_loop()", "Peer {} is blacklisted", url);
                         continue

+ 3 - 10
src/net/connector.rs

@@ -46,16 +46,9 @@ impl Connector {
 
     /// Establish an outbound connection
     pub async fn connect(&self, url: &Url) -> Result<(Url, ChannelPtr)> {
-        if self
-            .session
-            .upgrade()
-            .unwrap()
-            .p2p()
-            .hosts()
-            .container
-            .contains(HostColor::Black as usize, url)
-            .await ||
-            self.session.upgrade().unwrap().p2p().settings().blacklist.contains(url)
+        let hosts = self.session.upgrade().unwrap().p2p().hosts();
+        if hosts.container.contains(HostColor::Black as usize, url).await ||
+            hosts.block_all_ports(url.host_str().unwrap().to_string()).await
         {
             warn!(target: "net::connector::connect", "Peer {} is blacklisted", url);
             return Err(Error::ConnectFailed)

+ 48 - 2
src/net/hosts.rs

@@ -1035,6 +1035,35 @@ impl Hosts {
         false
     }
 
+    /// Import blacklisted peers specified in the config file.
+    pub async fn import_blacklist(&self) -> Result<()> {
+        for (mut host, ports) in self.settings.blacklist.clone() {
+            // If the ports are empty, simply store the host_str. We will use this to
+            // blacklist all ports of a given peer in `block_all_ports()`.
+            if ports.is_empty() {
+                self.container.store(HostColor::Black as usize, host.clone(), 0).await;
+            }
+            // Otherwise, store all the specified ports.
+            else {
+                for port in ports {
+                    host.set_port(Some(port))?;
+                    self.container.store(HostColor::Black as usize, host.clone(), 0).await;
+                }
+            }
+        }
+        Ok(())
+    }
+
+    /// If we have the Host of the Url in the hostlist, and there are no ports stored,
+    /// we should block all ports of this peer.
+    pub async fn block_all_ports(&self, addr: String) -> bool {
+        self.container.hostlists[HostColor::Black as usize]
+            .read()
+            .await
+            .iter()
+            .any(|(u, _t)| u.host_str().unwrap() == addr && u.port().is_none())
+    }
+
     /// Filter given addresses based on certain rulesets and validity. Strictly called only on
     /// the first time learning of a new peer.
     async fn filter_addresses(
@@ -1067,7 +1096,7 @@ impl Hosts {
 
             // Blacklist peers should never enter the hostlist.
             if self.container.contains(HostColor::Black as usize, addr_).await ||
-                settings.blacklist.contains(addr_)
+                self.block_all_ports(addr_.host_str().unwrap().to_string()).await
             {
                 warn!(target: "net::hosts::filter_addresses()",
                 "[{}] is blacklisted", addr_);
@@ -1227,7 +1256,7 @@ impl Hosts {
                     self.container.remove_if_exists(HostColor::Grey, addr).await;
                     self.container.remove_if_exists(HostColor::White, addr).await;
                     self.container.remove_if_exists(HostColor::Gold, addr).await;
-                    self.container.store_or_update(HostColor::Gold, addr.clone(), last_seen).await;
+                    self.container.store_or_update(HostColor::Black, addr.clone(), last_seen).await;
                 }
             }
 
@@ -1282,6 +1311,23 @@ mod tests {
         });
     }
 
+    #[test]
+    fn test_block_all_ports() {
+        smol::block_on(async {
+            let settings = Settings { ..Default::default() };
+
+            let hosts = Hosts::new(Arc::new(settings.clone()));
+            let blacklist1 = Url::parse("tcp+tls://nietzsche.king:333").unwrap();
+            let blacklist2 = Url::parse("tcp+tls://agorism.xyz").unwrap();
+
+            hosts.container.store(HostColor::Black as usize, blacklist1.clone(), 0).await;
+            hosts.container.store(HostColor::Black as usize, blacklist2.clone(), 0).await;
+
+            assert!(hosts.block_all_ports(blacklist2.host_str().unwrap().to_string()).await);
+            assert!(!hosts.block_all_ports(blacklist1.host_str().unwrap().to_string()).await);
+        });
+    }
+
     #[test]
     fn test_store() {
         let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();

+ 27 - 17
src/net/session/refine_session.rs

@@ -78,6 +78,24 @@ impl RefineSession {
 
     /// Start the refinery and self handshake processes.
     pub(crate) async fn start(self: Arc<Self>) {
+        match self.p2p().hosts().container.load_all(&self.p2p().settings().hostlist).await {
+            Ok(()) => {
+                debug!(target: "net::refine_session::start()", "Load hosts successful!");
+            }
+            Err(e) => {
+                warn!(target: "net::refine_session::start()", "Error loading hosts {}", e);
+            }
+        }
+        match self.p2p().hosts().import_blacklist().await {
+            Ok(()) => {
+                debug!(target: "net::refine_session::start()", "Import blacklist successful!");
+            }
+            Err(e) => {
+                warn!(target: "net::refine_session::start()",
+                    "Error importing blacklist from config file {}", e);
+            }
+        }
+
         debug!(target: "net::refine_session", "Starting greylist refinery process");
         self.refinery.clone().start().await;
 
@@ -87,6 +105,15 @@ impl RefineSession {
 
     /// Stop the refinery and self handshake processes.
     pub(crate) async fn stop(&self) {
+        match self.p2p().hosts().container.save_all(&self.p2p().settings().hostlist).await {
+            Ok(()) => {
+                debug!(target: "net::refine_session::stop()", "Save hosts successful!");
+            }
+            Err(e) => {
+                warn!(target: "net::refine_session::stop()", "Error saving hosts {}", e);
+            }
+        }
+
         debug!(target: "net::refine_session", "Stopping refinery process");
         self.refinery.clone().stop().await;
 
@@ -177,14 +204,6 @@ impl GreylistRefinery {
     }
 
     pub async fn start(self: Arc<Self>) {
-        match self.p2p().hosts().container.load_all(&self.p2p().settings().hostlist).await {
-            Ok(()) => {
-                debug!(target: "net::refinery::start()", "Load hosts successful!");
-            }
-            Err(e) => {
-                warn!(target: "net::refinery::start()", "Error loading hosts {}", e);
-            }
-        }
         let ex = self.p2p().executor();
         self.process.clone().start(
             async move {
@@ -201,15 +220,6 @@ impl GreylistRefinery {
     pub async fn stop(self: Arc<Self>) {
         debug!(target: "net::refinery", "Stopping refinery");
         self.process.stop().await;
-
-        match self.p2p().hosts().container.save_all(&self.p2p().settings().hostlist).await {
-            Ok(()) => {
-                debug!(target: "net::refinery::stop()", "Save hosts successful!");
-            }
-            Err(e) => {
-                warn!(target: "net::refinery::stop()", "Error saving hosts {}", e);
-            }
-        }
     }
 
     // Randomly select a peer on the greylist and probe it. This method will remove from the

+ 7 - 5
src/net/settings.rs

@@ -77,8 +77,9 @@ pub struct Settings {
     /// Number of seconds with no connections after which refinery
     /// process is paused.
     pub time_with_no_connections: u64,
-    /// Nodes to avoid interacting with for the duration of the program.
-    pub blacklist: Vec<Url>,
+    /// Nodes to avoid interacting with for the duration of the program,
+    /// in the format ["scheme://host", [port, port]]
+    pub blacklist: Vec<(Url, Vec<u16>)>,
 }
 
 impl Default for Settings {
@@ -217,10 +218,11 @@ pub struct SettingsOpt {
     #[structopt(skip)]
     pub time_with_no_connections: Option<u64>,
 
-    /// Nodes to avoid interacting with for the duration of the program.
+    /// Nodes to avoid interacting with for the duration of the program,
+    /// in the format ["scheme://host", [port, port]]
     #[serde(default)]
-    #[structopt(long)]
-    pub blacklist: Vec<Url>,
+    #[structopt(skip)]
+    pub blacklist: Vec<(Url, Vec<u16>)>,
 }
 
 impl From<SettingsOpt> for Settings {