diff options
author | Camil Staps | 2016-05-02 22:51:37 +0200 |
---|---|---|
committer | Camil Staps | 2016-05-02 22:51:37 +0200 |
commit | 28e067fc983aa8241c782b04406abd9c538a0986 (patch) | |
tree | d16857831f340829c7be65a73e89a508101061d0 /project2/proj2_s4498062/dns/resolver.py | |
parent | pylint project 2 (diff) |
Project 2: simple DNS resolver
Diffstat (limited to 'project2/proj2_s4498062/dns/resolver.py')
-rw-r--r-- | project2/proj2_s4498062/dns/resolver.py | 94 |
1 files changed, 75 insertions, 19 deletions
diff --git a/project2/proj2_s4498062/dns/resolver.py b/project2/proj2_s4498062/dns/resolver.py index 8d12c1d..a046d5d 100644 --- a/project2/proj2_s4498062/dns/resolver.py +++ b/project2/proj2_s4498062/dns/resolver.py @@ -5,15 +5,33 @@ 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. """ +import re import socket from dns.classes import Class from dns.message import Message, Header, Question from dns.types import Type +import dns.regexes as rgx 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, nameservers, timeout, caching, ttl): """ Initialize the resolver @@ -21,11 +39,61 @@ class Resolver(object): caching (bool): caching is enabled if True ttl (int): ttl of cache entries (if > 0) """ - self.nameservers = nameservers + self.nameservers = nameservers + self.ROOT_SERVERS self.timeout = timeout self.caching = caching self.ttl = ttl + def do_query(self, query, using): + """Send a query to a list of name servers""" + for hint in using: + if re.match(rgx.IP, hint) == None: + continue + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.settimeout(self.timeout) + try: + sock.sendto(query.to_bytes(), (hint, 53)) + data = sock.recv(512) + response = Message.from_bytes(data) + yield response + except socket.timeout: + pass + + def get_hints(self, domain, parent='', using=None, in_recursion=False): + """Get a list of nameservers for a domain""" + if using is None: + using = [] + + if not in_recursion: + using += self.nameservers + + domains = re.match(rgx.DOMAIN, domain) + if domains == None: + return None + sub, dom = domains.groups() + if parent != '': + dom += '.' + parent + + header = Header(0, 0, 1, 0, 0, 0) + header.qr = 0 + header.opcode = 0 + header.rd = 0 + query = Message(header, [Question(dom, Type.NS, Class.IN)]) + + for response in self.do_query(query, using): + new_hints = [ip for _, [ip] in list(response.get_hints())] + + if new_hints != []: + if sub is '': + return new_hints + using + + result = self.get_hints( + sub, dom, new_hints + using, in_recursion=True) + if result != None: + return result + + return [] + def gethostbyname(self, hostname): """ Translate a host name to IPv4 address. @@ -49,21 +117,9 @@ class Resolver(object): header.opcode = 0 header.rd = 1 query = Message(header, [question]) - #TODO should come from self.nameservers - sock.sendto(query.to_bytes(), ("8.8.8.8", 53)) - - # Receive response - data = sock.recv(512) - response = Message.from_bytes(data) - - # Get data - aliases = [] - for additional in response.additionals: - if additional.type_ == Type.CNAME: - aliases.append(additional.rdata.data) - addresses = [] - for answer in response.answers: - if answer.type_ == Type.A: - addresses.append(answer.rdata.data) - - return hostname, aliases, addresses + + for response in self.do_query(query, self.get_hints(hostname)): + aliases = response.get_aliases() + addresses = response.get_addresses() + + return hostname, aliases, addresses |