Implement subver regex checking, allow multiple version messages
authorMatt Corallo <git@bluematt.me>
Sun, 19 May 2019 19:52:01 +0000 (15:52 -0400)
committerMatt Corallo <git@bluematt.me>
Sun, 19 May 2019 19:52:01 +0000 (15:52 -0400)
Cargo.toml
src/datastore.rs
src/main.rs
src/printer.rs

index d8f6fbc17a0e4cd94e40f347eea0ef58e24878a8..efdea2bc47f0d9e34e7475d0a1151081ab912bb4 100644 (file)
@@ -11,3 +11,4 @@ tokio = "0.1"
 bytes = "0.4"
 futures = "0.1"
 rand = "0.6"
+regex = "1"
index 886f32f5d9266469974e50480515ab8099c8b3e7..3e9a1b31714f8423a2a3cd81eb61cae5c20ce117 100644 (file)
@@ -1,6 +1,6 @@
 use std::{cmp, mem};
 use std::collections::{HashSet, HashMap, hash_map};
-use std::sync::RwLock;
+use std::sync::{Arc, RwLock};
 use std::net::SocketAddr;
 use std::time::{Duration, Instant};
 use std::io::{BufRead, BufReader};
@@ -14,6 +14,8 @@ use tokio::prelude::*;
 use tokio::fs::File;
 use tokio::io::write_all;
 
