-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdns_resolver.py
174 lines (140 loc) · 6.18 KB
/
dns_resolver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import dns.resolver
from dns.exception import DNSException
from threading import Thread
from Queue import Queue
class DNSResolver(object):
"""
Attributes:
custom_configure: True if you want to specify your own nameservers. False by default.
custom_nameservers: list your nameservers here if you want to use your
own nameservers. Not does not take effect unless custom_configure is True
"""
def __init__(self, custom_configure=False, custom_nameservers=[]):
if custom_configure:
if not isinstance(custom_nameservers, list):
raise InputError(custom_nameservers, "custom_nameservers need to be a list")
if len(custom_nameservers) == 0:
raise InputError(custom_nameservers, "speciify nameservers to resolve from")
"""
The configure flag is True for default configuration
"""
self.resolver = dns.resolver.Resolver(configure=not custom_configure)
if custom_configure:
self.resolver.nameservers = custom_nameservers
"""
This is related to the NS and MX query records that we do.
We only go up max_depth of parents
"""
self.max_depth = 2
self.supported_records = ["NS", "A", "MX", "CNAME"]
def getRecords(self, hostname, record):
"""
getRecords should be able to get a record for a hostname
Currently, the record could be of type listed in records.
It is not clear that we need to restrict the queries for records
that we allow to be made so might make sense to get rid of it in
the future
Attributes:
hostname: hostname to query
record: the type of record that we want to fetch
"""
if record not in self.supported_records:
raise InputError(record_type, "Unsupported record type: {}".format(record_type))
records = self.resolver.query(hostname, record)
return [rec.to_text() for rec in records]
def getRecordsBrief(self, hostname, record_type):
if record_type not in self.supported_records:
raise InputError(record_type, "Unsupported record type: {}".format(record_type))
try:
records = []
fetch_err = None
records = self.getRecords(hostname, record_type)
except DNSException as e:
fetch_err = "{}".format(e)
finally:
result = {}
result["error"] = fetch_err
result["hostname"] = hostname
result["record_type"] = record_type
result["records"] = records
return result
def getARecords(self, hostname):
return self.getRecordsBrief(hostname, "A")
def getARecordsMT(self, hostname, mt_queue):
mt_queue.put({"A": self.getRecordsBrief(hostname, "A")})
def getCNAMERecords(self, hostname):
return self.getRecordsBrief(hostname, "CNAME")
def getCNAMERecordsMT(self, hostname, mt_queue):
mt_queue.put({"CNAME": self.getRecordsBrief(hostname, "CNAME")})
def getRecordsExhaustive(self, hostname, record_type):
if record_type not in self.supported_records:
raise InputError(record_type, "Unsupported record type: {}".format(record_type))
depth = 0
no_err = True
allResults = []
host = dns.name.from_text(hostname)
while no_err and (depth < self.max_depth):
try:
records = []
detailed_records = {}
fetch_err = None
cur_hostname = host.to_text()
records = self.getRecords(cur_hostname, record_type)
for record in records:
_record = record
if record_type == "MX":
_record = record.split()[1]
detailed_records[record] = self.getARecords(_record)
except DNSException as e:
fetch_err = "{}".format(e)
finally:
result = {}
result["error"] = fetch_err
result["hostname"] = cur_hostname
result["record_type"] = record_type
result["records"] = detailed_records
allResults.append(result)
try:
host = host.parent()
depth += 1
except dns.name.NoParent:
no_err = False
return allResults
def getMXRecords(self, hostname):
return self.getRecordsExhaustive(hostname, "MX")
def getMXRecordsMT(self, hostname, mt_queue):
mt_queue.put({"MX": self.getMXRecords(hostname)})
def getNSRecords(self, hostname):
return self.getRecordsExhaustive(hostname, "NS")
def getNSRecordsMT(self, hostname, mt_queue):
mt_queue.put({"NS": self.getNSRecords(hostname)})
def getAllRecords(self, hostname, records=["NS", "A", "MX", "CNAME"]):
allRecords = {}
if "A" in records:
allRecords["A"] = self.getARecords(hostname)
if "CNAME" in records:
allRecords["CNAME"] = self.getCNAMERecords(hostname)
if "MX" in records:
allRecords["MX"] = self.getMXRecords(hostname)
if "NS" in records:
allRecords["NS"] = self.getNSRecords(hostname)
return allRecords
def getAllRecordsMT(self, hostname, records=["NS", "A", "MX", "CNAME"], nameserver=None):
allRecords = {}
threads = []
mt_queue = Queue()
if nameserver is not None:
self.resolver.nameservers = nameserver
if "A" in records:
threads.append(Thread(target=self.getARecordsMT, args=(hostname, mt_queue)))
if "CNAME" in records:
threads.append(Thread(target=self.getCNAMERecordsMT, args=(hostname, mt_queue)))
if "MX" in records:
threads.append(Thread(target=self.getMXRecordsMT, args=(hostname, mt_queue)))
if "NS" in records:
threads.append(Thread(target=self.getNSRecordsMT, args=(hostname, mt_queue)))
map(lambda thread: thread.start(), threads)
map(lambda thread: thread.join(), threads)
while not mt_queue.empty():
allRecords.update(mt_queue.get())
return allRecords