summaryrefslogtreecommitdiff
path: root/project2/proj2_s4498062/dns
diff options
context:
space:
mode:
Diffstat (limited to 'project2/proj2_s4498062/dns')
-rw-r--r--project2/proj2_s4498062/dns/cache.py51
-rw-r--r--project2/proj2_s4498062/dns/resolver.py61
-rw-r--r--project2/proj2_s4498062/dns/resource.py4
3 files changed, 86 insertions, 30 deletions
diff --git a/project2/proj2_s4498062/dns/cache.py b/project2/proj2_s4498062/dns/cache.py
index 3ef14b3..a3a9304 100644
--- a/project2/proj2_s4498062/dns/cache.py
+++ b/project2/proj2_s4498062/dns/cache.py
@@ -7,6 +7,7 @@ It is highly recommended to use these.
"""
import json
+import time
from dns.resource import ResourceRecord, RecordData
from dns.types import Type
@@ -22,11 +23,12 @@ class ResourceEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, ResourceRecord):
return {
- "name": obj.name,
- "type": Type.to_string(obj.type_),
- "class": Class.to_string(obj.class_),
- "ttl": obj.ttl,
- "rdata": obj.rdata.data
+ "name": obj.name,
+ "type": Type.to_string(obj.type_),
+ "class": Class.to_string(obj.class_),
+ "ttl": obj.ttl,
+ "rdata": obj.rdata.data,
+ "timestamp": obj.timestamp
}
return json.JSONEncoder.default(self, obj)
@@ -42,20 +44,33 @@ def resource_from_json(dct):
class_ = Class.from_string(dct["class"])
ttl = dct["ttl"]
rdata = RecordData.create(type_, dct["rdata"])
- return ResourceRecord(name, type_, class_, ttl, rdata)
+ timestamp = dct["timestamp"]
+ return ResourceRecord(name, type_, class_, ttl, rdata, timestamp)
class RecordCache(object):
""" Cache for ResourceRecords """
- def __init__(self, ttl):
+ FILE = '.dns.cache'
+
+ def __init__(self):
""" Initialize the RecordCache
Args:
ttl (int): TTL of cached entries (if > 0)
"""
self.records = []
- self.ttl = ttl
+ self.read_cache_file()
+
+ def __del__(self):
+ self.write_cache_file()
+
+ def remove_old(self):
+ """Remove entries for which the TTL has expired"""
+ now = int(time.clock())
+ for record in reversed(self.records):
+ if record.ttl + record.timestamp < now:
+ self.records.remove(record)
def lookup(self, dname, type_, class_):
""" Lookup resource records in cache
@@ -68,7 +83,12 @@ class RecordCache(object):
type_ (Type): type
class_ (Class): class
"""
- pass
+ self.remove_old()
+ for record in self.records:
+ if record.name == dname and \
+ record.type_ == type_ and \
+ record.class_ == class_:
+ yield record
def add_record(self, record):
""" Add a new Record to the cache
@@ -76,12 +96,19 @@ class RecordCache(object):
Args:
record (ResourceRecord): the record added to the cache
"""
- pass
+ self.records.append(record)
def read_cache_file(self):
""" Read the cache file from disk """
- pass
+ try:
+ with open(self.FILE, 'r') as jsonfile:
+ self.records = json.load(
+ jsonfile, object_hook=resource_from_json)
+ except IOError:
+ pass
def write_cache_file(self):
""" Write the cache file to disk """
- pass
+ self.remove_old()
+ with open(self.FILE, 'w') as jsonfile:
+ json.dump(self.records, jsonfile, cls=ResourceEncoder, indent=4)
diff --git a/project2/proj2_s4498062/dns/resolver.py b/project2/proj2_s4498062/dns/resolver.py
index b57e044..af60686 100644
--- a/project2/proj2_s4498062/dns/resolver.py
+++ b/project2/proj2_s4498062/dns/resolver.py
@@ -8,6 +8,7 @@ client and the DNS server, but with a different list of servers.
import re
import socket
+from dns.cache import RecordCache
from dns.classes import Class
from dns.message import Message, Header, Question
from dns.types import Type
@@ -45,6 +46,9 @@ class Resolver(object):
self.caching = caching
self.ttl = ttl
+ if self.caching:
+ self.cache = RecordCache()
+
def do_query(self, query, using):
"""Send a query to a list of name servers"""
for hint in using:
@@ -56,10 +60,27 @@ class Resolver(object):
sock.sendto(query.to_bytes(), (hint, 53))
data = sock.recv(512)
response = Message.from_bytes(data)
+
+ if self.caching:
+ for record in response.answers + \
+ response.authorities + \
+ response.additionals:
+ self.cache.add_record(record)
+
yield response
except socket.timeout:
pass
+ def try_hint(self, sub, dom, hints):
+ """Helper for get_hints"""
+ if sub == '':
+ for hint in hints:
+ yield hint
+ else:
+ for new_hint in self.get_hints(
+ sub, dom, hints, in_recursion=True):
+ yield new_hint
+
def get_hints(self, domain, parent='', using=None, in_recursion=False):
"""Get a list of nameservers for a domain"""
if using is None:
@@ -68,13 +89,21 @@ class Resolver(object):
if not in_recursion:
using += self.nameservers
+ print 'Trying', domain, parent, 'using', using
+
domains = re.match(rgx.DOMAIN, domain)
if domains is None:
- return None
+ return
sub, dom = domains.groups()
if parent != '':
dom += '.' + parent
+ if self.caching:
+ hints = self.cache.lookup(dom, Type.NS, Class.IN)
+ for hint in self.try_hint(
+ sub, dom, [r.rdata.data for r in hints]):
+ yield hint
+
header = Header(0, 0, 1, 0, 0, 0)
header.qr = 0
header.opcode = 0
@@ -82,26 +111,13 @@ class Resolver(object):
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 is not None:
- return result
-
- return []
+ hints = [ip for _, [ip] in response.get_hints()]
+ for hint in self.try_hint(sub, dom, hints):
+ yield hint
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
@@ -111,6 +127,15 @@ class Resolver(object):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(self.timeout)
+ if self.caching:
+ addrs = list(self.cache.lookup(hostname, Type.A, Class.IN))
+ cnames = list(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])
+
# Create and send query
question = Question(hostname, Type.A, Class.IN)
header = Header(9001, 0, 1, 0, 0, 0)
@@ -124,3 +149,5 @@ class Resolver(object):
addresses = response.get_addresses()
return hostname, aliases, addresses
+
+ return hostname, [], []
diff --git a/project2/proj2_s4498062/dns/resource.py b/project2/proj2_s4498062/dns/resource.py
index adce3e7..d54cdba 100644
--- a/project2/proj2_s4498062/dns/resource.py
+++ b/project2/proj2_s4498062/dns/resource.py
@@ -7,13 +7,14 @@ of your resolver and server.
import socket
import struct
+import time
from dns.types import Type
class ResourceRecord(object):
""" DNS resource record """
- def __init__(self, name, type_, class_, ttl, rdata):
+ def __init__(self, name, type_, class_, ttl, rdata, timestamp=time.time()):
""" Create a new resource record
Args:
@@ -27,6 +28,7 @@ class ResourceRecord(object):
self.class_ = class_
self.ttl = ttl
self.rdata = rdata
+ self.timestamp = timestamp
def to_bytes(self, offset, composer):
""" Convert ResourceRecord to bytes """