diff options
author | Camil Staps | 2016-05-30 17:43:40 +0200 |
---|---|---|
committer | Camil Staps | 2016-05-30 17:43:40 +0200 |
commit | bbd1357b8d3cfde6dd129397fbe1df5e674b0447 (patch) | |
tree | c92bcfedbaf74b7f2a81e6b9b406c2c951a7740d | |
parent | Test cases for the server; fix infinite loop in resolver (diff) |
Last fixes; concurrency test for the server
-rw-r--r-- | project2/proj2_s4498062/dns/cache.py | 3 | ||||
-rw-r--r-- | project2/proj2_s4498062/dns/server.py | 10 | ||||
-rwxr-xr-x | project2/proj2_s4498062/dns_tests.py | 48 |
3 files changed, 55 insertions, 6 deletions
diff --git a/project2/proj2_s4498062/dns/cache.py b/project2/proj2_s4498062/dns/cache.py index a8b62be..9e3a508 100644 --- a/project2/proj2_s4498062/dns/cache.py +++ b/project2/proj2_s4498062/dns/cache.py @@ -20,7 +20,7 @@ class ResourceEncoder(json.JSONEncoder): Usage: string = json.dumps(records, cls=ResourceEncoder, indent=4) """ - def default(self, obj): + def default(self, obj): # pylint: disable=method-hidden if isinstance(obj, ResourceRecord): return { "name": obj.name, @@ -90,6 +90,7 @@ class RecordCache(object): if r.match(name=dname, type_=type_, class_=class_)] def add_records_from(self, msg): + """Apply add_record to answers, authorities and additionals of msg""" for record in msg.answers + msg.authorities + msg.additionals: if record.type_ in [Type.A, Type.AAAA, Type.CNAME, Type.NS] and \ record.class_ == Class.IN: diff --git a/project2/proj2_s4498062/dns/server.py b/project2/proj2_s4498062/dns/server.py index 10cad8b..f1d33c1 100644 --- a/project2/proj2_s4498062/dns/server.py +++ b/project2/proj2_s4498062/dns/server.py @@ -19,7 +19,7 @@ from dns.resource import \ class RequestHandler(Thread): """ A handler for requests to the DNS server """ - def __init__(self, skt, ttl, data, addr, zone): + def __init__(self, skt, ttl, data, addr, zone, resolv): # pylint: disable=too-many-arguments """ Initialize the handler thread """ super(RequestHandler, self).__init__() @@ -29,11 +29,10 @@ class RequestHandler(Thread): self.data = data self.addr = addr self.zone = zone + self.resolv = resolv def run(self): """ Run the handler thread """ - resolver = Resolver(True, self.ttl) - request = Message.from_bytes(self.data) answs, adds, auths = [], [], [] @@ -44,7 +43,7 @@ class RequestHandler(Thread): if rrs != []: auths += rrs elif req.qtype in [Type.A, Type.CNAME] and req.qclass == Class.IN: - name, cnames, addrs = resolver.gethostbyname(req.qname) + name, cnames, addrs = self.resolv.gethostbyname(req.qname) if name != req.qname: answs.append(ResourceRecord( str(req.qname), Type.CNAME, Class.IN, self.ttl, @@ -86,6 +85,7 @@ class Server(object): self.port = port self.done = False self.zone = [] + self.resolv = Resolver(True, self.ttl) self.skt = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -95,7 +95,7 @@ class Server(object): while not self.done: data, addr = self.skt.recvfrom(512) reqh = RequestHandler( - self.skt, self.ttl, data, addr, zone=self.zone) + self.skt, self.ttl, data, addr, self.zone, self.resolv) reqh.start() def shutdown(self): diff --git a/project2/proj2_s4498062/dns_tests.py b/project2/proj2_s4498062/dns_tests.py index 6a5a16e..fe4ffc2 100755 --- a/project2/proj2_s4498062/dns_tests.py +++ b/project2/proj2_s4498062/dns_tests.py @@ -3,6 +3,7 @@ # pylint: disable=too-many-public-methods, invalid-name +import copy from random import randint import socket import sys @@ -143,6 +144,53 @@ class TestServer(unittest.TestCase): elif record.type_ == Type.CNAME: self.assertIn(record.rdata.data, aliases) + def test_concurrency(self): + """Test the server's concurrent abilities + + Note: we can never be sure that the server handles these requests in + parallel. What we do is (1) sending a request for a domain outside the + zone, and (2) sending a request for inside the zone. We would expect + the latter to be quicker. + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.settimeout(self.TIMEOUT) + + header = Header(0, 0, 1, 0, 0, 0) + header.qr = 0 + header.opcode = 0 + header.rd = 1 + + header.ident = 1 + req1 = Message(header, [Question('camilstaps.nl', Type.A, Class.IN)]) + + header = copy.copy(header) + header.ident = 2 + req2 = Message(header, [Question('cloogle.org', Type.A, Class.IN)]) + + sock.sendto(req1.to_bytes(), ('127.0.0.1', portnr)) + sock.sendto(req2.to_bytes(), ('127.0.0.1', portnr)) + + try: + # The second request + data = sock.recv(512) + resp = Message.from_bytes(data) + self.assertEqual(resp.header.ident, 2) + self.assert_match( + resp.authorities[0], + 'cloogle.org', Type.A, Class.IN, '84.22.111.158') + self.assertEqual(resp.answers + resp.additionals, []) + + # The first request + data = sock.recv(512) + resp = Message.from_bytes(data) + self.assertEqual(resp.header.ident, 1) + self.assert_match( + resp.answers[0], + 'camilstaps.nl', Type.A, Class.IN, '84.22.111.158') + self.assertEqual(resp.authorities + resp.additionals, []) + except socket.timeout: + self.fail('timeout') + if __name__ == "__main__": # Parse command line arguments import argparse |