]> git.bitcoin.ninja Git - dnsseed-rust/blob - src/reader.rs
Print ASN lookups in output zonefile comments
[dnsseed-rust] / src / reader.rs
1 use std::sync::Arc;
2 use std::sync::atomic::Ordering;
3 use std::io::BufReader;
4 use std::net::{IpAddr, Ipv6Addr, SocketAddr};
5 use std::time::Instant;
6
7 use tokio::prelude::*;
8 use tokio::io::{stdin, lines};
9
10 use crate::printer::Printer;
11 use crate::datastore::{Store, AddressState, U64Setting, RegexSetting};
12 use crate::bgp_client::BGPClient;
13
14 use crate::{START_SHUTDOWN, scan_node};
15
16 use regex::Regex;
17
18 // base32 decoder and tests stolen (transliterated) from Bitcoin Core
19 // Copyright (c) 2012-2019 The Bitcoin Core developers
20 // Distributed under the MIT software license, see
21 // http://www.opensource.org/licenses/mit-license.php.
22 fn decode_base32(inp: &[u8]) -> Option<Vec<u8>> {
23         let decode32_table: [i8; 256] = [
24                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
25                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
26                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
27                 -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
28                 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1,  0,  1,  2,
29                  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
30                 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
33                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
37         ];
38
39         let mut ret = Vec::with_capacity((inp.len() * 5) / 8);
40
41         let mut acc: u16 = 0;
42         let mut bits: u8 = 0;
43         for i in inp {
44                 if *i == '=' as u8 { break; }
45                 let codepoint = decode32_table[*i as usize];
46                 if codepoint < 0 { return None; }
47                 acc = ((acc << 5) | codepoint as u16) & ((1 << (8 + 5 - 1)) - 1);
48                 bits += 5;
49                 while bits >= 8 {
50                         bits -= 8;
51                         ret.push((acc >> bits) as u8);
52                 }
53         }
54         Some(ret)
55 }
56
57 #[test]
58 fn test_decode_base32() {
59         let tests_in = ["","f","fo","foo","foob","fooba","foobar"];
60         let tests_out = ["","my======","mzxq====","mzxw6===","mzxw6yq=","mzxw6ytb","mzxw6ytboi======"];
61         for (inp, out) in tests_in.iter().zip(tests_out.iter()) {
62                 assert_eq!(&decode_base32(out.as_bytes()).unwrap()[..], inp.as_bytes());
63         }
64         // My seednode's onion addr:
65         assert_eq!(decode_base32("nkf5e6b7pl4jfd4a".as_bytes()).unwrap()[..],[0x6a, 0x8b, 0xd2, 0x78, 0x3f, 0x7a, 0xf8, 0x92, 0x8f, 0x80]);
66 }
67
68 pub fn read(store: &'static Store, printer: &'static Printer, bgp_client: Arc<BGPClient>) {
69         tokio::spawn(lines(BufReader::new(stdin())).for_each(move |line| {
70                 macro_rules! err {
71                         () => { {
72                                 printer.add_line(format!("Unparsable input: \"{}\"", line), true);
73                                 return future::ok(());
74                         } }
75                 }
76                 let mut line_iter = line.split(' ');
77                 macro_rules! get_next_chunk {
78                         () => { {
79                                 match line_iter.next() {
80                                         Some(c) => c,
81                                         None => err!(),
82                                 }
83                         } }
84                 }
85                 macro_rules! try_parse_next_chunk {
86                         ($type: ty) => { {
87                                 match get_next_chunk!().parse::<$type>() {
88                                         Ok(res) => res,
89                                         Err(_) => err!(),
90                                 }
91                         } }
92                 }
93                 match get_next_chunk!() {
94                         "t" => store.set_u64(U64Setting::RunTimeout, try_parse_next_chunk!(u64)),
95                         "v" => store.set_u64(U64Setting::MinProtocolVersion, try_parse_next_chunk!(u64)),
96                         "w" => store.set_u64(U64Setting::WasGoodTimeout, try_parse_next_chunk!(u64)),
97                         "s" => {
98                                 if line.len() < 3 || !line.starts_with("s ") {
99                                         err!();
100                                 }
101                                 store.set_regex(RegexSetting::SubverRegex, match line[2..].parse::<Regex>() {
102                                         Ok(res) => res,
103                                         Err(_) => err!(),
104                                 });
105                         },
106                         "a" => {
107                                 let host_port = get_next_chunk!();
108                                 let parsed = if host_port.len() > 23 && &host_port[16..23] == ".onion:" {
109                                         let port = match host_port[23..].parse::<u16>() { Ok(res) => res, Err(_) => err!(), };
110
111                                         let ipv6 = match decode_base32(host_port[0..16].as_bytes()) { Some(res) => res, None => err!(), };
112                                         if ipv6.len() != 10 { err!(); }
113                                         let mut octets = [0xFD,0x87,0xD8,0x7E,0xEB,0x43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
114                                         octets[6..].copy_from_slice(&ipv6[0..10]);
115
116                                         SocketAddr::new(IpAddr::V6(Ipv6Addr::from(octets)), port)
117                                 } else {
118                                         match host_port.parse::<SocketAddr>() {
119                                                 Ok(res) => res, Err(_) => err!(), }
120                                 };
121                                 scan_node(Instant::now(), parsed, true)
122                         },
123                         "b" => {
124                                 let ip = try_parse_next_chunk!(IpAddr);
125                                 printer.add_line(format!("ASN for {} is {} (prefixlen, path: {:?})", ip, bgp_client.get_asn(ip), bgp_client.get_path(ip)), false);
126                         },
127                         "r" => {
128                                 match AddressState::from_num(try_parse_next_chunk!(u8)) {
129                                         Some(state) => store.set_u64(U64Setting::RescanInterval(state), try_parse_next_chunk!(u64)),
130                                         None => err!(),
131                                 }
132                         },
133                         "q" => {
134                                 START_SHUTDOWN.store(true, Ordering::SeqCst);
135                                 return future::err(std::io::Error::new(std::io::ErrorKind::Other, ""));
136                         },
137                         _ => err!(),
138                 }
139                 future::ok(())
140         }).then(move |_| {
141                 printer.add_line("Shutting down...".to_string(), true);
142                 future::ok(())
143         }));
144 }