summaryrefslogtreecommitdiff
path: root/project2
diff options
context:
space:
mode:
Diffstat (limited to 'project2')
-rw-r--r--project2/proj2_s4498062/.gitignore2
-rw-r--r--project2/proj2_s4498062/README.md45
-rw-r--r--project2/proj2_s4498062/dns/__init__.py0
-rw-r--r--project2/proj2_s4498062/dns/cache.py114
-rw-r--r--project2/proj2_s4498062/dns/classes.py43
-rw-r--r--project2/proj2_s4498062/dns/domainname.py112
-rw-r--r--project2/proj2_s4498062/dns/message.py325
-rw-r--r--project2/proj2_s4498062/dns/rcodes.py70
-rw-r--r--project2/proj2_s4498062/dns/regexes.py33
-rw-r--r--project2/proj2_s4498062/dns/resolver.py153
-rw-r--r--project2/proj2_s4498062/dns/resource.py241
-rw-r--r--project2/proj2_s4498062/dns/server.py51
-rw-r--r--project2/proj2_s4498062/dns/types.py55
-rw-r--r--project2/proj2_s4498062/dns/zone.py54
-rwxr-xr-xproject2/proj2_s4498062/dns_client.py34
-rwxr-xr-xproject2/proj2_s4498062/dns_server.py36
-rwxr-xr-xproject2/proj2_s4498062/dns_tests.py46
17 files changed, 0 insertions, 1414 deletions
diff --git a/project2/proj2_s4498062/.gitignore b/project2/proj2_s4498062/.gitignore
deleted file mode 100644
index 0191c0c..0000000
--- a/project2/proj2_s4498062/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.pyc
-.dns.cache
diff --git a/project2/proj2_s4498062/README.md b/project2/proj2_s4498062/README.md
deleted file mode 100644
index 7aee804..0000000
--- a/project2/proj2_s4498062/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Project 2 Framework
-
-## Description
-
-This directory contains a framework for a DNS resolver and a recursive DNS server.
-The framework provides classes for manipulating DNS messages (and converting them to bytes).
-The framework also contains a few stubs which you need to implement.
-Most files contain pointers to the relevant sections of RFC 1034 and RFC 1035.
-These are not the only relevant sections though, and you might need to read more of the RFCs.
-
-It is probably a good idea to read RFC 1034 before proceeding.
-This RFC explains an overview of DNS and introduces some of the naming which is also used in the framework.
-
-## File structure
-
-* proj1_sn1_sn2
- * dns
- * cache.py: Contains a cache for the resolver. You have to implement this.
- * classes.py: Enum of CLASSes and QCLASSes.
- * domainname.py: Classes for reading and writing domain names as bytes.
- * message.py: Classes for DNS messages.
- * rcodes.py: Enum of RCODEs.
- * resolver.py: Class for a DNS resolver. You have to implement this.
- * resource.py: Classes for DNS resource records.
- * server.py: Contains a DNS server. You have to implement this.
- * types.py: Enum of TYPEs and QTYPEs.
- * zone.py: name space zones. You have to implement this.
- * dns_client.py: A simple DNS client, which serves as an example user of the resolver.
- * dns_server.py: Code for starting the DNS server and parsing args.
- * dns_tests.py: Tests for your resolver, cache and server. You have to implement this.
-
-## Implementation Hints and Tips
-
-You should start with implementing the resolver, which you need for the server.
-You will need message.py, resource.py, types.py, classes.py and rcodes.py.
-You can ignore the code for converting from and to bytes from these files if
-you want, but it might be useful (especially for debugging).
-
-After finishing the resolver you need to implement caching and the DNS server.
-You can implement these in any order that you like.
-I suggest implementing the recursive part (the resolving) of your DNS server, before implementing the management of the servers zone.
-
-Wireshark and dns_client.py are useful tools for debugging your resolver.
-Wireshark and nslookup are useful tools for debugging your server.
-
diff --git a/project2/proj2_s4498062/dns/__init__.py b/project2/proj2_s4498062/dns/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/project2/proj2_s4498062/dns/__init__.py
+++ /dev/null
diff --git a/project2/proj2_s4498062/dns/cache.py b/project2/proj2_s4498062/dns/cache.py
deleted file mode 100644
index a3a9304..0000000
--- a/project2/proj2_s4498062/dns/cache.py
+++ /dev/null
@@ -1,114 +0,0 @@
-"""A cache for resource records
-
-This module contains a class which implements a cache for DNS resource records,
-you still have to do most of the implementation. The module also provides a
-class and a function for converting ResourceRecords from and to JSON strings.
-It is highly recommended to use these.
-"""
-
-import json
-import time
-
-from dns.resource import ResourceRecord, RecordData
-from dns.types import Type
-from dns.classes import Class
-
-
-class ResourceEncoder(json.JSONEncoder):
- """ Conver ResourceRecord to JSON
-
- Usage:
- string = json.dumps(records, cls=ResourceEncoder, indent=4)
- """
- 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,
- "timestamp": obj.timestamp
- }
- return json.JSONEncoder.default(self, obj)
-
-
-def resource_from_json(dct):
- """ Convert JSON object to ResourceRecord
-
- Usage:
- records = json.loads(string, object_hook=resource_from_json)
- """
- name = dct["name"]
- type_ = Type.from_string(dct["type"])
- class_ = Class.from_string(dct["class"])
- ttl = dct["ttl"]
- rdata = RecordData.create(type_, dct["rdata"])
- timestamp = dct["timestamp"]
- return ResourceRecord(name, type_, class_, ttl, rdata, timestamp)
-
-
-class RecordCache(object):
- """ Cache for ResourceRecords """
-
- FILE = '.dns.cache'
-
- def __init__(self):
- """ Initialize the RecordCache
-
- Args:
- ttl (int): TTL of cached entries (if > 0)
- """
- self.records = []
- 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
-
- Lookup for the resource records for a domain name with a specific type
- and class.
-
- Args:
- dname (str): domain name
- type_ (Type): type
- class_ (Class): class
- """
- 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
-
- Args:
- record (ResourceRecord): the record added to the cache
- """
- self.records.append(record)
-
- def read_cache_file(self):
- """ Read the cache file from disk """
- 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 """
- 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/classes.py b/project2/proj2_s4498062/dns/classes.py
deleted file mode 100644
index 9446f01..0000000
--- a/project2/proj2_s4498062/dns/classes.py
+++ /dev/null
@@ -1,43 +0,0 @@
-""" DNS CLASS and QCLASS values
-
-This module contains an Enum of CLASS and QCLASS values. The Enum also contains
-a method for converting values to strings. See sections 3.2.4 and 3.2.5 of RFC
-1035 for more information.
-"""
-
-
-class Class(object):
- """ Enum of CLASS and QCLASS values
-
- Usage:
- >>> Class.IN
- 1
- >>> Class.ANY
- 255
- """
-
- IN = 1
- CS = 2
- CH = 3
- HS = 4
- ANY = 255
-
- by_string = {
- "IN": IN,
- "CS": CS,
- "CH": CH,
- "HS": HS,
- "*": ANY
- }
-
- by_value = dict([(y, x) for x, y in by_string.items()])
-
- @staticmethod
- def to_string(class_):
- """Convert a class to a string"""
- return Class.by_value[class_]
-
- @staticmethod
- def from_string(string):
- """Convert a string to a class"""
- return Class.by_string[string]
diff --git a/project2/proj2_s4498062/dns/domainname.py b/project2/proj2_s4498062/dns/domainname.py
deleted file mode 100644
index 6dbb039..0000000
--- a/project2/proj2_s4498062/dns/domainname.py
+++ /dev/null
@@ -1,112 +0,0 @@
-""" Parsing and composing domain names
-
-This module contains two classes for converting domain names to and from bytes.
-You won't have to use these classes. They're used internally in Message,
-Question, ResourceRecord and RecordData. You can read section 4.1.4 of RFC 1035
-if you want more info.
-"""
-
-import struct
-
-
-class Composer(object):
- def __init__(self):
- self.offsets = dict()
-
- def to_bytes(self, offset, dnames):
- # Convert each domain name in to bytes
- result = b""
- for _, dname in enumerate(dnames):
- # Split domain name into labels
- labels = dname.split(".")
-
- # Determine keys of subdomains in offset dict
- keys = []
- for label in reversed(labels):
- name = label
- if keys:
- name += "." + keys[-1]
- keys.append(name)
- keys.reverse()
-
- # Convert label to bytes
- add_null = True
- for j, label in enumerate(labels):
- if keys[j] in self.offsets:
- offset = self.offsets[keys[j]]
- pointer = (3 << 14) + offset
- result += struct.pack("!H", pointer)
- add_null = False
- offset += 2
- break
- else:
- self.offsets[keys[j]] = offset
- result += struct.pack(
- "!B{}s".format(len(label)),
- len(label),
- label)
- offset += 1 + len(label)
-
- # Add null character at end
- if add_null:
- result += b"\x00"
- offset += 1
-
- return result
-
-
-class Parser(object):
- def __init__(self):
- self.labels = dict()
-
- def from_bytes(self, packet, offset, num):
- dnames = []
-
- # Read the domain names
- for _ in range(num):
- # Read a new domain name
- dname = ""
- prev_offsets = []
- done = False
- while done is False:
- # Read length of next label
- llength = struct.unpack_from("!B", packet, offset)[0]
-
- # Done reading domain when length is zero
- if llength == 0:
- offset += 1
- break
-
- # Compression label
- elif (llength >> 6) == 3:
- new_offset = offset + 2
- target = struct.unpack_from("!H", packet, offset)[0]
- target -= 3 << 14
- label = self.labels[target]
- done = True
-
- # Normal label
- else:
- new_offset = offset + llength + 1
- label = struct.unpack_from(
- "{}s".format(llength),
- packet, offset+1)[0]
-
- # Add label to dictionary
- self.labels[offset] = label
- for prev_offset in prev_offsets:
- self.labels[prev_offset] += "." + label
- prev_offsets.append(offset)
-
- # Update offset
- offset = new_offset
-
- # Append label to domain name
- if len(dname) > 0:
- dname += "."
- dname += label
-
- # Append domain name to list
- dnames.append(dname)
-
- return dnames, offset
diff --git a/project2/proj2_s4498062/dns/message.py b/project2/proj2_s4498062/dns/message.py
deleted file mode 100644
index a8ad621..0000000
--- a/project2/proj2_s4498062/dns/message.py
+++ /dev/null
@@ -1,325 +0,0 @@
-""" DNS messages
-
-This module contains classes for DNS messages, their header section and
-question fields. See section 4 of RFC 1035 for more info.
-"""
-
-import struct
-
-from dns.domainname import Parser, Composer
-from dns.resource import ResourceRecord
-from dns.types import Type
-
-
-class Message(object):
- """ DNS message """
-
- def __init__(
- self, header, questions=None, answers=None, authorities=None,
- additionals=None):
- """ Create a new DNS message
-
- Args:
- header (Header): the header section
- questions ([Question]): the question section
- answers ([ResourceRecord]): the answer section
- authorities ([ResourceRecord]): the authority section
- additionals ([ResourceRecord]): the additional section
- """
- self.header = header
- self.questions = questions if questions != None else []
- self.answers = answers if answers != None else []
- self.authorities = authorities if authorities != None else []
- self.additionals = additionals if additionals != None else []
-
- @property
- def resources(self):
- """ Getter for all resource records """
- return self.answers + self.authorities + self.additionals
-
- def to_bytes(self):
- """ Convert Message to bytes """
- composer = Composer()
-
- # Add header
- result = self.header.to_bytes()
-
- # Add questions
- for question in self.questions:
- offset = len(result)
- result += question.to_bytes(offset, composer)
-
- # Add answers
- for answer in self.answers:
- offset = len(result)
- result += answer.to_bytes(offset, composer)
-
- # Add authorities
- for authority in self.authorities:
- offset = len(result)
- result += authority.to_bytes(offset, composer)
-
- # Add additionals
- for additional in self.additionals:
- offset = len(result)
- result += additional.to_bytes(offset, composer)
-
- return result
-
- @classmethod
- def from_bytes(cls, packet):
- """ Create Message from bytes
-
- Args:
- packet (bytes): byte representation of the message
- """
- parser = Parser()
-
- # Parse header
- header, offset = Header.from_bytes(packet), 12
-
- # Parse questions
- questions = []
- for _ in range(header.qd_count):
- question, offset = Question.from_bytes(packet, offset, parser)
- questions.append(question)
-
- # Parse answers
- answers = []
- for _ in range(header.an_count):
- answer, offset = ResourceRecord.from_bytes(packet, offset, parser)
- answers.append(answer)
-
- # Parse authorities
- authorities = []
- for _ in range(header.ns_count):
- auth, offset = ResourceRecord.from_bytes(packet, offset, parser)
- authorities.append(auth)
-
- # Parse additionals
- additionals = []
- for _ in range(header.ar_count):
- add, offset = ResourceRecord.from_bytes(packet, offset, parser)
- additionals.append(add)
-
- return cls(header, questions, answers, authorities, additionals)
-
- def get_addresses(self):
- """Get the addresses from a response"""
- return self.get_data('answers', Type.A)
-
- def get_hints(self):
- """Get the nameservers from a response"""
- hints = self.get_data('authorities', Type.NS)
- results = []
- for hint in hints:
- addits = [
- a.rdata.data for a in self.additionals if
- a.name == hint and a.type_ == Type.A]
- if addits == []:
- continue
- results.append((hint, addits))
- return results
-
- def get_aliases(self):
- """Get the aliases from a response"""
- return self.get_data('additionals', Type.CNAME)
-
- def get_data(self, key, type_=None):
- """Yield from a message section, possibly filtering by type"""
- for k in getattr(self, key):
- if type_ == None or k.type_ == type_:
- yield k.rdata.data
-
-
-class Header(object):
- """ The header section of a DNS message
-
- Contains a number of properties which are accessible as normal member
- variables.
-
- See section 4.1.1 of RFC 1035 for their meaning.
- """
-
- def __init__(self, ident, flags, qd_count, an_count, ns_count, ar_count):
- """ Create a new Header object
-
- Args:
- ident (int): identifier
- qd_count (int): number of entries in question section
- an_count (int): number of entries in answer section
- ns_count (int): number of entries in authority section
- ar_count (int): number of entries in additional section
- """
- self.ident = ident
- self._flags = flags
- self.qd_count = qd_count
- self.an_count = an_count
- self.ns_count = ns_count
- self.ar_count = ar_count
-
- def to_bytes(self):
- """ Convert header to bytes """
- return struct.pack(
- "!6H",
- self.ident,
- self._flags,
- self.qd_count,
- self.an_count,
- self.ns_count,
- self.ar_count)
-
- @classmethod
- def from_bytes(cls, packet):
- """ Convert Header from bytes """
- if len(packet) < 12:
- raise ShortHeader
- return cls(*struct.unpack_from("!6H", packet))
-
- @property
- def flags(self):
- """The flags of the header"""
- return self._flags
-
- @flags.setter
- def flags(self, value):
- """Set the flags of the header"""
- if value >= (1 << 16):
- raise ValueError("value too big for flags")
- self._flags = value
-
- @property
- def qr(self):
- """The QR flag"""
- return self._flags & (1 << 15)
-
- @qr.setter
- def qr(self, value):
- """Set the QR flag"""
- if value:
- self._flags |= (1 << 15)
- else:
- self._flags &= ~(1 << 15)
-
- @property
- def opcode(self):
- """The opcode of the header"""
- return (self._flags & (((1 << 4) - 1) << 11)) >> 11
-
- @opcode.setter
- def opcode(self, value):
- """Set the opcode"""
- if value > 0b1111:
- raise ValueError("invalid opcode")
- self._flags &= ~(((1 << 4) - 1) << 11)
- self._flags |= value << 11
-
- @property
- def aa(self):
- """The AA flag"""
- return self._flags & (1 << 10)
-
- @aa.setter
- def aa(self, value):
- """Set the AA flag"""
- if value:
- self._flags |= (1 << 10)
- else:
- self._flags &= ~(1 << 10)
-
- @property
- def tc(self):
- """The TC flag"""
- return self._flags & (1 << 9)
-
- @tc.setter
- def tc(self, value):
- """Set the TC flag"""
- if value:
- self._flags |= (1 << 9)
- else:
- self._flags &= ~(1 << 9)
-
- @property
- def rd(self):
- """The RD flag"""
- return self._flags & (1 << 8)
-
- @rd.setter
- def rd(self, value):
- """Set the RD flag"""
- if value:
- self._flags |= (1 << 8)
- else:
- self._flags &= ~(1 << 8)
-
- @property
- def ra(self):
- """The RA flag"""
- return self._flags & (1 << 7)
-
- @ra.setter
- def ra(self, value):
- """Set the RA flag"""
- if value:
- self._flags |= (1 << 7)
- else:
- self._flags &= ~(1 << 7)
-
- @property
- def z(self):
- """The Z flag"""
- return self._flags & (((1 << 3) - 1) << 4) >> 4
-
- @z.setter
- def z(self, value):
- """Set the Z flag"""
- if value:
- raise ValueError("non-zero zero flag")
-
- @property
- def rcode(self):
- """The return code"""
- return self._flags & ((1 << 4) - 1)
-
- @rcode.setter
- def rcode(self, value):
- """Set the return code"""
- if value > 0b1111:
- raise ValueError("invalid return code")
- self._flags &= ~((1 << 4) - 1)
- self._flags |= value
-
-
-class Question(object):
- """ An entry in the question section.
-
- See section 4.1.2 of RFC 1035 for more info.
- """
-
- def __init__(self, qname, qtype, qclass):
- """ Create a new entry in the question section
-
- Args:
- qname (str): QNAME
- qtype (Type): QTYPE
- qclass (Class): QCLASS
- """
- self.qname = qname
- self.qtype = qtype
- self.qclass = qclass
-
- def to_bytes(self, offset, composer):
- """ Convert Question to bytes """
- bqname = composer.to_bytes(offset, [self.qname])
- bqtype = struct.pack("!H", self.qtype)
- bqclass = struct.pack("!H", self.qclass)
- return bqname + bqtype + bqclass
-
- @classmethod
- def from_bytes(cls, packet, offset, parser):
- """ Convert Question from bytes """
- qnames, offset = parser.from_bytes(packet, offset, 1)
- qname = qnames[0]
- qtype, qclass = struct.unpack_from("!2H", packet, offset)
- return cls(qname, qtype, qclass), offset + 4
diff --git a/project2/proj2_s4498062/dns/rcodes.py b/project2/proj2_s4498062/dns/rcodes.py
deleted file mode 100644
index 8f63b90..0000000
--- a/project2/proj2_s4498062/dns/rcodes.py
+++ /dev/null
@@ -1,70 +0,0 @@
-""" DNS RCODE values
-
-This module contains an Enum of RCODE values. See section 4.1.4 of RFC 1035 for
-more info.
-"""
-
-
-class RCode(object):
- """ Enum of RCODE values
-
- Usage:
- >>> NoError
- 0
- >>> NXDomain
- 3
- """
-
- NoError = 0
- FormErr = 1
- ServFail = 2
- NXDomain = 3
- NotImp = 4
- Refused = 5
- YXDomain = 6
- YXRRSet = 7
- NXRRSet = 8
- NotAuth = 9
- NotZone = 10
- BADVERS = 16
- BADSIG = 16
- BADKEY = 17
- BADTIME = 18
- BADMODE = 19
- BADNAME = 20
- BADALG = 21
- BADTRUNC = 22
-
- by_string = {
- "NoError": NoError,
- "FormErr": FormErr,
- "ServFail": ServFail,
- "NXDomain": NXDomain,
- "NotImp": NotImp,
- "Refused": Refused,
- "YXDomain": YXDomain,
- "YXRRSet": YXRRSet,
- "NXRRSet": NXRRSet,
- "NotAuth": NotAuth,
- "NotZone": NotZone,
- "BADVERS": BADVERS,
- "BADSIG": BADSIG,
- "BADKEY": BADKEY,
- "BADTIME": BADTIME,
- "BADMODE": BADMODE,
- "BADNAME": BADNAME,
- "BADALG": BADALG,
- "BADTRUNC": BADTRUNC
- }
-
- by_value = dict([(y, x) for x, y in by_string.items()])
-
- @staticmethod
- def to_string(rcode):
- """Convert a return code to a string"""
- return RCode.by_value[rcode]
-
- @staticmethod
- def from_string(string):
- """Convert a string to a return code"""
- return RCode.by_string[string]
diff --git a/project2/proj2_s4498062/dns/regexes.py b/project2/proj2_s4498062/dns/regexes.py
deleted file mode 100644
index 07c1eed..0000000
--- a/project2/proj2_s4498062/dns/regexes.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""Regexes used in the DNS protocol"""
-
-
-def grpm(regex):
- """Make a matching group"""
- return grp(regex, matching=True)
-
-
-def grp(regex, matching=False):
- """Make a group"""
- return r'(' + (r'' if matching else r'?:') + regex + r')'
-
-
-def opt(regex):
- """Make an optional group"""
- return grp(grp(regex) + r'?')
-
-
-def regex_opt_r(*regexes):
- """Make a group that matches one of the given regexes"""
- return grp(r'|'.join(regexes))
-
-
-DIGIT = r'\d'
-LETTER = r'[a-zA-Z]'
-LETDIG = grp(regex_opt_r(DIGIT, LETTER))
-LETDIGHYP = grp(regex_opt_r(LETDIG, r'-'))
-LDHSTR = grp(LETDIGHYP + r'+')
-LABEL = grp(LETTER + opt(opt(LDHSTR) + LETDIG))
-SUBDOMAIN = grp(grpm(grp(LABEL + r'\.') + r'*') + grpm(LABEL))
-DOMAIN = regex_opt_r(SUBDOMAIN, r' ')
-
-IP = r'(?:(?:\d{1,3}\.){3}\d{1,3})'
diff --git a/project2/proj2_s4498062/dns/resolver.py b/project2/proj2_s4498062/dns/resolver.py
deleted file mode 100644
index af60686..0000000
--- a/project2/proj2_s4498062/dns/resolver.py
+++ /dev/null
@@ -1,153 +0,0 @@
-""" DNS Resolver
-
-This module contains a class for resolving hostnames. You will have to
-implement things in this module. This resolver will be both used by the DNS
-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
-import dns.regexes as rgx
-
-
-class Resolver(object):
- """ DNS resolver """
-
- ROOT_SERVERS = [
- '198.41.0.4',
- '192.228.79.201',
- '192.33.4.12',
- '199.7.91.13',
- '192.203.230.10',
- '192.5.5.241',
- '192.112.36.4',
- '198.97.190.53',
- '192.36.148.17',
- '192.58.128.30',
- '193.0.14.129',
- '199.7.83.42',
- '202.12.27.33'
- ]
-
- def __init__(self, nameservers, timeout, caching, ttl):
- """ Initialize the resolver
-
- Args:
- caching (bool): caching is enabled if True
- ttl (int): ttl of cache entries (if > 0)
- """
- self.nameservers = nameservers + self.ROOT_SERVERS
- self.timeout = timeout
- 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:
- if re.match(rgx.IP, hint) is None:
- continue
- sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- sock.settimeout(self.timeout)
- try:
- 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:
- using = []
-
- if not in_recursion:
- using += self.nameservers
-
- print 'Trying', domain, parent, 'using', using
-
- domains = re.match(rgx.DOMAIN, domain)
- if domains is 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
- header.rd = 0
- query = Message(header, [Question(dom, Type.NS, Class.IN)])
-
- for response in self.do_query(query, using):
- 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.
-
- Args:
- hostname (str): the hostname to resolve
-
- Returns:
- (str, [str], [str]): (hostname, aliaslist, ipaddrlist)
- """
- 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)
- header.qr = 0
- header.opcode = 0
- header.rd = 1
- query = Message(header, [question])
-
- for response in self.do_query(query, self.get_hints(hostname)):
- aliases = response.get_aliases()
- 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
deleted file mode 100644
index d54cdba..0000000
--- a/project2/proj2_s4498062/dns/resource.py
+++ /dev/null
@@ -1,241 +0,0 @@
-""" A DNS resource record
-
-This class contains classes for DNS resource records and record data. This
-module is fully implemented. You will have this module in the implementation
-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, timestamp=time.time()):
- """ Create a new resource record
-
- Args:
- name (str): domain name
- type_ (Type): the type
- class_ (Class): the class
- rdata (RecordData): the record data
- """
- self.name = name
- self.type_ = type_
- self.class_ = class_
- self.ttl = ttl
- self.rdata = rdata
- self.timestamp = timestamp
-
- def to_bytes(self, offset, composer):
- """ Convert ResourceRecord to bytes """
- record = composer.to_bytes(offset, [self.name])
- record += struct.pack("!HHI", self.type_, self.class_, self.ttl)
- offset += len(record) + 2
- rdata = self.rdata.to_bytes(offset, composer)
- record += struct.pack("!H", len(rdata)) + rdata
- return record
-
- @classmethod
- def from_bytes(cls, packet, offset, parser):
- """ Convert ResourceRecord from bytes """
- names, offset = parser.from_bytes(packet, offset, 1)
- name = names[0]
- type_, class_, ttl, rdlen = struct.unpack_from("!HHIH", packet, offset)
- offset += 10
- rdata = RecordData.from_bytes(type_, packet, offset, rdlen, parser)
- offset += rdlen
- return cls(name, type_, class_, ttl, rdata), offset
-
-
-class RecordData(object):
- """ Record Data """
-
- def __init__(self, data):
- """ Initialize the record data
-
- Args:
- data (str): data
- """
- self.data = data
-
- @staticmethod
- def create(type_, data):
- """ Create a RecordData object from bytes
-
- Args:
- type_ (Type): type
- packet (bytes): packet
- offset (int): offset in message
- rdlength (int): length of rdata
- parser (int): domain name parser
- """
- classdict = {
- Type.A: ARecordData,
- Type.CNAME: CNAMERecordData,
- Type.NS: NSRecordData,
- Type.AAAA: AAAARecordData
- }
- if type_ in classdict:
- return classdict[type_](data)
- else:
- return GenericRecordData(data)
-
- @staticmethod
- def from_bytes(type_, packet, offset, rdlength, parser):
- """ Create a RecordData object from bytes
-
- Args:
- type_ (Type): type
- packet (bytes): packet
- offset (int): offset in message
- rdlength (int): length of rdata
- parser (int): domain name parser
- """
- classdict = {
- Type.A: ARecordData,
- Type.CNAME: CNAMERecordData,
- Type.NS: NSRecordData,
- Type.AAAA: AAAARecordData
- }
- if type_ in classdict:
- return classdict[type_].from_bytes(
- packet, offset, rdlength, parser)
- else:
- return GenericRecordData.from_bytes(
- packet, offset, rdlength, parser)
-
-
-class ARecordData(RecordData):
- """Data of an A record"""
-
- def to_bytes(self, offset, composer):
- """ Convert to bytes
-
- Args:
- offset (int): offset in message
- composer (Composer): domain name composer
- """
- return socket.inet_aton(self.data)
-
- @classmethod
- def from_bytes(cls, packet, offset, rdlength, parser):
- """ Create a RecordData object from bytes
-
- Args:
- packet (bytes): packet
- offset (int): offset in message
- rdlength (int): length of rdata
- parser (int): domain name parser
- """
- data = socket.inet_ntoa(packet[offset:offset+4])
- return cls(data)
-
-
-class CNAMERecordData(RecordData):
- """Data of a CNAME record"""
-
- def to_bytes(self, offset, composer):
- """ Convert to bytes
-
- Args:
- offset (int): offset in message
- composer (Composer): domain name composer
- """
- return composer.to_bytes(offset, [self.data])
-
- @classmethod
- def from_bytes(cls, packet, offset, rdlength, parser):
- """ Create a RecordData object from bytes
-
- Args:
- packet (bytes): packet
- offset (int): offset in message
- rdlength (int): length of rdata
- parser (int): domain name parser
- """
- names, offset = parser.from_bytes(packet, offset, 1)
- data = names[0]
- return cls(data)
-
-
-class NSRecordData(RecordData):
- """Data of an NS record"""
-
- def to_bytes(self, offset, composer):
- """ Convert to bytes
-
- Args:
- offset (int): offset in message
- composer (Composer): domain name composer
- """
- return composer.to_bytes(offset, [self.data])
-
- @classmethod
- def from_bytes(cls, packet, offset, rdlength, parser):
- """ Create a RecordData object from bytes
-
- Args:
- packet (bytes): packet
- offset (int): offset in message
- rdlength (int): length of rdata
- parser (int): domain name parser
- """
- names, offset = parser.from_bytes(packet, offset, 1)
- data = names[0]
- return cls(data)
-
-
-class AAAARecordData(RecordData):
- """Data of an AAAA record"""
-
- def to_bytes(self, offset, composer):
- """ Convert to bytes
-
- Args:
- offset (int): offset in message
- composer (Composer): domain name composer
- """
- return socket.inet_pton(socket.AF_INET6, self.data)
-
- @classmethod
- def from_bytes(cls, packet, offset, rdlength, parser):
- """ Create a RecordData object from bytes
-
- Args:
- packet (bytes): packet
- offset (int): offset in message
- rdlength (int): length of rdata
- parser (int): domain name parser
- """
- data = socket.inet_ntop(socket.AF_INET6, packet[offset:offset+16])
- return cls(data)
-
-
-class GenericRecordData(RecordData):
- """Data of a generic record"""
-
- def to_bytes(self, offset, composer):
- """ Convert to bytes
-
- Args:
- offset (int): offset in message
- composer (Composer): domain name composer
- """
- return self.data
-
- @classmethod
- def from_bytes(cls, packet, offset, rdlength, parser):
- """ Create a RecordData object from bytes
-
- Args:
- packet (bytes): packet
- offset (int): offset in message
- rdlength (int): length of rdata
- parser (int): domain name parser
- """
- data = packet[offset:offset+rdlength]
- return cls(data)
diff --git a/project2/proj2_s4498062/dns/server.py b/project2/proj2_s4498062/dns/server.py
deleted file mode 100644
index f412311..0000000
--- a/project2/proj2_s4498062/dns/server.py
+++ /dev/null
@@ -1,51 +0,0 @@
-""" A recursive DNS server
-
-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.
-"""
-
-from threading import Thread
-
-
-class RequestHandler(Thread):
- """ A handler for requests to the DNS server """
-
- def __init__(self):
- """ Initialize the handler thread """
- super(RequestHandler).__init__()
- self.daemon = True
-
- def run(self):
- """ Run the handler thread """
- # TODO: Handle DNS request
- pass
-
-
-class Server(object):
- """ A recursive DNS server """
-
- def __init__(self, port, caching, ttl):
- """ Initialize the server
-
- Args:
- port (int): port that server is listening on
- caching (bool): server uses resolver with caching if true
- ttl (int): ttl for records (if > 0) of cache
- """
- self.caching = caching
- self.ttl = ttl
- self.port = port
- self.done = False
- # TODO: create socket
-
- def serve(self):
- """ Start serving request """
- # TODO: start listening
- while not self.done:
- # TODO: receive request and open handler
- pass
-
- def shutdown(self):
- """ Shutdown the server """
- self.done = True
- # TODO: shutdown socket
diff --git a/project2/proj2_s4498062/dns/types.py b/project2/proj2_s4498062/dns/types.py
deleted file mode 100644
index 3c2fb29..0000000
--- a/project2/proj2_s4498062/dns/types.py
+++ /dev/null
@@ -1,55 +0,0 @@
-""" DNS TYPE and QTYPE values
-
-This module contains an Enum for TYPE and QTYPE values. This Enum also contains
-a method for converting Enum values to strings. See sections 3.2.2 and 3.2.3 of
-RFC 1035 for more information.
-"""
-
-class Type(object):
- """ DNS TYPE and QTYPE
-
- Usage:
- >>> Type.A
- 1
- >>> Type.CNAME
- 5
- """
- A = 1
- NS = 2
- CNAME = 5
- SOA = 6
- WKS = 11
- PTR = 12
- HINFO = 13
- MINFO = 14
- MX = 15
- TXT = 16
- AAAA = 28
- ANY = 255
-
- by_string = {
- "A": A,
- "NS": NS,
- "CNAME": CNAME,
- "SOA": SOA,
- "WKS": WKS,
- "PTR": PTR,
- "HINFO": HINFO,
- "MINFO": MINFO,
- "MX": MX,
- "TXT": TXT,
- "AAAA": AAAA,
- "*": ANY
- }
-
- by_value = dict([(y, x) for x, y in by_string.items()])
-
- @staticmethod
- def to_string(type_):
- """Convert a type to a string"""
- return Type.by_value[type_]
-
- @staticmethod
- def from_string(string):
- """Convert a string to a type"""
- return Type.by_string[string]
diff --git a/project2/proj2_s4498062/dns/zone.py b/project2/proj2_s4498062/dns/zone.py
deleted file mode 100644
index accea99..0000000
--- a/project2/proj2_s4498062/dns/zone.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python2
-
-""" Zones of domain name space
-
-See section 6.1.2 of RFC 1035 and section 4.2 of RFC 1034.
-Instead of tree structures we simply use dictionaries from domain names to
-zones or record sets.
-
-These classes are merely a suggestion, feel free to use something else.
-"""
-
-
-class Catalog(object):
- """ A catalog of zones """
-
- def __init__(self):
- """ Initialize the catalog """
- self.zones = {}
-
- def add_zone(self, name, zone):
- """ Add a new zone to the catalog
-
- Args:
- name (str): root domain name
- zone (Zone): zone
- """
- self.zones[name] = zone
-
-
-class Zone(object):
- """ A zone in the domain name space """
-
- def __init__(self):
- """ Initialize the Zone """
- self.records = {}
-
- def add_node(self, name, record_set):
- """ Add a record set to the zone
-
- Args:
- name (str): domain name
- record_set ([ResourceRecord]): resource records
- """
- self.records[name] = record_set
-
- def read_master_file(self, filename):
- """ Read the zone from a master file
-
- See section 5 of RFC 1035.
-
- Args:
- filename (str): the filename of the master file
- """
- pass
diff --git a/project2/proj2_s4498062/dns_client.py b/project2/proj2_s4498062/dns_client.py
deleted file mode 100755
index ecc3f3d..0000000
--- a/project2/proj2_s4498062/dns_client.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python2
-""" Simple DNS client
-
-A simple example of a client using the DNS resolver.
-"""
-
-import dns.resolver
-
-
-def main():
- """Run the client"""
- # Parse arguments
- import argparse
- parser = argparse.ArgumentParser(description="DNS Client")
- parser.add_argument("hostname", help="hostname to resolve")
- parser.add_argument(
- "-c", "--caching", action="store_true",
- help="Enable caching")
- parser.add_argument(
- "-t", "--ttl", metavar="time", type=int, default=0,
- help="TTL value of cached entries")
- args = parser.parse_args()
-
- # Resolve hostname
- resolver = dns.resolver.Resolver(["localhost"], 15, args.caching, args.ttl)
- hostname, aliases, addresses = resolver.gethostbyname(args.hostname)
-
- # Print output
- print hostname
- print list(aliases)
- print list(addresses)
-
-if __name__ == "__main__":
- main()
diff --git a/project2/proj2_s4498062/dns_server.py b/project2/proj2_s4498062/dns_server.py
deleted file mode 100755
index a518546..0000000
--- a/project2/proj2_s4498062/dns_server.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env python2
-
-""" DNS server
-
-This script contains the code for starting a DNS server.
-"""
-
-import dns.server
-
-
-def main():
- """Run the server"""
- # Parse arguments
- import argparse
- parser = argparse.ArgumentParser(description="DNS Server")
- parser.add_argument(
- "-c", "--caching", action="store_true",
- help="Enable caching")
- parser.add_argument(
- "-t", "--ttl", metavar="time", type=int, default=0,
- help="TTL value of cached entries (if > 0)")
- parser.add_argument(
- "-p", "--port", type=int, default=5353,
- help="Port which server listens on")
- args = parser.parse_args()
-
- # Start server
- server = dns.server.Server(args.port, args.caching, args.ttl)
- try:
- server.serve()
- except KeyboardInterrupt:
- server.shutdown()
- print()
-
-if __name__ == "__main__":
- main()
diff --git a/project2/proj2_s4498062/dns_tests.py b/project2/proj2_s4498062/dns_tests.py
deleted file mode 100755
index 71e5660..0000000
--- a/project2/proj2_s4498062/dns_tests.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env python2
-""" Tests for your DNS resolver and server """
-
-import sys
-import unittest
-
-
-port = 5353
-server = "localhost"
-
-
-class TestResolver(unittest.TestCase):
- """Unit tests for the resolver"""
- pass
-
-
-class TestResolverCache(unittest.TestCase):
- """Unit tests for the resolver cache"""
- pass
-
-
-class TestServer(unittest.TestCase):
- """Unit tests for the server"""
- pass
-
-
-def main():
- """Run the tests"""
- # Parse command line arguments
- import argparse
- parser = argparse.ArgumentParser(description="HTTP Tests")
- parser.add_argument("-s", "--server", type=str, default="localhost")
- parser.add_argument("-p", "--port", type=int, default=5001)
- args, extra = parser.parse_known_args()
- port = args.port
- server = args.server
-
- # Pass the extra arguments to unittest
- sys.argv[1:] = extra
-
- # Start test suite
- unittest.main()
-
-if __name__ == "__main__":
- main()
-