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
|
#!/usr/bin/env python2
""" DNS Resolver
This module contains a class for resolving hostnames. You will have to implement
things in this module. This resolver will be both used by the DNS client and the
DNS server, but with a different list of servers.
"""
from random import randint
import socket
from dns.classes import Class
from dns.types import Type
import dns.cache
from dns.message import Message, Question, Header
import dns.rcodes
class Resolver(object):
""" DNS resolver """
ROOT_SERVERS = [
'198.41.0.4',
'192.228.79.201',
'192.33.4.12',
'199.7.91.13',
'192.203.230.10',
'192.5.5.241',
'192.112.36.4',
'198.97.190.53',
'192.36.148.17',
'192.58.128.30',
'193.0.14.129',
'199.7.83.42',
'202.12.27.33'
]
def __init__(self, caching, ttl, timeout=3):
""" Initialize the resolver
Args:
caching (bool): caching is enabled if True
ttl (int): ttl of cache entries (if > 0)
"""
self.caching = caching
self.ttl = ttl
self.timeout = timeout
def do_query(self, hint, hostname, type_, class_=Class.IN):
"""Do a query to a hint"""
ident = randint(0, 65535)
header = Header(ident, 0, 1, 0, 0, 0)
header.qr = 0
header.opcode = 0
header.rd = 1
req = Message(header, [Question(hostname, type_, class_)])
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(self.timeout)
sock.sendto(req.to_bytes(), (hint, 53))
try:
data = sock.recv(512)
resp = Message.from_bytes(data)
if resp.header.ident == ident:
return resp
except socket.timeout:
pass
return None
def do_query_to_multiple(self, hints, hostname, type_, class_=Class.IN):
"""Do a query to multiple hints, return the remaining hints"""
while hints != []:
hint = hints.pop()
response = self.do_query(hint, hostname, type_, class_)
if response is not None:
return hints, response
return [], None
def gethostbyname(self, hostname):
""" Translate a host name to IPv4 address.
Currently this method contains an example. You will have to replace
this example with example with the algorithm described in section
5.3.3 in RFC 1034.
Args:
hostname (str): the hostname to resolve
Returns:
(str, [str], [str]): (hostname, aliaslist, ipaddrlist)
"""
domains = hostname.split('.')
hints = self.ROOT_SERVERS
if domains == []:
return hostname, [], []
domain = domains.pop(-1)
aliases = []
while hints != []:
hints, resp = self.do_query_to_multiple(hints, domain, Type.A)
if resp == None:
continue
info = resp.answers + resp.authorities + resp.additionals
aliases += [
r.rdata.data for r in info
if r.match(type_=Type.CNAME, class_=Class.IN, name=domain)]
# Case 1: answer
ips = [
r.rdata.data for r in info
if r.match(type_=Type.A, class_=Class.IN, name=domain)]
if ips != []:
return hostname, aliases, ips
# Case 2: name servers
auths = [
r.rdata.data for r in info
if r.match(type_=Type.NS, class_=Class.IN, name=domain)]
ips = [
add.rdata.data for ns in auths for add in info
if add.match(name=ns, type_=Type.A)]
if ips != []:
hints += ips
if domain != hostname:
domain = domains.pop(-1) + '.' + domain
continue
if auths != []:
auths = [h for a in auths for h in self.gethostbyname(a)[2]]
hints += auths
if domain != hostname:
domain = domains.pop(-1) + '.' + domain
continue
# Case 3: delegation to other name servers
parent = '.'.join(domain.split('.')[1:])
refs = [
r.rdata.data for r in info
if r.match(type_=Type.NS, class_=Class.IN, name=parent)]
ips = [
add.rdata.data for ns in refs for add in info
if add.match(name=ns, type_=Type.A)]
if ips != []:
hints += ips
continue
if refs != []:
refs = [h for r in refs for h in self.gethostbyname(r)[2]]
hints += refs
continue
# Case 4: aliases
for alias in aliases:
_, extra_aliases, alias_addresses = self.gethostbyname(alias)
if alias_addresses != []:
return hostname, aliases + extra_aliases, alias_addresses
return hostname, aliases, []
|