From: Matt Corallo Date: Sun, 19 May 2019 19:52:01 +0000 (-0400) Subject: Implement subver regex checking, allow multiple version messages X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=405c10047bad68f7f2fabcf975ca0574444a7d47;p=dnsseed-rust Implement subver regex checking, allow multiple version messages --- diff --git a/Cargo.toml b/Cargo.toml index d8f6fbc..efdea2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ tokio = "0.1" bytes = "0.4" futures = "0.1" rand = "0.6" +regex = "1" diff --git a/src/datastore.rs b/src/datastore.rs index 886f32f..3e9a1b3 100644 --- a/src/datastore.rs +++ b/src/datastore.rs @@ -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>, - subver_regex: RwLock, + subver_regex: RwLock>, nodes: RwLock, 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, String), ()> { + future::ok((u64s, try_read!(l, Regex))) + }).or_else(|_| -> future::FutureResult<(HashMap, 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 { + 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)>) { diff --git a/src/main.rs b/src/main.rs index 438d40f..ac1b0d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 || diff --git a/src/printer.rs b/src/printer.rs index 10d2150..a23d023 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -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?");