Skip to content

Commit dac4c75

Browse files
committed
Implement round robin and more safeties and use TCP
1 parent fbf9994 commit dac4c75

File tree

3 files changed

+105
-21
lines changed

3 files changed

+105
-21
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ path = "src/main.rs"
2727
rayon = "1.7.0"
2828
trust-dns-client = { version = "0.22.0", default-features = false }
2929
rustdns = "0.4.0"
30+
weighted-rs = "0.1.3"

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22

33
A Rust program to resolve IP lists to their DNS PTR
44

5-
It uses the `1.1.1.1:53` DNS server. And 50 threads.
5+
It uses the following TCP DNS servers in a round-robin mode:
6+
7+
- "1.1.1.1:53"
8+
- "1.0.0.1:53"
9+
- "8.8.8.8:53"
10+
- "8.8.4.4:53"
11+
12+
And 30 threads.
613

714
## Example input
815

src/main.rs

+96-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use trust_dns_client::client::{ Client, SyncClient };
2-
use trust_dns_client::udp::UdpClientConnection;
2+
use trust_dns_client::tcp::TcpClientConnection;
33
use std::str::FromStr;
44
use std::net::IpAddr;
55
use trust_dns_client::op::DnsResponse;
@@ -9,34 +9,109 @@ use rayon::prelude::*;
99
use std::fs::read_to_string;
1010
use std::env;
1111
use std::process;
12+
use std::net::SocketAddr;
13+
use weighted_rs::{ RoundrobinWeight, Weight };
14+
use std::time::Duration;
15+
use std::thread;
1216

13-
fn get_ptr(conn: UdpClientConnection, addr: IpAddr) {
17+
struct PtrResult {
18+
query_addr: IpAddr,
19+
query: Name,
20+
result: Option<Name>,
21+
error: Option<String>,
22+
}
23+
24+
#[derive(Copy, Clone)]
25+
struct IpToResolve {
26+
address: IpAddr,
27+
server: SocketAddr,
28+
}
29+
30+
fn get_ptr(to_resolve: IpToResolve) -> PtrResult {
31+
let conn = match TcpClientConnection::with_timeout(to_resolve.server, Duration::new(5, 0)) {
32+
Ok(conn) => conn,
33+
Err(err) => {
34+
eprintln!("Something went wrong with the UDP client connection: {}", err);
35+
process::exit(1);
36+
}
37+
};
1438
let client = SyncClient::new(conn);
1539
// Specify the name, note the final '.' which specifies it's an FQDN
16-
let name = Name::from_str(&reverse(addr)).unwrap();
40+
let name = match Name::from_str(&reverse(to_resolve.address)) {
41+
Ok(name) => name,
42+
Err(err) => {
43+
eprintln!(
44+
"Something went wrong while building the name ({}): {}",
45+
reverse(to_resolve.address),
46+
err
47+
);
48+
process::exit(1);
49+
}
50+
};
51+
52+
let response: DnsResponse = match client.query(&name, DNSClass::IN, RecordType::PTR) {
53+
Ok(res) => res,
54+
Err(err) => {
55+
let two_hundred_millis = Duration::from_millis(400);
56+
thread::sleep(two_hundred_millis);
57+
eprintln!("Query error for ({}) from ({}): {}", name, to_resolve.server, err);
58+
return PtrResult {
59+
query_addr: to_resolve.address,
60+
query: name,
61+
result: None,
62+
error: Some(err.to_string()),
63+
};
64+
}
65+
};
1766

18-
let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::PTR).unwrap();
1967
let answers: &[Record] = response.answers();
2068

2169
if answers.len() == 0 {
22-
println!("{}", addr);
23-
return;
70+
return PtrResult {
71+
query_addr: to_resolve.address,
72+
query: name,
73+
result: None,
74+
error: None,
75+
};
2476
}
2577

2678
if let Some(RData::PTR(ref res)) = answers[0].data() {
27-
println!("{} # {}", addr, res);
79+
return PtrResult {
80+
query_addr: to_resolve.address,
81+
query: name,
82+
result: Some(res.clone()),
83+
error: None,
84+
};
2885
} else {
29-
assert!(false, "unexpected result")
86+
assert!(false, "unexpected result");
87+
process::exit(1);
3088
}
3189
}
3290

33-
fn resolve_file(filename: &str, dns_server: &str) {
91+
fn resolve_file(filename: &str, dns_servers: Vec<&str>) {
92+
let mut rr: RoundrobinWeight<SocketAddr> = RoundrobinWeight::new();
93+
for dns_server in dns_servers {
94+
let address = match dns_server.parse() {
95+
Ok(addr) => addr,
96+
Err(err) => {
97+
eprintln!("Something went wrong while parsing the DNS server address: {}", err);
98+
process::exit(1);
99+
}
100+
};
101+
102+
rr.add(address, 1);
103+
}
104+
34105
let mut ips = vec![];
35106
match read_to_string(filename) {
36107
Ok(file) => {
37108
for line in file.lines() {
38109
match IpAddr::from_str(line) {
39-
Ok(addr) => ips.push(addr),
110+
Ok(addr) =>
111+
ips.push(IpToResolve {
112+
address: addr,
113+
server: rr.next().unwrap(),
114+
}),
40115
Err(err) => {
41116
eprintln!("Something went wrong while parsing the IP ({}): {}", line, err);
42117
process::exit(1);
@@ -49,20 +124,21 @@ fn resolve_file(filename: &str, dns_server: &str) {
49124
process::exit(1);
50125
}
51126
}
52-
rayon::ThreadPoolBuilder::new().num_threads(50).build_global().unwrap();
53-
let address = dns_server.parse().unwrap();
54-
let conn = match UdpClientConnection::new(address) {
55-
Ok(conn) => conn,
127+
match rayon::ThreadPoolBuilder::new().num_threads(30).build_global() {
128+
Ok(r) => r,
56129
Err(err) => {
57-
eprintln!("Something went wrong with the UDP client connection: {}", err);
130+
eprintln!("Something went wrong while building the thread pool: {}", err);
58131
process::exit(1);
59132
}
60-
};
133+
}
61134

62135
ips.into_par_iter()
63136
.enumerate()
64-
.for_each(|(_i, addr)| {
65-
get_ptr(conn, addr);
137+
.for_each(|(_i, to_resolve)| {
138+
match get_ptr(to_resolve).result {
139+
Some(res) => println!("{} # {}", to_resolve.address, res),
140+
None => println!("{}", to_resolve.address),
141+
};
66142
});
67143
}
68144

@@ -72,7 +148,7 @@ fn main() {
72148
eprintln!("Use: dns-ptr-resolver ./ips.txt");
73149
process::exit(1);
74150
}
75-
resolve_file(&args[1], "1.1.1.1:53")
151+
resolve_file(&args[1], vec!["1.1.1.1:53", "1.0.0.1:53", "8.8.8.8:53", "8.8.4.4:53"])
76152
}
77153

78154
#[cfg(test)]
@@ -86,6 +162,6 @@ mod test {
86162

87163
#[test]
88164
fn test_resolve_file() {
89-
resolve_file("./example/ips-to-resolve.txt", "1.1.1.1:53");
165+
resolve_file("./example/ips-to-resolve.txt", vec!["1.1.1.1:53"]);
90166
}
91167
}

0 commit comments

Comments
 (0)