summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCamil Staps2016-05-30 11:31:31 +0200
committerCamil Staps2016-05-30 11:31:31 +0200
commit6e8b544d472c710d0aaac026a47f7477c13a0b93 (patch)
tree1f42f5e507e173903f8f39d6004afde4ce7c919e
parentStart zone reading (diff)
Simple DNS server
-rw-r--r--project2/proj2_s4498062/dns/message.py8
-rw-r--r--project2/proj2_s4498062/dns/resolver.py18
-rw-r--r--project2/proj2_s4498062/dns/server.py75
-rwxr-xr-xproject2/proj2_s4498062/dns_server.py11
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()