summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCamil Staps2016-05-30 17:18:36 +0200
committerCamil Staps2016-05-30 17:18:36 +0200
commitbb00c6566c4ae8b632e10fc6660b92c78cc8d9f8 (patch)
tree61f723dbce218dba45eab84325557584f15b1443
parentMerge branch 'newframework' (diff)
Test cases for the server; fix infinite loop in resolver
-rw-r--r--project2/proj2_s4498062/dns/resolver.py17
-rwxr-xr-xproject2/proj2_s4498062/dns_tests.py74
2 files changed, 84 insertions, 7 deletions
diff --git a/project2/proj2_s4498062/dns/resolver.py b/project2/proj2_s4498062/dns/resolver.py
index 4c09681..8bd7bb9 100644
--- a/project2/proj2_s4498062/dns/resolver.py
+++ b/project2/proj2_s4498062/dns/resolver.py
@@ -82,12 +82,14 @@ class Resolver(object):
def do_query_to_multiple(
self, hints, hostname, type_, class_=Class.IN, caching=True):
"""Do a query to multiple hints, return the remaining hints"""
+ seen = []
while hints != []:
hint = hints.pop()
+ seen.append(hint)
response = self.do_query(hint, hostname, type_, class_, caching)
if response is not None:
- return hints, response
- return [], []
+ return seen, hints, response
+ return seen, [], []
def gethostbyname(self, hostname):
""" Translate a host name to IPv4 address.
@@ -108,15 +110,18 @@ class Resolver(object):
for cname in cnames:
cname, aliases, addrs = self.gethostbyname(cname.rdata.data)
if addrs != []:
- return str(cname), aliases, addrs
+ return str(cname), list(set(aliases)), list(set(addrs))
if hostname == '':
return hostname, [], []
+ seen = []
hints = self.ROOT_SERVERS[:]
aliases = []
while hints != []:
- hints, info = self.do_query_to_multiple(hints, hostname, Type.A)
+ used, hints, info = self.do_query_to_multiple(
+ hints, hostname, Type.A)
+ seen += used
aliases += [
r.rdata.data for r in info
@@ -137,11 +142,11 @@ class Resolver(object):
add.rdata.data for ns in auths for add in info
if add.match(name=ns, type_=Type.A)]
if ips != []:
- hints += ips
+ hints += [ip for ip in ips if ip not in seen]
continue
if auths != []:
auths = [h for a in auths for h in self.gethostbyname(a)[2]]
- hints += auths
+ hints += [auth for auth in auths if auth not in seen]
continue
# Case 3: aliases
diff --git a/project2/proj2_s4498062/dns_tests.py b/project2/proj2_s4498062/dns_tests.py
index 4a054b6..6a5a16e 100755
--- a/project2/proj2_s4498062/dns_tests.py
+++ b/project2/proj2_s4498062/dns_tests.py
@@ -1,19 +1,26 @@
#!/usr/bin/env python2
""" Tests for the DNS resolver and server """
+# pylint: disable=too-many-public-methods, invalid-name
+
+from random import randint
+import socket
import sys
import time
import unittest
from dns.cache import RecordCache
from dns.classes import Class
+from dns.message import Header, Message, Question
from dns.resolver import Resolver
from dns.resource import ResourceRecord, CNAMERecordData
from dns.types import Type
+
portnr = 5300
server = '127.0.0.1'
+
class TestResolver(unittest.TestCase):
"""Test cases for the resolver with caching disabled"""
@@ -40,6 +47,11 @@ class TestResolverCache(unittest.TestCase):
TTL = 3
+ def __init__(self, *pargs):
+ self.resolv = None
+ self.cache = None
+ super(TestResolverCache, self).__init__(*pargs)
+
def setup_resolver(self):
"""Setup a resolver with an invalid cache"""
self.resolv = Resolver(True, self.TTL)
@@ -68,8 +80,68 @@ class TestResolverCache(unittest.TestCase):
class TestServer(unittest.TestCase):
- pass
+ """Test cases for the server"""
+
+ TIMEOUT = 5
+
+ def setUp(self):
+ self.resolv = Resolver(False, 0)
+ def do_query(self, hostname, type_=Type.A, class_=Class.IN):
+ """Send a query to the server"""
+ 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(), ('127.0.0.1', portnr))
+
+ try:
+ data = sock.recv(512)
+ resp = Message.from_bytes(data)
+ if resp.header.ident == ident:
+ return resp.answers, resp.authorities, resp.additionals
+ except socket.timeout:
+ pass
+ return [], [], []
+
+ # pylint: disable=too-many-arguments
+ def assert_match(self, rec, name=None, type_=None, class_=None, data=None):
+ """Assert that a ResourceRecord matches some properties"""
+ self.assertTrue(
+ (name is None or rec.name == name) and
+ (type_ is None or rec.type_ == type_) and
+ (class_ is None or rec.class_ == class_) and
+ (data is None or rec.rdata.data == data))
+
+ def test_solve_auth(self):
+ """Test solving a name for which we are authoritative"""
+ answs, auths, adds = self.do_query('cloogle.org')
+ self.assertEqual(answs, [])
+ self.assert_match(
+ auths[0], 'cloogle.org', Type.A, Class.IN, '84.22.111.158')
+ self.assertEqual(adds, [])
+
+ def test_solve_zone(self):
+ """Test solving a name in our zone"""
+ answs, auths, adds = self.do_query('sub.cloogle.org')
+ self.assertEquals(answs + auths + adds, [])
+
+ def test_solve_outside(self):
+ """Test solving a name outside our zone"""
+ for name in ['camilstaps.nl', 'cname.camilstaps.nl']:
+ answs, auths, adds = self.do_query(name)
+ _, aliases, addrs = self.resolv.gethostbyname(name)
+
+ for record in answs + auths + adds:
+ if record.type_ == Type.A:
+ self.assertIn(record.rdata.data, addrs)
+ elif record.type_ == Type.CNAME:
+ self.assertIn(record.rdata.data, aliases)
if __name__ == "__main__":
# Parse command line arguments