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/__init__.py2
-rw-r--r--project2/proj2_s4498062/dns/cache.py182
-rw-r--r--project2/proj2_s4498062/dns/classes.py86
-rw-r--r--project2/proj2_s4498062/dns/domainname.py226
-rw-r--r--project2/proj2_s4498062/dns/message.py544
-rw-r--r--project2/proj2_s4498062/dns/rcodes.py138
-rw-r--r--project2/proj2_s4498062/dns/resolver.py140
-rw-r--r--project2/proj2_s4498062/dns/resource.py472
-rw-r--r--project2/proj2_s4498062/dns/server.py106
-rw-r--r--project2/proj2_s4498062/dns/types.py110
-rw-r--r--project2/proj2_s4498062/dns/zone.py108
11 files changed, 1057 insertions, 1057 deletions
diff --git a/project2/proj2_s4498062/dns/__init__.py b/project2/proj2_s4498062/dns/__init__.py
index 144e7c6..18ff536 100644
--- a/project2/proj2_s4498062/dns/__init__.py
+++ b/project2/proj2_s4498062/dns/__init__.py
@@ -1 +1 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python2
diff --git a/project2/proj2_s4498062/dns/cache.py b/project2/proj2_s4498062/dns/cache.py
index 1b64b51..eab51c5 100644
--- a/project2/proj2_s4498062/dns/cache.py
+++ b/project2/proj2_s4498062/dns/cache.py
@@ -1,91 +1,91 @@
-#!/usr/bin/env python2
-
-"""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
-
-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
- }
- 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"])
- return ResourceRecord(name, type_, class_, ttl, rdata)
-
-
-class RecordCache(object):
- """ Cache for ResourceRecords """
-
- def __init__(self, ttl):
- """ Initialize the RecordCache
-
- Args:
- ttl (int): TTL of cached entries (if > 0)
- """
- self.records = []
- self.ttl = ttl
-
- 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
- """
- pass
-
- def add_record(self, record):
- """ Add a new Record to the cache
-
- Args:
- record (ResourceRecord): the record added to the cache
- """
- pass
-
- def read_cache_file(self):
- """ Read the cache file from disk """
- pass
-
- def write_cache_file(self):
- """ Write the cache file to disk """
- pass
-
-
+#!/usr/bin/env python2
+
+"""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
+
+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
+ }
+ 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"])
+ return ResourceRecord(name, type_, class_, ttl, rdata)
+
+
+class RecordCache(object):
+ """ Cache for ResourceRecords """
+
+ def __init__(self, ttl):
+ """ Initialize the RecordCache
+
+ Args:
+ ttl (int): TTL of cached entries (if > 0)
+ """
+ self.records = []
+ self.ttl = ttl
+
+ 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
+ """
+ pass
+
+ def add_record(self, record):
+ """ Add a new Record to the cache
+
+ Args:
+ record (ResourceRecord): the record added to the cache
+ """
+ pass
+
+ def read_cache_file(self):
+ """ Read the cache file from disk """
+ pass
+
+ def write_cache_file(self):
+ """ Write the cache file to disk """
+ pass
+
+
diff --git a/project2/proj2_s4498062/dns/classes.py b/project2/proj2_s4498062/dns/classes.py
index aeb1da7..767b2f6 100644
--- a/project2/proj2_s4498062/dns/classes.py
+++ b/project2/proj2_s4498062/dns/classes.py
@@ -1,43 +1,43 @@
-#!/usr/bin/env python2
-
-""" 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_):
- return Class.by_value[class_]
-
- @staticmethod
- def from_string(string):
- return Class.by_string[string]
+#!/usr/bin/env python2
+
+""" 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_):
+ return Class.by_value[class_]
+
+ @staticmethod
+ def from_string(string):
+ return Class.by_string[string]
diff --git a/project2/proj2_s4498062/dns/domainname.py b/project2/proj2_s4498062/dns/domainname.py
index 09f98fa..85e14bf 100644
--- a/project2/proj2_s4498062/dns/domainname.py
+++ b/project2/proj2_s4498062/dns/domainname.py
@@ -1,113 +1,113 @@
-#!/usr/bin/env python2
-
-""" 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 i, 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):
- begin_offset = offset
- dnames = []
-
- # Read the domain names
- for i 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
+#!/usr/bin/env python2
+
+""" 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 i, 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):
+ begin_offset = offset
+ dnames = []
+
+ # Read the domain names
+ for i 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
index f948f2f..3c86443 100644
--- a/project2/proj2_s4498062/dns/message.py
+++ b/project2/proj2_s4498062/dns/message.py
@@ -1,272 +1,272 @@
-#!/usr/bin/env python2
-
-""" 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 socket
-import struct
-
-from dns.classes import Class
-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=[], answers=[], authorities=[], additionals=[]):
- """ 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
- self.answers = answers
- self.authorities = authorities
- self.additionals = additionals
-
- @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 i in range(header.qd_count):
- question, offset = Question.from_bytes(packet, offset, parser)
- questions.append(question)
-
- # Parse answers
- answers = []
- for i in range(header.an_count):
- answer, offset = ResourceRecord.from_bytes(packet, offset, parser)
- answers.append(answer)
-
- # Parse authorities
- authorities = []
- for i in range(header.ns_count):
- authority, offset = ResourceRecord.from_bytes(packet, offset, parser)
- authorities.append(authority)
-
- # Parse additionals
- additionals = []
- for i in range(header.ar_count):
- additional, offset = ResourceRecord.from_bytes(packet, offset, parser)
- additionals.append(additional)
-
- return cls(header, questions, answers, authorities, additionals)
-
-
-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):
- return self._flags
- @flags.setter
- def flags(self, value):
- if value >= (1 << 16):
- raise ValueError("value too big for flags")
- self._flags = value
-
- @property
- def qr(self):
- return self._flags & (1 << 15)
- @qr.setter
- def qr(self, value):
- if value:
- self._flags |= (1 << 15)
- else:
- self._flags &= ~(1 << 15)
-
- @property
- def opcode(self):
- return (self._flags & (((1 << 4) - 1) << 11)) >> 11
- @opcode.setter
- def opcode(self, value):
- if value > 0b1111:
- raise ValueError("invalid opcode")
- self._flags &= ~(((1 << 4) - 1) << 11)
- self._flags |= value << 11
-
- @property
- def aa(self):
- return self._flags & (1 << 10)
- @aa.setter
- def aa(self, value):
- if value:
- self._flags |= (1 << 10)
- else:
- self._flags &= ~(1 << 10)
-
- @property
- def tc(self):
- return self._flags & (1 << 9)
- @tc.setter
- def tc(self, value):
- if value:
- self._flags |= (1 << 9)
- else:
- self._flags &= ~(1 << 9)
-
- @property
- def rd(self):
- return self._flags & (1 << 8)
- @rd.setter
- def rd(self, value):
- if value:
- self._flags |= (1 << 8)
- else:
- self._flags &= ~(1 << 8)
-
- @property
- def ra(self):
- return self._flags & (1 << 7)
- @ra.setter
- def ra(self, value):
- if value:
- self._flags |= (1 << 7)
- else:
- self._flags &= ~(1 << 7)
-
- @property
- def z(self):
- return (self._flags & (((1 << 3) - 1) << 4) >> 4)
- @z.setter
- def z(self, value):
- if value:
- raise ValueError("non-zero zero flag")
-
- @property
- def rcode(self):
- return self._flags & ((1 << 4) - 1)
- @rcode.setter
- def rcode(self, value):
- 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
+#!/usr/bin/env python2
+
+""" 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 socket
+import struct
+
+from dns.classes import Class
+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=[], answers=[], authorities=[], additionals=[]):
+ """ 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
+ self.answers = answers
+ self.authorities = authorities
+ self.additionals = additionals
+
+ @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 i in range(header.qd_count):
+ question, offset = Question.from_bytes(packet, offset, parser)
+ questions.append(question)
+
+ # Parse answers
+ answers = []
+ for i in range(header.an_count):
+ answer, offset = ResourceRecord.from_bytes(packet, offset, parser)
+ answers.append(answer)
+
+ # Parse authorities
+ authorities = []
+ for i in range(header.ns_count):
+ authority, offset = ResourceRecord.from_bytes(packet, offset, parser)
+ authorities.append(authority)
+
+ # Parse additionals
+ additionals = []
+ for i in range(header.ar_count):
+ additional, offset = ResourceRecord.from_bytes(packet, offset, parser)
+ additionals.append(additional)
+
+ return cls(header, questions, answers, authorities, additionals)
+
+
+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):
+ return self._flags
+ @flags.setter
+ def flags(self, value):
+ if value >= (1 << 16):
+ raise ValueError("value too big for flags")
+ self._flags = value
+
+ @property
+ def qr(self):
+ return self._flags & (1 << 15)
+ @qr.setter
+ def qr(self, value):
+ if value:
+ self._flags |= (1 << 15)
+ else:
+ self._flags &= ~(1 << 15)
+
+ @property
+ def opcode(self):
+ return (self._flags & (((1 << 4) - 1) << 11)) >> 11
+ @opcode.setter
+ def opcode(self, value):
+ if value > 0b1111:
+ raise ValueError("invalid opcode")
+ self._flags &= ~(((1 << 4) - 1) << 11)
+ self._flags |= value << 11
+
+ @property
+ def aa(self):
+ return self._flags & (1 << 10)
+ @aa.setter
+ def aa(self, value):
+ if value:
+ self._flags |= (1 << 10)
+ else:
+ self._flags &= ~(1 << 10)
+
+ @property
+ def tc(self):
+ return self._flags & (1 << 9)
+ @tc.setter
+ def tc(self, value):
+ if value:
+ self._flags |= (1 << 9)
+ else:
+ self._flags &= ~(1 << 9)
+
+ @property
+ def rd(self):
+ return self._flags & (1 << 8)
+ @rd.setter
+ def rd(self, value):
+ if value:
+ self._flags |= (1 << 8)
+ else:
+ self._flags &= ~(1 << 8)
+
+ @property
+ def ra(self):
+ return self._flags & (1 << 7)
+ @ra.setter
+ def ra(self, value):
+ if value:
+ self._flags |= (1 << 7)
+ else:
+ self._flags &= ~(1 << 7)
+
+ @property
+ def z(self):
+ return (self._flags & (((1 << 3) - 1) << 4) >> 4)
+ @z.setter
+ def z(self, value):
+ if value:
+ raise ValueError("non-zero zero flag")
+
+ @property
+ def rcode(self):
+ return self._flags & ((1 << 4) - 1)
+ @rcode.setter
+ def rcode(self, value):
+ 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
index d1b8c15..a35482c 100644
--- a/project2/proj2_s4498062/dns/rcodes.py
+++ b/project2/proj2_s4498062/dns/rcodes.py
@@ -1,69 +1,69 @@
-#!/usr/bin/env python2
-
-""" 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):
- return RCode.by_value[rcode]
-
- @staticmethod
- def from_string(string):
- return RCode.by_string[string]
+#!/usr/bin/env python2
+
+""" 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):
+ return RCode.by_value[rcode]
+
+ @staticmethod
+ def from_string(string):
+ return RCode.by_string[string]
diff --git a/project2/proj2_s4498062/dns/resolver.py b/project2/proj2_s4498062/dns/resolver.py
index c9f6632..ca4be95 100644
--- a/project2/proj2_s4498062/dns/resolver.py
+++ b/project2/proj2_s4498062/dns/resolver.py
@@ -1,70 +1,70 @@
-#!/usr/bin/env python2
-
-""" 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 socket
-
-from dns.cache import RecordCache
-from dns.classes import Class
-from dns.message import Message, Header, Question
-from dns.rcodes import RCode
-from dns.types import Type
-
-class Resolver(object):
- """ DNS resolver """
-
- def __init__(self, caching, ttl):
- """ Initialize the resolver
-
- Args:
- caching (bool): caching is enabled if True
- ttl (int): ttl of cache entries (if > 0)
- """
- self.caching = caching
- self.ttl = ttl
-
- 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
-
- Returns:
- (str, [str], [str]): (hostname, aliaslist, ipaddrlist)
- """
- sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- sock.settimeout(timeout)
-
- # Create and send query
- question = dns.message.Question(hostname, Type.A, Class.IN)
- header = dns.message.Header(9001, 0, 1, 0, 0, 0)
- header.qr = 0
- header.opcode = 0
- header.rd = 1
- query = dns.message.Message(header, [question])
- sock.sendto(query.to_bytes(), ("8.8.8.8", 53))
-
- # Receive response
- data = sock.recv(512)
- response = dns.message.Message.from_bytes(data)
-
- # Get data
- aliases = []
- for additional in response.additionals:
- if additional.type_ == Type.CNAME:
- aliases.append(additional.rdata.data)
- addresses = []
- for answer in response.answers:
- if answer.type_ == Type.A:
- addresses.append(answer.rdata.data)
-
- return hostname, aliases, addresses
+#!/usr/bin/env python2
+
+""" 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 socket
+
+from dns.cache import RecordCache
+from dns.classes import Class
+from dns.message import Message, Header, Question
+from dns.rcodes import RCode
+from dns.types import Type
+
+class Resolver(object):
+ """ DNS resolver """
+
+ def __init__(self, caching, ttl):
+ """ Initialize the resolver
+
+ Args:
+ caching (bool): caching is enabled if True
+ ttl (int): ttl of cache entries (if > 0)
+ """
+ self.caching = caching
+ self.ttl = ttl
+
+ 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
+
+ Returns:
+ (str, [str], [str]): (hostname, aliaslist, ipaddrlist)
+ """
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.settimeout(timeout)
+
+ # Create and send query
+ question = dns.message.Question(hostname, Type.A, Class.IN)
+ header = dns.message.Header(9001, 0, 1, 0, 0, 0)
+ header.qr = 0
+ header.opcode = 0
+ header.rd = 1
+ query = dns.message.Message(header, [question])
+ sock.sendto(query.to_bytes(), ("8.8.8.8", 53))
+
+ # Receive response
+ data = sock.recv(512)
+ response = dns.message.Message.from_bytes(data)
+
+ # Get data
+ aliases = []
+ for additional in response.additionals:
+ if additional.type_ == Type.CNAME:
+ aliases.append(additional.rdata.data)
+ addresses = []
+ for answer in response.answers:
+ if answer.type_ == Type.A:
+ addresses.append(answer.rdata.data)
+
+ return hostname, aliases, addresses
diff --git a/project2/proj2_s4498062/dns/resource.py b/project2/proj2_s4498062/dns/resource.py
index 57fc53e..350c87d 100644
--- a/project2/proj2_s4498062/dns/resource.py
+++ b/project2/proj2_s4498062/dns/resource.py
@@ -1,236 +1,236 @@
-#!/usr/bin/env python2
-
-""" 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
-
-from dns.classes import Class
-from dns.types import Type
-
-
-class ResourceRecord(object):
- """ DNS resource record """
- def __init__(self, name, type_, class_, ttl, rdata):
- """ 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
-
- def to_bytes(self, offset, composer):
- """ Convert ResourceRecord to bytes """
- name = composer.to_bytes(offset, [self.name])
- offset += len(name)
- rdata = self.rdata.to_bytes(offset, composer)
- return (name +
- struct.pack("!HHIH",
- self.type_,
- self.class_,
- self.ttl,
- len(rdata)) +
- rdata)
-
- @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, rdlength = struct.unpack_from("!HHIH", packet, offset)
- offset += 10
- rdata = RecordData.from_bytes(type_, packet, offset, rdlength, parser)
- offset += rdlength
- 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):
- 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):
- 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):
- 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):
- 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(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):
- 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(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)
+#!/usr/bin/env python2
+
+""" 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
+
+from dns.classes import Class
+from dns.types import Type
+
+
+class ResourceRecord(object):
+ """ DNS resource record """
+ def __init__(self, name, type_, class_, ttl, rdata):
+ """ 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
+
+ def to_bytes(self, offset, composer):
+ """ Convert ResourceRecord to bytes """
+ name = composer.to_bytes(offset, [self.name])
+ offset += len(name)
+ rdata = self.rdata.to_bytes(offset, composer)
+ return (name +
+ struct.pack("!HHIH",
+ self.type_,
+ self.class_,
+ self.ttl,
+ len(rdata)) +
+ rdata)
+
+ @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, rdlength = struct.unpack_from("!HHIH", packet, offset)
+ offset += 10
+ rdata = RecordData.from_bytes(type_, packet, offset, rdlength, parser)
+ offset += rdlength
+ 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):
+ 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):
+ 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):
+ 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):
+ 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(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):
+ 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(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
index 7a8fc6d..7234e36 100644
--- a/project2/proj2_s4498062/dns/server.py
+++ b/project2/proj2_s4498062/dns/server.py
@@ -1,53 +1,53 @@
-#!/usr/bin/env python2
-
-""" 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.
-"""
-
-import socket
-from threading import Thread
-
-
-class RequestHandler(Thread):
- """ A handler for requests to the DNS server """
-
- def __init__(self):
- """ Initialize the handler thread """
- super().__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
- # 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
+#!/usr/bin/env python2
+
+""" 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.
+"""
+
+import socket
+from threading import Thread
+
+
+class RequestHandler(Thread):
+ """ A handler for requests to the DNS server """
+
+ def __init__(self):
+ """ Initialize the handler thread """
+ super().__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
+ # 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
index 27f2652..f0d9ef3 100644
--- a/project2/proj2_s4498062/dns/types.py
+++ b/project2/proj2_s4498062/dns/types.py
@@ -1,55 +1,55 @@
-#!/usr/bin/env python2
-
-""" 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_):
- return Type.by_value[type_]
-
- @staticmethod
- def from_string(string):
- return Type.by_string[string]
+#!/usr/bin/env python2
+
+""" 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_):
+ return Type.by_value[type_]
+
+ @staticmethod
+ def from_string(string):
+ return Type.by_string[string]
diff --git a/project2/proj2_s4498062/dns/zone.py b/project2/proj2_s4498062/dns/zone.py
index e4d3f27..accea99 100644
--- a/project2/proj2_s4498062/dns/zone.py
+++ b/project2/proj2_s4498062/dns/zone.py
@@ -1,54 +1,54 @@
-#!/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
+#!/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