diff options
author | Camil Staps | 2016-05-30 11:31:31 +0200 |
---|---|---|
committer | Camil Staps | 2016-05-30 11:31:31 +0200 |
commit | 6e8b544d472c710d0aaac026a47f7477c13a0b93 (patch) | |
tree | 1f42f5e507e173903f8f39d6004afde4ce7c919e | |
parent | Start zone reading (diff) |
Simple DNS server
-rw-r--r-- | project2/proj2_s4498062/dns/message.py | 8 | ||||
-rw-r--r-- | project2/proj2_s4498062/dns/resolver.py | 18 | ||||
-rw-r--r-- | project2/proj2_s4498062/dns/server.py | 75 | ||||
-rwxr-xr-x | project2/proj2_s4498062/dns_server.py | 11 |
4 files changed, 91 insertions, 21 deletions
diff --git a/project2/proj2_s4498062/dns/message.py b/project2/proj2_s4498062/dns/message.py index baaba17..fd858b2 100644 --- a/project2/proj2_s4498062/dns/message.py +++ b/project2/proj2_s4498062/dns/message.py @@ -8,6 +8,8 @@ import struct from dns.domainname import Parser, Composer from dns.resource import ResourceRecord +from dns.types import Type +from dns.classes import Class class Message(object): @@ -278,3 +280,9 @@ class Question(object): qname = qnames[0] qtype, qclass = struct.unpack_from("!2H", packet, offset) return cls(qname, qtype, qclass), offset + 4 + + def __repr__(self): + return '{} {} {}'.format( + self.qname, + Type.to_string(self.qtype), + Class.to_string(self.qclass)) diff --git a/project2/proj2_s4498062/dns/resolver.py b/project2/proj2_s4498062/dns/resolver.py index fe46492..2c22adb 100644 --- a/project2/proj2_s4498062/dns/resolver.py +++ b/project2/proj2_s4498062/dns/resolver.py @@ -15,7 +15,6 @@ from dns.types import Type from dns.cache import RecordCache from dns.message import Message, Question, Header -import dns.rcodes class Resolver(object): @@ -93,10 +92,6 @@ class Resolver(object): 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 @@ -106,6 +101,19 @@ class Resolver(object): domains = hostname.split('.') hints = self.ROOT_SERVERS + if self.caching: + addrs = self.cache.lookup(hostname, Type.A, Class.IN) + cnames = self.cache.lookup(hostname, Type.CNAME, Class.IN) + if addrs != []: + return hostname, \ + [r.rdata.data for r in cnames], \ + [r.rdata.data for r in addrs] + for cname in cnames: + print 'trying', cname.rdata.data + cname, aliases, addrs = self.gethostbyname(cname.rdata.data) + if addrs != []: + return str(cname), aliases, addrs + if domains == []: return hostname, [], [] diff --git a/project2/proj2_s4498062/dns/server.py b/project2/proj2_s4498062/dns/server.py index f01043d..f830651 100644 --- a/project2/proj2_s4498062/dns/server.py +++ b/project2/proj2_s4498062/dns/server.py @@ -3,27 +3,70 @@ This module provides a recursive DNS server. You will have to implement this server using the algorithm described in section 4.3.2 of RFC 1034. """ - import re +import socket from threading import Thread import dns.regexes as rgx from dns.classes import Class from dns.types import Type +from dns.message import Header, Message +from dns.resolver import Resolver +from dns.resource import ResourceRecord, ARecordData, CNAMERecordData class RequestHandler(Thread): """ A handler for requests to the DNS server """ - def __init__(self): + def __init__(self, skt, ttl, data, addr, zone): + # pylint: disable=too-many-arguments """ Initialize the handler thread """ super(RequestHandler, self).__init__() self.daemon = True + self.skt = skt + self.ttl = ttl + self.data = data + self.addr = addr + self.zone = zone def run(self): """ Run the handler thread """ - # TODO: Handle DNS request - pass + resolver = Resolver(True, self.ttl) + + request = Message.from_bytes(self.data) + answs, adds, auths = [], [], [] + + for req in request.questions: + rrs = [ + r for r in self.zone + if r.match(type_=req.qtype, class_=req.qclass, name=req.qname)] + if rrs != []: + auths += rrs + elif req.qtype in [Type.A, Type.CNAME] and req.qclass == Class.IN: + name, cnames, addrs = resolver.gethostbyname(req.qname) + if name != req.qname: + answs.append(ResourceRecord( + str(req.qname), Type.CNAME, Class.IN, self.ttl, + CNAMERecordData(str(name)))) + # pylint: disable=bad-continuation + addrs = [ResourceRecord( + name, Type.A, Class.IN, self.ttl, + ARecordData(data)) + for data in addrs] + cnames = [ResourceRecord( + name, Type.CNAME, Class.IN, self.ttl, + CNAMERecordData(data)) + for data in cnames] + if req.qtype == Type.A: + answs += addrs + cnames + if req.qtype == Type.CNAME: + answs += cnames + + header = Header( + request.header.ident, 0, 0, len(answs), len(auths), len(adds)) + response = Message(header, None, answs, auths, adds) + + self.skt.sendto(response.to_bytes(), self.addr) class Server(object): @@ -41,23 +84,32 @@ class Server(object): self.ttl = ttl self.port = port self.done = False - # TODO: create socket + self.zone = [] + + self.skt = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def serve(self): """ Start serving request """ - # TODO: start listening + self.skt.bind(('localhost', self.port)) while not self.done: - # TODO: receive request and open handler - pass + data, addr = self.skt.recvfrom(512) + reqh = RequestHandler( + self.skt, self.ttl, data, addr, zone=self.zone) + reqh.start() def shutdown(self): """ Shutdown the server """ + self.skt.close() self.done = True - # TODO: shutdown socket def parse_zone_file(self, fname): + """Parse a zone file + + Will crash if the zone file has incorrect syntax. + """ with open(fname) as zonef: zone = zonef.read() + ttl, class_ = 3600000, Class.IN for match in re.finditer(rgx.ZONE_LINE_DOMAIN, zone, re.MULTILINE): match = match.groups() name = match[0] @@ -66,5 +118,6 @@ class Server(object): match[2] or match[3] or Class.to_string(class_)) type_ = Type.from_string(match[5]) data = match[6] - print match - print name, ttl, Class.to_string(class_), Type.to_string(type_), data + + record = ResourceRecord(name, type_, class_, ttl, data) + self.zone.append(record) diff --git a/project2/proj2_s4498062/dns_server.py b/project2/proj2_s4498062/dns_server.py index 6fe9a3a..41d837a 100755 --- a/project2/proj2_s4498062/dns_server.py +++ b/project2/proj2_s4498062/dns_server.py @@ -27,11 +27,12 @@ def main(): # Start server server = dns.server.Server(args.port, args.caching, args.ttl) server.parse_zone_file('named.root') - #try: - # server.serve() - #except KeyboardInterrupt: - # server.shutdown() - # print() + + try: + server.serve() + except KeyboardInterrupt: + server.shutdown() + print if __name__ == "__main__": main() |