def test_header_from_bytes(self): packet = b"\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06" header = Header(1, 2, 3, 4, 5, 6) self.assertEqual(Header.from_bytes(packet), header)
def test_qr(self): header = Header(0, 0, 0, 0, 0, 0) header.qr = 1 self.assertEqual(header.qr, 1)
def test_flags_rcode(self): header = Header(0, 0, 0, 0, 0, 0) header.rcode = 15 self.assertEqual(header.flags, 15)
def test_header_to_bytes(self): header = Header(1, 2, 3, 4, 5, 6) self.assertEqual(header.to_bytes(), b"\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06")
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, []))
def test_flags_ra(self): header = Header(0, 0, 0, 0, 0, 0) header.ra = 1 self.assertEqual(header.flags, 1 << 7)
def test_rd(self): header = Header(0, 0, 0, 0, 0, 0) header.rd = 1 self.assertEqual(header.rd, 1)
def test_flags_opcode(self): header = Header(0, 0, 0, 0, 0, 0) header.opcode = 15 self.assertEqual(header.flags, 15 << 11)
def test_tc(self): header = Header(0, 0, 0, 0, 0, 0) header.tc = 1 self.assertEqual(header.tc, 1)
def test_flags_tc(self): header = Header(0, 0, 0, 0, 0, 0) header.tc = 1 self.assertEqual(header.flags, 1 << 9)
def test_flags_aa(self): header = Header(0, 0, 0, 0, 0, 0) header.aa = 1 self.assertEqual(header.flags, 1 << 10)
def gethostbyname(self, hostname, dnsserv='192.112.36.4'): """Translate a host name to IPv4 address. Currently this method contains an example. You will have to replace this 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) """ ipaddrlist = [] cnames = [] temp = [] if (self.caching): rcache = RecordCache(self.ttl) rcord = rcache.lookup(hostname, Type.ANY, Class.IN) if (rcord): for rec in rcord: if rec.type_ == Type.A: arec = rec.rdata ipaddrlist.append(arec.address) elif rec.type_ == Type.CNAME: crec = rec.rdata cnames.append(crec.cname) if ipaddrlist: return hostname, cnames, ipaddrlist elif cnames: return self.gethostbyname(cnames[0], dnsserv) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(self.timeout) # Create and send query question = Question(Name(str(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]) sock.sendto(query.to_bytes(), (str(dnsserv), 53)) # Receive response data = sock.recv(2048) response = Message.from_bytes(data) print("Number of answers: " + str(len(response.answers))) print("Number of authorities: " + str(len(response.authorities))) print("Number of additionals: " + str(len(response.additionals))) # Get data aliaslist = cnames ipaddrlist = [] dnslist = [] while response.answers: for answer in response.answers: if answer.type_ == Type.A: print("found A RR") if (self.caching): rcache.add_record(answer) ipaddrlist.append(answer.rdata.address) if answer.type_ == Type.CNAME: aliaslist.append(answer.rdata.cname) if answer.type_ == Type.NS: dnslist.append(answer.rdata.nsdname) if ipaddrlist: return hostname, aliaslist, ipaddrlist elif aliaslist: question = Question(Name(aliaslist[0]), Type.A, Class.IN) query = Message(header, [question]) sock.sendto(query.to_bytes(), (dnsserv, 53)) data = sock.recv(2048) response = Message.from_bytes(data) elif dnslist: nsname = dnslist.pop() maybe_dnsserv = self.getnsaddr(nsname, response.additionals) if maybe_dnsserv: dnsserv = maybe_dnsserv else: pass sock.sendto(query.to_bytes(), (dnsserv, 53)) data = sock.recv(2048) response = Message.from_bytes(data) else: break if response.authorities: for authority in response.authorities: if authority.type_ != Type.NS: pass dnslist.append(authority.rdata.nsdname) while dnslist: nsname = dnslist.pop() maybe_next_dnsserv = self.getnsaddr(nsname, response.additionals) if maybe_next_dnsserv: next_dns_serv = maybe_next_dnsserv else: pass (hname, aliasl, ipaddrl) = self.gethostbyname(hostname, nsname) if ipaddrl: return hname, aliasl, ipaddrl
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 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) """ alias_list = [] a_list = [] slist = [] found = False acs = self.getRecordsFromCache(hostname, Type.A, Class.IN) if acs: a_list += acs return hostname, alias_list, a_list nscs = self.matchByLabel(hostname, Type.NS, Class.IN) for ns in nscs: glue = self.getRecordsFromCache(str(ns.rdata.nsdname)) if glue: slist += glue else: slist += [ns] id = self._make_id() # Create and send query question = Question(Name(hostname), Type.A, Class.IN) header = Header(id, 0, 1, 0, 0, 0) header.qr = 0 # 0 for query header.opcode = 0 # standad query header.rd = 0 # not recursive query = Message(header, [question]) self.zone.read_master_file('dns/root.zone') sbelt = [] for root in list(self.zone.records.values()): sbelt += [r for r in root if r.type_ == Type.A] while not found: if slist: rr = slist.pop() if rr.type_ == Type.A: addr = rr.rdata.address self.sock.send((query, addr, 53)) elif rr.type_ == Type.NS: fqdn = str(rr.rdata.nsdname) _, _, a_rrs = self.gethostbyname(fqdn) slist += a_rrs continue elif rr.type_ == Type.CNAME: fqdn = str(rr.rdata.cname) _, cname_rrs, a_rrs = self.gethostbyname(fqdn) a_list += a_rrs alias_list += cname_rrs break elif sbelt: rr = sbelt.pop() addr = rr.rdata.address self.sock.send((query, addr, 53)) else: break # Receive response data = None while not data: data = self.sock.msgThere(id) response, _ = data[0] #response = Message.from_bytes(data) for answer in response.answers: if answer.type_ == Type.A: self.addRecordToCache(answer) a_list.append(answer) found = True if answer.type_ == Type.CNAME: self.addRecordToCache(answer) alias_list.append(answer) slist += [answer] continue nss = [] for auth in response.authorities: if auth.type_ == Type.NS: nss.append(auth) self.addRecordToCache(auth) a_add = {} for add in response.additionals: if add.type_ == Type.A: name = str(add.name) a_add[name] = add self.addRecordToCache(add) for ns in nss: name = str(ns.rdata.nsdname) if name in a_add: slist += [a_add[name]] else: slist += [ns] return hostname, alias_list, a_list
def test_flags_qr(self): header = Header(0, 0, 0, 0, 0, 0) header.qr = 1 self.assertEqual(header.flags, 1 << 15)
def test_flags_rd(self): header = Header(0, 0, 0, 0, 0, 0) header.rd = 1 self.assertEqual(header.flags, 1 << 8)
def test_opcode(self): header = Header(0, 0, 0, 0, 0, 0) header.opcode = 1 self.assertEqual(header.opcode, 1)
def test_ra(self): header = Header(0, 0, 0, 0, 0, 0) header.ra = 1 self.assertEqual(header.ra, 1)
def test_aa(self): header = Header(0, 0, 0, 0, 0, 0) header.aa = 1 self.assertEqual(header.aa, 1)
def run(self): """ Run the handler thread""" msg = Message.from_bytes(self.data) header = msg.header recursion = header.rd != 0 questions = msg.questions answers = [] authorative = [] additional = [] for question in questions: qname = question.qname qtype = question.qtype if question.qclass != Class.IN: pass domains = self.getdomains(str(qname)) for domain in domains: if not self.zone.records.get(domain, False): continue for rr in self.zone.records[domain]: if (rr.type_ == Type.A or rr.type_ == Type.CNAME) and domain == str(qname): answers.append(rr) if rr.type_ == Type.NS: authorative.append(rr) for rec in self.zone.records[str(rr.rdata.nsdname)]: if rec not in additional: if rec.qtype == Type.A: additional.append(rec) if authorative or answers: header_response = Header(9001, 0, 1, len(answers), len(authorative), len(additionals)) header_response.qr = True header_response.opcode(1) header_response.aa(False) header_respones.tc(False) header_response.rd(False) header_response.ra(True) header_response.z(False) header_respones.rcode(False) respons = Message(header_response, [question], answers, authorities, additionals) self.sock.sendto(respons.to_bytes(), self.addr) break if recursion and (qtype == Type.A or qtype == Type.CNAME): answers = [] resolver = Resolver(100, False, 0, True) (hostname, aliaslist, ipaddrlist) = resolver.gethostbyname(question.qname) header_response = Header(9001, 0, 1, len(aliaslist) + len(ipaddrlist), 0, 0) header_response.qr = 1 header_response.opcode = 1 header_response.aa = 0 header_response.tc = 0 header_response.rd = 0 header_response.ra = 1 header_response.rcode = 0 for addr in ipaddrlist: answers.append( ResourceRecord.from_dict({ "name": str(hostname), "type": str(Type.A), "class": str(Class.IN), "ttl": 0, "rdata": { "address": str(addr) } })) for alias in aliaslist: answers.append( ResourceRecord.from_dict({ "name": str(hostname), "type": str(Type.CNAME), "class": str(Class.IN), "ttl": 0, "rdata": { "cname": str(alias) } })) response = Message(header_response, questions, answers) self.sock.sendto(response.to_bytes(), self.addr) break