def test_authority_domain(self): question = Question(Name("server1.gumpe"), Type.A, Class.IN) header = Header(1337, 0, 1, 0, 0, 0) query = Message(header, questions=[question]) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.sendto(query.to_bytes(), (SERVER, PORT)) data = s.recv(512) s.close() message = Message.from_bytes(data) self.assertCountEqual(message.answers, [ ResourceRecord( name=Name("server1.gumpe."), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("10.0.1.5"), ), ResourceRecord( name=Name("server1.gumpe."), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("10.0.1.4"), ), ])
def send_request(self, ip, name): #create socket and request sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(self.timeout) question = Question(name, Type.A, Class.IN) header = Header(9001, 0, 1, 0, 0, 0) header.qr = 0 header.opcode = 0 header.rd = self.rd query = Message(header, [question]) sock.sendto(query.to_bytes(), (ip, 53)) # Receive response data = sock.recv(512) sock.close() response = Message.from_bytes(data) self.logHeader(response.header) if self.caching: for r in response.resources: if r.type_ == Type.A or r.type_ == Type.CNAME or r.type_ == Type.NS: self.cache.add_record(r) return response.answers, response.authorities, response.additionals
def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) question = Question(Name("google.com."), Type.A, Class.IN) header = Header(9001, 0, 1, 0, 0, 0) header.qr = 0 # 0 for query header.opcode = 0 # standad query header.rd = 1 # recursive query = Message(header, [question]) ip = (([ ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.") ] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) + ["no IP found"])[0] sock.sendto(query.to_bytes(), (ip, 53)) data = sock.recv(1024) mess = Message.from_bytes(data) """ answer = ResourceRecord.to_dict(mess.answers[0]) auth = ResourceRecord.to_dict(mess.authorities[0]) addi = ResourceRecord.to_dict(mess.additionals[0]) """ rrs = [] rrs += mess.answers + mess.authorities + mess.additionals for r in rrs: print("R", r.to_dict())
def test_concurrent_requests(self): queries = [] answers = [] question = Question(Name("gaia.cs.umass.edu"), Type.A, Class.IN) header = Header(1337, 0, 1, 0, 0, 0) header.rd = 1 queries.append(Message(header, questions=[question])) answers.append([ ResourceRecord(name=Name("gaia.cs.umass.edu"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("128.119.245.12")), ]) question = Question(Name("server2.gumpe"), Type.A, Class.IN) header = Header(420, 0, 1, 0, 0, 0) queries.append(Message(header, questions=[question])) answers.append([ ResourceRecord(name=Name("server2.gumpe"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("10.0.1.7")), ]) header = Header(69, 0, 1, 0, 0, 0) question = Question(Name("www.gumpe"), Type.A, Class.IN) queries.append(Message(header, questions=[question])) answers.append([ ResourceRecord(name=Name("www.gumpe"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("10.0.1.7")), ResourceRecord(name=Name("www.gumpe"), type_=Type.CNAME, class_=Class.IN, ttl=0, rdata=CNAMERecordData(Name("server2.gumpe"))), ]) sockets = [ socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for _ in range(len(queries)) ] for i in range(len(sockets)): sockets[i].sendto(queries[i].to_bytes(), (SERVER, PORT)) responses = [] for i in range(len(sockets)): responses.append(Message.from_bytes(sockets[i].recv(1024))) for i in range(len(sockets)): sockets[i].close() for i in range(len(queries)): self.assertCountEqual(responses[i].answers, answers[i])
def send_query(sock, hostname, ip): # Create and send query question = Question(Name(hostname), Type.A, Class.IN) header = Header(randint(0, 2**16), 0, 1, 0, 0, 0) header.qr = 0 header.opcode = 0 header.rd = 0 # no recursion desired query = Message(header, [question]) sock.sendto(query.to_bytes(), (ip, 53)) # Receive response data = sock.recv(512) return Message.from_bytes(data)
def test_message_from_bytes(self, HeaderMock, QuestionMock, ResourceMock): packet = b"\x01\x02\x03\x04\x05" header = Header(9001, 0, 1, 1, 1, 1) HeaderMock.from_bytes.return_value = header QuestionMock.from_bytes.return_value = (1, 13) ResourceMock.from_bytes.side_effect = [(2, 14), (3, 15), (4, 16)] message1 = Message.from_bytes(packet) message2 = Message(header, [1], [2], [3], [4]) self.assertEqual(message1, message2) HeaderMock.from_bytes.assert_called_with(packet) QuestionMock.from_bytes.assert_called_with(packet, 12) calls = [call(packet, 13), call(packet, 14), call(packet, 15)] ResourceMock.from_bytes.assert_has_calls(calls)
def create_http_wire_response( request: Request, query_response: Message ) -> Response: logger = logging.getLogger("doh-server") logger.debug( "[HTTP] " + str(request.method) + " " + str(request.headers.get("Accept")) ) if isinstance(query_response, Message): query_response.id = 0 body = query_response.to_wire() response = Response(body, content_type=DOH_CONTENT_TYPE) response.headers["content-length"] = str(len(body)) return set_headers(request, response, query_response) else: return Response(query_response)
def test_server_no_caching(self): data = self.send_query("nickstracke.xyz", PORT + 5) mess = Message.from_bytes(data) ip1 = mess.answers[0].rdata.address ip2 = socket.gethostbyname("nickstracke.xyz") self.assertEqual(ip1, ip2)
def send_response(self, records, authoritative, error=0): """Send a response to some message.""" if not error and len(records) == 0: error = 3 # NXDOMAIN (Domain Name not found) if error != 0: header = Header(self.message.header.ident, 0, 0, 0, 0, 0) header.rcode = error else: header = Header(self.message.header.ident, 0, 0, len(records), 0, 0) header.aa = authoritative # Authoritative Answer header.qr = 1 # Message is Response header.rd = self.message.header.rd # Recursion desired header.ra = 1 # Recursion Available response = Message(header, answers=records) self.sock.sendto(response.to_bytes(), self.address)
def test_outside_zone(self): question = Question(Name("gaia.cs.umass.edu"), Type.A, Class.IN) header = Header(1337, 0, 1, 0, 0, 0) header.rd = 1 query = Message(header, questions=[question]) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.sendto(query.to_bytes(), (SERVER, PORT)) data = s.recv(512) s.close() message = Message.from_bytes(data) self.assertEqual(message.answers, [ ResourceRecord(name=Name("gaia.cs.umass.edu"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("128.119.245.12")), ])
def build_message(self,id, rd, aa, rcode, questions, answers, authorities, additionals): header = Header(id, 0, len(questions), len(answers), len(authorities), len(additionals)) header.qr = 1 header.opcode = 0 header.rd = rd header.ra = 1 header.aa = aa header.rcode = rcode return Message(header, questions=questions, answers=answers, authorities=authorities, additionals=additionals)
def sendQuestion(self, hostname, server): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(self.timeout) # Create and send query identifier = 9001 # placeholder question = Question(Name(hostname), Type.A, Class.IN) header = Header(identifier, 0, 1, 0, 0, 0) header.qr = 0 header.opcode = 0 header.rd = 1 query = Message(header, [question]) sock.sendto(query.to_bytes(), (server, 53)) # Receive response data = sock.recv(512) response = Message.from_bytes(data) sock.close() return response
def test_concurrency(self): s2 = threading.Thread(target=self.send_query, args=("nickstracke.xyz", PORT + 2)) s2.daemon = True s2.start() data = self.send_query("nickstracke.xyz", PORT + 3) mess = Message.from_bytes(data) ip1 = mess.answers[0].rdata.address ip2 = socket.gethostbyname("nickstracke.xyz") self.assertEqual(ip1, ip2)
def send_query(self, hostname, port): question = Question(Name(hostname), Type.A, Class.IN) # use port as id header = Header(port, 0, 1, 0, 0, 0) header.qr = 0 # 0 for query header.opcode = 0 # standad query header.rd = 1 # recursive query = Message(header, [question]) ip = (([ ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.") ] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)] ][0][1]]) + ["no IP found"])[0] sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((ip, port)) print("Sock Test:", sock) sock.sendto(query.to_bytes(), (ip, PORT)) data = sock.recv(1024) sock.close() return data
def test_message_to_bytes(self): header = MagicMock() header.to_bytes.return_value = b"\x01" question = MagicMock() question.to_bytes.return_value = b"\x02" answer = MagicMock() answer.to_bytes.return_value = b"\x03" authority = MagicMock() authority.to_bytes.return_value = b"\x04" additional = MagicMock() additional.to_bytes.return_value = b"\x05" message = Message(header, [question], [answer], [authority], [additional]) self.assertEqual(message.to_bytes(), b"\x01\x02\x03\x04\x05")
def run(self): """ Run the handler thread""" try: self.message = Message.from_bytes(self.data) except: self.message = Message(Header(0, 0, 0, 0, 0, 0)) self.send_response([], False, 1) return self.domain = self.message.questions[0].qname print(threading.current_thread()) print("\tDomain:", self.domain) print("\tAddress:", self.address) authoritative, records = self.lookup_zone(self.domain) if records is None: if self.message.header.rd: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) resolver = Resolver(5, Server.cache) records = resolver.query_recursive(sock, self.domain, Resolver.root_server) sock.close() else: records = [] self.send_response(records, authoritative)
def serve(self): """ Start serving request """ print("[+] - DNS Server up and running.") while not self.done: data, addr = self.socket.recvfrom(1024) try: message = Message.from_bytes(data) except: print("[-] - Received invalid data.") continue rh = RequestHandler(self.socket, addr, self.ttl, message, self.resolver, self.catalog) rh.start()
def serve(self): """Start serving requests""" sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(("", self.port)) while not self.done: data, address = sock.recvfrom(65565) message = Message.from_bytes(data) rd = message.header.rd rcode = 0 aa = 1 self.log("REQUEST RECIEVED:", address) answers, authorities, additionals = self.zone_resolution(message.questions) if answers == []: if authorities == [] and additionals == []: self.log("\tZONE RESOLUTION FAILED") answers = self.consult_cache(message.questions) if answers == []: self.log("\tCACHE LOOKUP FAILED") rcode = 3 else: aa = 0 if rcode == 3 and rd == 1: rcode = 0 self.log("\tCALLING RESOLVER") resolver = Resolver(5, True, 0) resolver.rd = 0 resolver.rootip = "198.41.0.4" for q in message.questions: self.log("\t\tRESOLVING:", q.qname) hostname, namelist, iplist = resolver.gethostbyname(str(q.qname)) if hostname == str(q.qname): for ip in iplist: answers.append(ResourceRecord(q.qname, Type.A, Class.IN, self.ttl, ARecordData(ip))) for n in namelist: answers.append(ResourceRecord(q.qname, Type.CNAME, Class.IN, self.ttl, CNAMERecordData(n))) self.log("SENDING RESPONSE:", rcode, "\n") mess = self.build_message(message.header.ident, rd, aa, rcode, message.questions, answers, authorities, additionals) sock.sendto(mess.to_bytes(), address)
def serve(self): """Start serving requests""" sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(('localhost', self.port)) zone = Zone().read_master_file("roothints.md") #load zone file into memory while not self.done: print("Waiting...") data, address = sock.recvfrom(512) #Following piece of code results in a weird error message which I do not understand. I do quite literally the same thing some other students do but it results in an error message. req = Message.from_bytes(data) questions = req.questions for question in questions: hostname = question.qname #consult zone and try to answer and cache #if recursion is enabled, and zone does not help: use resolver #send back reply. pass
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
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 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_authority(self): data = self.send_query("ns1.ourdomain.com", PORT + 4) mess = Message.from_bytes(data) ip1 = mess.answers[0].rdata.address print(ip1) self.assertEqual(ip1, "255.255.255.255")