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 """ records = re.finditer( Zone.record_re, open(filename, "r").read(), flags=re.MULTILINE ) for r in records: domain, ttl, class_, type_, rdata = r.groups() domain = domain or self.last_domain self.last_domain = domain record = ResourceRecord( name=Name(domain), type_=Type[type_], class_=Class[class_], ttl=int(ttl) if ttl is not None else Zone.default_ttl, rdata=RecordData.create_from_str(Type[type_], rdata) ) if domain in self.records: self.records[domain].append(record) else: self.records[domain] = [record]
def load_and_parse(self, content): content = re.sub(re.compile(";.*?\n"), "\n", content) #Remove comments content = re.sub(re.compile("\n\n*\n"), "\n", content) #Remove whitespaces content = re.sub(re.compile(" * "), " ", content) #Remove multiple spaces between words content = re.sub(re.compile("\t\t*\t"), " ", content) #Remove tabs between words content = re.sub(re.compile("\n *"), "\n", content) #Remove spaces at start of line content = re.sub(re.compile("\n\t*"), "\n", content) #Remove tabs at start of line content = re.compile(r'\(.*?\)', re.DOTALL)\ .sub(lambda x: x.group().replace('\n', ''), content) #Remove newlines between () content = re.sub(re.compile("\t+"), " ", content) #Remove tabs between words default_ttl = None origin = None recordSet = [] for line in content.split('\n'): if line[:4] == "$TTL": prev_ttl = self.time_to_seconds(line[4:].strip()) elif line[:7] == "$ORIGIN": origin = line[7:].strip() elif "SOA" not in line: parts = line.split(' ') rr_name = parts[0] rr_ttl = self.time_to_seconds(parts[1]) offset = int(rr_ttl == 0) if offset: rr_ttl = prev_ttl rr_class = Class[parts[1 + offset]] rr_type = parts[2 + offset] if Type[rr_type] == Type.CNAME or Type[rr_type] == Type.NS: rr_data = RecordData.create( Type[rr_type], Name(parts[3 + offset].rstrip('.'))) else: rr_data = RecordData.create(Type[rr_type], parts[3 + offset].rstrip('.')) self.add_node( rr_name, ResourceRecord(Name(rr_name), Type[rr_type], rr_class, rr_ttl, rr_data))
def setUp(self): # put invalid record in cache file record_data = RecordData.create(Type.A, "192.168.123.456") self.rr = ResourceRecord("invalid.invalid", Type.A, Class.IN, 3, record_data) cache = RecordCache() cache.add_record(self.rr) cache.write_cache_file()
def test_cache_lookup(self): """ Add a record to the cache and look it up """ rr = ResourceRecord("wiki.nl", Type.A, Class.IN, self.ttl, RecordData.create(Type.A, "192.168.123.456")) cache = RecordCache() cache.add_record(rr) lookup_vals = cache.lookup("wiki.nl", Type.A, Class.IN) self.assertEqual([rr], lookup_vals)
def test_TTL_expiration(self): """ cache a record, wait till ttl expires, see if record is removed from cache """ rr = ResourceRecord("wiki.nl", Type.A, Class.IN, self.ttl, RecordData.create(Type.A, "192.168.123.456")) cache = RecordCache() cache.add_record(rr) time.sleep(rr.ttl) lookup_vals = cache.lookup("wiki.nl", Type.A, Class.IN) self.assertFalse(lookup_vals)
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)
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"]) t = dct["time"] return ResourceRecord(name, type_, class_, ttl, rdata, t)
def test_cache_disk_io(self): """ Add a record to the cache, write to disk, read from disk, do a lookup """ rr = ResourceRecord("wiki.nl", Type.A, Class.IN, self.ttl, RecordData.create(Type.A, "192.168.123.456")) cache = RecordCache() cache.write_cache_file() # overwrite the current cache file # add rr to cache and write to disk cache.add_record(rr) cache.write_cache_file() # read from disk again new_cache = RecordCache() new_cache.read_cache_file() lookup_vals = new_cache.lookup("wiki.nl", Type.A, Class.IN) self.assertEqual([rr], lookup_vals)
def setUpClass(cls): cls.ttl = 3 rr = ResourceRecord("wiki.nl", Type.A, Class.IN, cls.ttl, RecordData.create(Type.A, "192.168.123.456"))
def handle_request(self): """ Attempts to answer the received query """ #Check this next to the given algorithm #print("Catalog:",self.catalog.zones) #for zone in self.catalog.zones: # print("Records:",self.catalog.zones[zone].records) if self.message.header.opcode != 0: #Send a not implemented error, we don't need to support those kinds of queries print("[-] - Received a nonstandard query. This is unsupported.") header = Header(ident, 0, 1, 0, 0, 0) header.qr = 1 header.rd = self.message.header.rd header.ra = 1 header.rcode = 4 self.sendResponse(Message(header, self.message.questions, [])) return #print("[*] - Handling request.") if len(self.message.questions) != 1: #Send a format error response print("[-] - Invalid request.") header = Header(ident, 0, 1, 0, 0, 0) header.qr = 1 header.rd = self.message.header.rd header.ra = 1 header.rcode = 1 self.sendResponse(Message(header, self.message.questions, [])) return #print("MSG:",self.message) #print("RECEIVED QUESTION",self.message.questions[0]) hname = str(self.message.questions[0].qname) #print("Solving",hname,type(hname)) ident = self.message.header.ident #print("Checking zone") answer, authority, found = self.check_zone(hname) #print("Wat we in de zone hebben gevonden") #print("ANS:",answer,"AUTH:",authority,"FOUND:",found) #found = False if found: print("Found in zone") header = Header(ident, 0, 1, len(answer), len(authority), 0) header.qr = 1 header.aa = 1 header.rd = self.message.header.rd header.ra = 1 self.sendResponse( Message(header, self.message.questions, answer, authority)) elif self.message.header.rd == 1: h, al, ad = self.resolver.gethostbyname(hname) #Make and send th appropriate response header = Header(ident, 0, 1, len(al) + len(ad), 0, 0) header.qr = 1 header.rd = self.message.header.rd header.ra = 1 aliases = [ ResourceRecord(Name(h), Type.CNAME, Class.IN, self.ttl, RecordData.create(Type.CNAME, Name(alias))) for alias in al ] addresses = [ ResourceRecord(Name(h), Type.A, Class.IN, self.ttl, RecordData.create(Type.A, address)) for address in ad ] self.sendResponse( Message(header, self.message.questions, aliases + addresses)) else: #Send an empty response header = Header(ident, 0, 1, 0, 0, 0) header.qr = 1 header.rd = self.message.header.rd header.ra = 1 header.rcode = 0 self.sendResponse(Message(header, self.message.questions, []))