1
1
from __future__ import print_function
2
2
import sys
3
+ import os
4
+ import base64
3
5
import argparse
4
6
import logging
5
7
import random
9
11
import progressbar
10
12
import gevent
11
13
import gevent .pool
12
- from dns .resolver import Resolver , Answer
14
+ from dns .resolver import Resolver , Answer , NXDOMAIN , NoNameservers
13
15
from dns .exception import DNSException
14
16
15
17
@@ -46,6 +48,7 @@ def time_resolve(args, server, name, rectype, tries=3):
46
48
resolver .lifetime = args .timeout
47
49
resolver .nameservers = [server ]
48
50
results = []
51
+
49
52
while tries > 0 :
50
53
start = time .time ()
51
54
try :
@@ -62,28 +65,72 @@ def time_resolve(args, server, name, rectype, tries=3):
62
65
return server , check_results (results ), results
63
66
64
67
65
- def download_resolvers ():
66
- LOG .info ("Downloading nameservers from public-dns.info" )
67
- resp = requests .get ('http://public-dns.info/nameservers.txt' )
68
- resp .raise_for_status ()
69
- return map (str .strip , filter (None , str (resp .text ).split ("\n " )))
70
-
71
-
72
- def load_resolvers (handle ):
73
- return map (str .strip , filter (None , handle .read ().split ("\n " )))
68
+ def check_for_wildcards (args , server , name , rectype , tries = 4 ):
69
+ """
70
+ Verify that the DNS server doesn't return wildcard results for domains
71
+ which don't exist, it should correctly return NXDOMAIN.
72
+ """
73
+ resolver = Resolver ()
74
+ resolver .timeout = args .timeout
75
+ resolver .lifetime = args .timeout
76
+ resolver .nameservers = [server ]
77
+ nx_names = [base64 .b32encode (
78
+ os .urandom (
79
+ random .randint (8 , 10 ))
80
+ ).strip ('=' ).lower () + name
81
+ for _ in range (0 , tries )]
82
+ correct_result_count = 0
83
+ for check_nx_name in nx_names :
84
+ try :
85
+ result = resolver .query (check_nx_name , rectype )
86
+ return False # Any valid response = immediate fail!
87
+ except (NXDOMAIN , NoNameservers ):
88
+ correct_result_count += 1
89
+ except DNSException :
90
+ continue
91
+ return correct_result_count > (tries / 2.0 )
74
92
75
93
76
94
def check_resolver (args , resolver ):
95
+ """
96
+ Ensure that the resolver passes all integrity checks, and output to save
97
+ list if it does. The checks are:
98
+
99
+ * Consistently and quickly resolves valid domains
100
+ * Doesn't return results for invalid domains
101
+ """
102
+ bad_reason = ""
77
103
server , (avgtime , isgood ), _ = time_resolve (args , resolver , "example.com" , "A" )
104
+ if not isgood :
105
+ bad_reason = "TIMEOUT"
106
+ else :
107
+ for rectype in ["A" , "AAAA" , "MX" , "SOA" ]:
108
+ if not check_for_wildcards (args , resolver , "example.com" , rectype ):
109
+ bad_reason = "WILDCARD-" + rectype
110
+ isgood = False
111
+ break
112
+
78
113
if args .output and isgood :
79
114
args .output .write (server + "\n " )
80
115
args .output .flush ()
116
+
81
117
if not args .quiet :
82
118
color = bcolors .OKGREEN if isgood else bcolors .FAIL
83
- print ("%s%s (%.2fs)%s " % (color , server , avgtime , bcolors .ENDC ))
119
+ print ("%s%s (%.2fs) %s%s " % (color , server , avgtime , bad_reason , bcolors .ENDC ))
84
120
sys .stdout .flush ()
85
121
86
122
123
+ def download_resolvers ():
124
+ LOG .info ("Downloading nameservers from public-dns.info" )
125
+ resp = requests .get ('http://public-dns.info/nameservers.txt' )
126
+ resp .raise_for_status ()
127
+ return map (str .strip , filter (None , str (resp .text ).split ("\n " )))
128
+
129
+
130
+ def load_resolvers (handle ):
131
+ return map (str .strip , filter (None , handle .read ().split ("\n " )))
132
+
133
+
87
134
def run (args ):
88
135
if args .download :
89
136
resolvers = download_resolvers ()
@@ -112,8 +159,8 @@ def main():
112
159
help = 'Download new list of resolvers from public-dns.info' )
113
160
parser .add_argument ('-T' , '--timeout' , default = 0.5 , type = float , metavar = 'SECS' ,
114
161
help = "Timeout for DNS request in seconds, default: 0.5" )
115
- parser .add_argument ('-C' , '--concurrency' , default = 20 , type = int ,
116
- help = "Concurrent DNS requests, default: 20 " , metavar = 'N' )
162
+ parser .add_argument ('-C' , '--concurrency' , default = 10 , type = int ,
163
+ help = "Concurrent DNS requests, default: 10 " , metavar = 'N' )
117
164
parser .add_argument ('-q' , '--quiet' , action = 'store_true' ,
118
165
help = "Don't print results to console" )
119
166
parser .add_argument ('-v' , '--verbose' , action = 'store_const' ,
0 commit comments