1
1
use trust_dns_client:: client:: { Client , SyncClient } ;
2
- use trust_dns_client:: udp :: UdpClientConnection ;
2
+ use trust_dns_client:: tcp :: TcpClientConnection ;
3
3
use std:: str:: FromStr ;
4
4
use std:: net:: IpAddr ;
5
5
use trust_dns_client:: op:: DnsResponse ;
@@ -9,34 +9,109 @@ use rayon::prelude::*;
9
9
use std:: fs:: read_to_string;
10
10
use std:: env;
11
11
use std:: process;
12
+ use std:: net:: SocketAddr ;
13
+ use weighted_rs:: { RoundrobinWeight , Weight } ;
14
+ use std:: time:: Duration ;
15
+ use std:: thread;
12
16
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
+ } ;
14
38
let client = SyncClient :: new ( conn) ;
15
39
// 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
+ } ;
17
66
18
- let response: DnsResponse = client. query ( & name, DNSClass :: IN , RecordType :: PTR ) . unwrap ( ) ;
19
67
let answers: & [ Record ] = response. answers ( ) ;
20
68
21
69
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
+ } ;
24
76
}
25
77
26
78
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
+ } ;
28
85
} else {
29
- assert ! ( false , "unexpected result" )
86
+ assert ! ( false , "unexpected result" ) ;
87
+ process:: exit ( 1 ) ;
30
88
}
31
89
}
32
90
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
+
34
105
let mut ips = vec ! [ ] ;
35
106
match read_to_string ( filename) {
36
107
Ok ( file) => {
37
108
for line in file. lines ( ) {
38
109
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
+ } ) ,
40
115
Err ( err) => {
41
116
eprintln ! ( "Something went wrong while parsing the IP ({}): {}" , line, err) ;
42
117
process:: exit ( 1 ) ;
@@ -49,20 +124,21 @@ fn resolve_file(filename: &str, dns_server: &str) {
49
124
process:: exit ( 1 ) ;
50
125
}
51
126
}
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,
56
129
Err ( err) => {
57
- eprintln ! ( "Something went wrong with the UDP client connection : {}" , err) ;
130
+ eprintln ! ( "Something went wrong while building the thread pool : {}" , err) ;
58
131
process:: exit ( 1 ) ;
59
132
}
60
- } ;
133
+ }
61
134
62
135
ips. into_par_iter ( )
63
136
. 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
+ } ;
66
142
} ) ;
67
143
}
68
144
@@ -72,7 +148,7 @@ fn main() {
72
148
eprintln ! ( "Use: dns-ptr-resolver ./ips.txt" ) ;
73
149
process:: exit ( 1 ) ;
74
150
}
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" ] )
76
152
}
77
153
78
154
#[ cfg( test) ]
@@ -86,6 +162,6 @@ mod test {
86
162
87
163
#[ test]
88
164
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" ] ) ;
90
166
}
91
167
}
0 commit comments