Skip to content

Commit 322c4ab

Browse files
authored
Merge pull request #24 from rustaceanrob/11-4-dns
Remove manual DNS implementation
2 parents a12f337 + 9a02a86 commit 322c4ab

File tree

2 files changed

+19
-219
lines changed

2 files changed

+19
-219
lines changed

src/dns.rs

Lines changed: 18 additions & 209 deletions
Original file line numberDiff line numberDiff line change
@@ -1,224 +1,33 @@
1-
use std::{
2-
fmt::Display,
3-
hash::{DefaultHasher, Hash, Hasher},
4-
io::Read,
5-
net::{IpAddr, Ipv4Addr, SocketAddr},
6-
time::SystemTime,
7-
};
8-
9-
use bitcoin::Network;
10-
11-
const BITCOIN_SEEDS: [&str; 9] = [
1+
/// Hostnames for the Bitcoin network.
2+
pub const BITCOIN_SEEDS: [&str; 9] = [
123
"seed.bitcoin.sipa.be",
134
"dnsseed.bluematt.me",
14-
"dnsseed.bitcoin.dashjr.org",
15-
"seed.bitcoinstats.com",
5+
"dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us",
166
"seed.bitcoin.jonasschnelli.ch",
17-
"seed.btc.petertodd.org",
7+
"seed.btc.petertodd.net",
188
"seed.bitcoin.sprovoost.nl",
199
"dnsseed.emzy.de",
2010
"seed.bitcoin.wiz.biz",
11+
"seed.mainnet.achownodes.xyz",
2112
];
2213

23-
const SIGNET_SEEDS: [&str; 2] = [
14+
/// Hostnames for the Signet test network.
15+
pub const SIGNET_SEEDS: [&str; 2] = [
2416
"seed.signet.bitcoin.sprovoost.nl",
2517
"seed.signet.achownodes.xyz",
2618
];
2719

28-
const LOCAL_HOST: &str = "0.0.0.0:0";
29-
const HEADER_BYTES: usize = 12;
30-
31-
const RECURSIVE_FLAGS: [u8; 2] = [
32-
0x01, 0x00, // Default flags with recursive resolver
33-
];
34-
35-
const QTYPE: [u8; 4] = [
36-
0x00, 0x01, // QType: A Record
37-
0x00, 0x01, // IN
20+
/// Hostnames for the Testnet 3 network.
21+
pub const TESTNET3_SEEDS: [&str; 5] = [
22+
"testnet-seed.bitcoin.jonasschnelli.ch",
23+
"seed.tbtc.petertodd.net",
24+
"seed.testnet.bitcoin.sprovoost.nl",
25+
"testnet-seed.bluematt.me",
26+
"seed.testnet.achownodes.xyz",
3827
];
3928

40-
const COUNTS: [u8; 6] = [
41-
0x00, 0x00, // ANCOUNT
42-
0x00, 0x00, // NSCOUNT
43-
0x00, 0x00, // ARCOUNT
29+
/// Hostnames for the Testnet 4 network.
30+
pub const TESTNET4_SEEDS: [&str; 2] = [
31+
"seed.testnet4.bitcoin.sprovoost.nl",
32+
"seed.testnet4.wiz.biz",
4433
];
45-
46-
const A_RECORD: u16 = 0x01;
47-
const A_CLASS: u16 = 0x01;
48-
const EXPECTED_RDATA_LEN: u16 = 0x04;
49-
50-
/// Query DNS seeds to find potential peers.
51-
pub trait DnsQueryExt {
52-
/// Return as many potential peers as possible, potentially zero.
53-
fn query_dns_seeds(&self, resolver: impl Into<SocketAddr>) -> Vec<IpAddr>;
54-
}
55-
56-
impl DnsQueryExt for Network {
57-
fn query_dns_seeds(&self, resolver: impl Into<SocketAddr>) -> Vec<IpAddr> {
58-
let resolver = resolver.into();
59-
match self {
60-
Network::Bitcoin => do_dns_query(&BITCOIN_SEEDS, resolver),
61-
Network::Signet => do_dns_query(&SIGNET_SEEDS, resolver),
62-
_ => Vec::new(),
63-
}
64-
}
65-
}
66-
67-
fn do_dns_query(seeds: &[&str], resolver: SocketAddr) -> Vec<IpAddr> {
68-
let mut vals = Vec::new();
69-
for seed in seeds {
70-
let query = DnsQuery::new(seed, resolver);
71-
if let Ok(hosts) = query.lookup() {
72-
vals.extend(&hosts);
73-
}
74-
}
75-
vals
76-
}
77-
78-
#[derive(Debug)]
79-
struct DnsQuery {
80-
message_id: [u8; 2],
81-
message: Vec<u8>,
82-
question: Vec<u8>,
83-
resolver: SocketAddr,
84-
}
85-
86-
impl DnsQuery {
87-
fn new(seed: &str, dns_resolver: SocketAddr) -> Self {
88-
// Build a header
89-
let message_id = rand_bytes();
90-
let mut message = message_id.to_vec();
91-
message.extend(RECURSIVE_FLAGS);
92-
message.push(0x00); // QDCOUNT
93-
message.push(0x01); // QDCOUNT
94-
message.extend(COUNTS);
95-
let mut question = encode_qname(seed, None);
96-
question.extend(QTYPE);
97-
message.extend_from_slice(&question);
98-
Self {
99-
message_id,
100-
message,
101-
question,
102-
resolver: dns_resolver,
103-
}
104-
}
105-
106-
fn lookup(self) -> Result<Vec<IpAddr>, Error> {
107-
let sock = std::net::UdpSocket::bind(LOCAL_HOST)?;
108-
sock.connect(self.resolver)?;
109-
sock.send(&self.message)?;
110-
let mut response_buf = [0u8; 512];
111-
let (amt, _src) = sock.recv_from(&mut response_buf)?;
112-
if amt < HEADER_BYTES {
113-
return Err(Error::MalformedHeader);
114-
}
115-
let ips = self.parse_message(&response_buf[..amt])?;
116-
Ok(ips)
117-
}
118-
119-
fn parse_message(&self, mut response: &[u8]) -> Result<Vec<IpAddr>, Error> {
120-
let mut ips = Vec::with_capacity(10);
121-
let mut buf: [u8; 2] = [0, 0];
122-
response.read_exact(&mut buf)?; // Read 2 bytes
123-
if self.message_id != buf {
124-
return Err(Error::MessageId);
125-
}
126-
// Read flags and ignore
127-
response.read_exact(&mut buf)?; // Read 4 bytes
128-
response.read_exact(&mut buf)?; // Read 6 bytes
129-
let _qdcount = u16::from_be_bytes(buf);
130-
response.read_exact(&mut buf)?; // Read 8 bytes
131-
let ancount = u16::from_be_bytes(buf);
132-
response.read_exact(&mut buf)?; // Read 10 bytes
133-
let _nscount = u16::from_be_bytes(buf);
134-
response.read_exact(&mut buf)?; // Read 12 bytes
135-
let _arcount = u16::from_be_bytes(buf);
136-
// The question should be repeated back to us
137-
let mut buf: Vec<u8> = vec![0; self.question.len()];
138-
response.read_exact(&mut buf)?;
139-
if self.question != buf {
140-
return Err(Error::Question);
141-
}
142-
for _ in 0..ancount {
143-
let mut buf: [u8; 2] = [0, 0];
144-
// Read the compressed NAME field of the record and ignore
145-
response.read_exact(&mut buf)?;
146-
// Read the TYPE
147-
response.read_exact(&mut buf)?;
148-
let atype = u16::from_be_bytes(buf);
149-
// Read the CLASS
150-
response.read_exact(&mut buf)?;
151-
let aclass = u16::from_be_bytes(buf);
152-
let mut buf: [u8; 4] = [0, 0, 0, 0];
153-
// Read the TTL
154-
response.read_exact(&mut buf)?;
155-
let _ttl = u32::from_be_bytes(buf);
156-
let mut buf: [u8; 2] = [0, 0];
157-
// Read the RDLENGTH
158-
response.read_exact(&mut buf)?;
159-
let rdlength = u16::from_be_bytes(buf);
160-
// Read RDATA
161-
let mut rdata: Vec<u8> = vec![0; rdlength as usize];
162-
response.read_exact(&mut rdata)?;
163-
if atype == A_RECORD && aclass == A_CLASS && rdlength == EXPECTED_RDATA_LEN {
164-
ips.push(IpAddr::V4(Ipv4Addr::new(
165-
rdata[0], rdata[1], rdata[2], rdata[3],
166-
)))
167-
}
168-
}
169-
Ok(ips)
170-
}
171-
}
172-
173-
#[derive(Debug)]
174-
enum Error {
175-
MessageId,
176-
MalformedHeader,
177-
Question,
178-
Io(std::io::Error),
179-
}
180-
181-
impl Display for Error {
182-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183-
match self {
184-
Self::Question => write!(f, "question section was not repeated back."),
185-
Self::MalformedHeader => write!(f, "the response header was undersized."),
186-
Self::MessageId => write!(f, "the response ID does not match the request."),
187-
Self::Io(io) => write!(f, "std::io error: {io}"),
188-
}
189-
}
190-
}
191-
192-
impl From<std::io::Error> for Error {
193-
fn from(value: std::io::Error) -> Self {
194-
Error::Io(value)
195-
}
196-
}
197-
198-
impl std::error::Error for Error {}
199-
200-
fn encode_qname<S: AsRef<str>>(hostname: S, filter: Option<S>) -> Vec<u8> {
201-
let mut qname = Vec::new();
202-
let str = hostname.as_ref();
203-
if let Some(filter) = filter {
204-
let prefix = filter.as_ref();
205-
qname.push(prefix.len() as u8);
206-
qname.extend(prefix.as_bytes());
207-
}
208-
for label in str.split(".") {
209-
qname.push(label.len() as u8);
210-
qname.extend(label.as_bytes());
211-
}
212-
qname.push(0x00);
213-
qname
214-
}
215-
216-
fn rand_bytes() -> [u8; 2] {
217-
let mut hasher = DefaultHasher::new();
218-
SystemTime::now().hash(&mut hasher);
219-
let mut hash = hasher.finish();
220-
hash ^= hash << 13;
221-
hash ^= hash >> 17;
222-
hash ^= hash << 5;
223-
hash.to_be_bytes()[..2].try_into().expect("trivial cast")
224-
}

tests/std.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
1+
use std::net::{Ipv4Addr, SocketAddrV4};
22

33
use bitcoin::Network;
4-
use bitcoin_p2p::dns::DnsQueryExt;
54
use corepc_node::{exe_path, P2P};
65

76
use bitcoin_p2p::handshake::ConnectionConfig;
@@ -76,11 +75,3 @@ fn maintain_connection() {
7675
reader.read_message().unwrap();
7776
bitcoind.stop().unwrap();
7877
}
79-
80-
#[test]
81-
fn dns_responds() {
82-
let network = Network::Signet;
83-
let cloudflare = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 53);
84-
let peers = network.query_dns_seeds(cloudflare);
85-
assert!(!peers.is_empty())
86-
}

0 commit comments

Comments
 (0)