+use regex::Regex;
+
 #[derive(Clone, Copy, Hash, PartialEq, Eq)]
 pub enum AddressState {
        Untested,
@@ -39,7 +41,7 @@ pub enum U64Setting {
 }
 
 #[derive(Hash, PartialEq, Eq)]
-pub enum StringSetting {
+pub enum RegexSetting {
        SubverRegex,
 }
 
@@ -72,7 +74,7 @@ impl Nodes {
 
 pub struct Store {
        u64_settings: RwLock<HashMap<U64Setting, u64>>,
-       subver_regex: RwLock<String>,
+       subver_regex: RwLock<Arc<Regex>>,
        nodes: RwLock<Nodes>,
        store: String,
 }
@@ -111,8 +113,8 @@ impl Store {
                        u64s.insert(U64Setting::RescanInterval(AddressState::TimeoutDuringRequest), try_read!(l, u64));
                        u64s.insert(U64Setting::RescanInterval(AddressState::Good), try_read!(l, u64));
                        u64s.insert(U64Setting::RescanInterval(AddressState::WasGood), try_read!(l, u64));
-                       future::ok((u64s, try_read!(l, String)))
-               }).or_else(|_| -> future::FutureResult<(HashMap<U64Setting, u64>, String), ()> {
+                       future::ok((u64s, try_read!(l, Regex)))
+               }).or_else(|_| -> future::FutureResult<(HashMap<U64Setting, u64>, Regex), ()> {
                        let mut u64s = HashMap::with_capacity(15);
                        u64s.insert(U64Setting::ConnsPerSec, 50);
                        u64s.insert(U64Setting::RunTimeout, 120);
@@ -129,7 +131,7 @@ impl Store {
                        u64s.insert(U64Setting::RescanInterval(AddressState::Good), 1800);
                        u64s.insert(U64Setting::RescanInterval(AddressState::WasGood), 1800);
                        u64s.insert(U64Setting::MinProtocolVersion, 10000); //XXX
-                       future::ok((u64s, ".*".to_string()))
+                       future::ok((u64s, Regex::new(".*").unwrap()))
                });
 
                macro_rules! nodes_uninitd {
@@ -216,7 +218,7 @@ impl Store {
                settings_future.join(nodes_future).and_then(move |((u64_settings, regex), nodes)| {
                        future::ok(Store {
                                u64_settings: RwLock::new(u64_settings),
-                               subver_regex: RwLock::new(regex),
+                               subver_regex: RwLock::new(Arc::new(regex)),
                                nodes: RwLock::new(nodes),
                                store,
                        })
@@ -227,12 +229,20 @@ impl Store {
                *self.u64_settings.read().unwrap().get(&setting).unwrap()
        }
 
+       pub fn set_u64(&self, setting: U64Setting, value: u64) {
+               *self.u64_settings.write().unwrap().get_mut(&setting).unwrap() = value;
+       }
+
        pub fn get_node_count(&self, state: AddressState) -> usize {
                self.nodes.read().unwrap().state_next_scan.get(&state).unwrap().len()
        }
 
-       pub fn get_string(&self, _setting: StringSetting) -> String {
-               self.subver_regex.read().unwrap().clone()
+       pub fn get_regex(&self, _setting: RegexSetting) -> Arc<Regex> {
+               Arc::clone(&*self.subver_regex.read().unwrap())
+       }
+
+       pub fn set_regex(&self, _setting: RegexSetting, value: Regex) {
+               *self.subver_regex.write().unwrap() = Arc::new(value);
        }
 
        pub fn add_fresh_nodes(&self, addresses: &Vec<(u32, Address)>) {
index 438d40fb32a462c1632bef3483565473dec8dc45..ac1b0d4cdb458943f84a1edb46e144ae6000123c 100644 (file)
@@ -19,7 +19,7 @@ use bitcoin::util::hash::BitcoinHash;
 
 use printer::{Printer, Stat};
 use peer::Peer;
-use datastore::{AddressState, Store, U64Setting};
+use datastore::{AddressState, Store, U64Setting, RegexSetting};
 
 use tokio::prelude::*;
 use tokio::timer::Delay;
@@ -102,6 +102,11 @@ fn scan_node(scan_time: Instant, node: SocketAddr) {
                                                state_lock.fail_reason = AddressState::NotFullNode;
                                                return future::err(());
                                        }
+                                       if !store.get_regex(RegexSetting::SubverRegex).is_match(&ver.user_agent) {
+                                               printer.add_line(format!("Updating {} to BadVersion subver {}", node, ver.user_agent.replace(|c: char| !c.is_ascii() || c < ' ' || c > '~', "")), true);
+                                               state_lock.fail_reason = AddressState::BadVersion;
+                                               return future::err(());
+                                       }
                                        check_set_flag!(recvd_version, "version");
                                        state_lock.node_services = ver.services;
                                        if let Err(_) = write.try_send(NetworkMessage::Verack) {
@@ -126,10 +131,8 @@ fn scan_node(scan_time: Instant, node: SocketAddr) {
                                        }
                                },
                                NetworkMessage::Addr(addrs) => {
-                                       if addrs.len() > 1 {
-                                               check_set_flag!(recvd_addrs, "addr");
-                                               unsafe { DATA_STORE.as_ref().unwrap() }.add_fresh_nodes(&addrs);
-                                       }
+                                       state_lock.recvd_addrs = true;
+                                       unsafe { DATA_STORE.as_ref().unwrap() }.add_fresh_nodes(&addrs);
                                },
                                NetworkMessage::Block(block) => {
                                        if block.header.bitcoin_hash() != state_lock.request.1 ||
index 10d2150fbf432dfec1582854a0fd7f3e102280ad..a23d023cb2d2457ce895643c6a0f84471e16a163 100644 (file)
@@ -2,7 +2,7 @@ use std::collections::LinkedList;
 use std::sync::{Arc, Mutex};
 use std::io::Write;
 
-use crate::datastore::{Store, AddressState, U64Setting, StringSetting};
+use crate::datastore::{Store, AddressState, U64Setting, RegexSetting};
 
 pub enum Stat {
        HeaderCount(u64),
@@ -82,7 +82,7 @@ impl Printer {
                                                "Minimum protocol version: {} (\"v x\" to change value to x)\n", store.get_u64(U64Setting::MinProtocolVersion)
                                                ).as_bytes()).expect("stdout broken?");
                                out.write_all(format!(
-                                               "Subversion match regex: {} (\"s x\" to change value to x)\n", store.get_string(StringSetting::SubverRegex)
+                                               "Subversion match regex: {} (\"s x\" to change value to x)\n", store.get_regex(RegexSetting::SubverRegex).as_str()
                                                ).as_bytes()).expect("stdout broken?");
 
                                out.write_all(b"\nRetry times (in seconds):\n").expect("stdout broken?");
@@ -129,7 +129,6 @@ impl Printer {
                                                "w x: Change the amount of time a node is considered WAS_GOOD after it fails to x from {} (in seconds)\n",
                                                store.get_u64(U64Setting::WasGoodTimeout)
                                                ).as_bytes()).expect("stdout broken?");
-                               out.write_all(b"p: Enable/disable updating these stats\n").expect("stdout broken?");
                                out.write_all(b"a x: Scan node x\n").expect("stdout broken?");
                                out.write_all(b"\x1b[s").expect("stdout broken?"); // Save cursor position and provide a blank line before cursor
                                out.write_all(b"\x1b[;H\x1b[2K").expect("stdout broken?");