Detect some simple classes of evil spy nodes
[dnsseed-rust] / src / main.rs
index a1d03d2b978533266f94c80f03d25e4196387c5b..4bf93510c5e3aaa41ba8fb38e8c4356965f3af73 100644 (file)
@@ -69,6 +69,7 @@ pub fn scan_node(scan_time: Instant, node: SocketAddr, manual: bool) {
                msg: (String::new(), false),
                request: Arc::clone(&unsafe { REQUEST_BLOCK.as_ref().unwrap() }.lock().unwrap()),
        }));
+       let err_peer_state = Arc::clone(&peer_state);
        let final_peer_state = Arc::clone(&peer_state);
 
        let peer = Delay::new(scan_time).then(move |_| {
@@ -77,7 +78,19 @@ pub fn scan_node(scan_time: Instant, node: SocketAddr, manual: bool) {
                Peer::new(node.clone(), Duration::from_secs(timeout), printer)
        });
        tokio::spawn(peer.and_then(move |(mut write, read)| {
-               TimeoutStream::new_timeout(read, scan_time + Duration::from_secs(store.get_u64(U64Setting::RunTimeout))).map_err(|_| { () }).for_each(move |msg| {
+               TimeoutStream::new_timeout(read, scan_time + Duration::from_secs(store.get_u64(U64Setting::RunTimeout))).map_err(move |err| {
+                       match err {
+                               bitcoin::consensus::encode::Error::UnrecognizedNetworkCommand(ref msg) => {
+                                       // If we got here, we hit one of the explicitly disallowed messages indicating
+                                       // a bogus "node".
+                                       let mut state_lock = err_peer_state.lock().unwrap();
+                                       state_lock.msg = (format!("(bad msg type {})", msg), true);
+                                       state_lock.fail_reason = AddressState::EvilNode;
+                               },
+                               _ => {},
+                       }
+                       ()
+               }).for_each(move |msg| {
                        let mut state_lock = peer_state.lock().unwrap();
                        macro_rules! check_set_flag {
                                ($recvd_flag: ident, $msg: expr) => { {
@@ -177,8 +190,17 @@ pub fn scan_node(scan_time: Instant, node: SocketAddr, manual: bool) {
                                        check_set_flag!(recvd_block, "block");
                                        return future::err(());
                                },
+                               NetworkMessage::Inv(invs) => {
+                                       for inv in invs {
+                                               if inv.inv_type == InvType::Transaction {
+                                                       state_lock.fail_reason = AddressState::EvilNode;
+                                                       state_lock.msg = ("due to unrequested inv tx".to_string(), true);
+                                                       return future::err(());
+                                               }
+                                       }
+                               },
                                NetworkMessage::Tx(_) => {
-                                       state_lock.fail_reason = AddressState::ProtocolViolation;
+                                       state_lock.fail_reason = AddressState::EvilNode;
                                        state_lock.msg = ("due to unrequested transaction".to_string(), true);
                                        return future::err(());
                                },
@@ -202,8 +224,10 @@ pub fn scan_node(scan_time: Instant, node: SocketAddr, manual: bool) {
                        }
                } else {
                        assert!(state_lock.fail_reason != AddressState::Good);
-                       if state_lock.fail_reason == AddressState::TimeoutDuringRequest && state_lock.recvd_version && state_lock.recvd_verack && state_lock.recvd_pong {
-                               if !state_lock.recvd_addrs {
+                       if state_lock.fail_reason == AddressState::TimeoutDuringRequest && state_lock.recvd_version && state_lock.recvd_verack {
+                               if !state_lock.recvd_pong {
+                                       state_lock.fail_reason = AddressState::TimeoutAwaitingPong;
+                               } else if !state_lock.recvd_addrs {
                                        state_lock.fail_reason = AddressState::TimeoutAwaitingAddr;
                                } else if !state_lock.recvd_block {
                                        state_lock.fail_reason = AddressState::TimeoutAwaitingBlock;
@@ -211,8 +235,8 @@ pub fn scan_node(scan_time: Instant, node: SocketAddr, manual: bool) {
                        }
                        let old_state = store.set_node_state(node, state_lock.fail_reason, 0);
                        if (manual || old_state != state_lock.fail_reason) && state_lock.fail_reason == AddressState::TimeoutDuringRequest {
-                               printer.add_line(format!("Updating {} from {} to Timeout During Request (ver: {}, vack: {}, pong: {})",
-                                       node, old_state.to_str(), state_lock.recvd_version, state_lock.recvd_verack, state_lock.recvd_pong), true);
+                               printer.add_line(format!("Updating {} from {} to Timeout During Request (ver: {}, vack: {})",
+                                       node, old_state.to_str(), state_lock.recvd_version, state_lock.recvd_verack), true);
                        } else if manual || (old_state != state_lock.fail_reason && state_lock.msg.0 != "" && state_lock.msg.1) {
                                printer.add_line(format!("Updating {} from {} to {} {}", node, old_state.to_str(), state_lock.fail_reason.to_str(), &state_lock.msg.0), state_lock.msg.1);
                        }