def reply_for_not_found(income_record): header = DNSHeader(id=income_record.header.id, bitmap=income_record.header.bitmap, qr=1) header.set_rcode(0) # 3 DNS_R_NXDOMAIN, 2 DNS_R_SERVFAIL, 0 DNS_R_NOERROR record = DNSRecord(header, q=income_record.q) return record
def resolve_mdns(self, request, handler, rewrite=None): sock = get_mdns_socket() d = DNSRecord(DNSHeader(id=0, bitmap=0), q=request.q) sock.sendto(d.pack(), (nameserver4, 5353)) # sock.sendto(d.pack(), (nameserver6, 5353)) qname = request.q.qname if rewrite: request.q.qname = rewrite reply = request.reply() while True: buf, remote = sock.recvfrom(8192) d = DNSRecord.parse(buf) success = False if (d.header.aa == 1) and (d.header.a > 0): for response in d.rr: if str(response.rname) == qname: success = True response.rclass = CLASS.IN # These two lines can be deleted if we dont want the original response reply.add_answer(response) response = RR.fromZone(response.toZone())[0] if rewrite: response.rname = rewrite reply.add_answer(response) # print(reply) if success: break return reply
def query(self, peer, request): id = request.header.id qname = request.q.qname queryType = request.q.qtype reply = DNSRecord( DNSHeader(id=id, qr=1, aa=1, ra=1), q=request.q ) def cnameRecursion(dHost): global tmpRes # used for overwriting previous recursion value tmpData = dbTest(dHost) # First: get CNAME of desired host cnameAddress = [i[1] for i in tmpData if i[0] == 'CNAME'] tmpRes = (dHost,tmpData) if cnameAddress: newAddr = checkMacro(cnameAddress[0],dHost,peer) reply.add_answer(RR(dHost, QTYPE.CNAME, rdata=CNAME(newAddr))) # Second: get desired QTYPE from desired host printOut(peer,QTYPE.CNAME,str(dHost),newAddr) cnameRecursion(newAddr) return tmpRes qname,rData = cnameRecursion(qname) if queryType == QTYPE.TXT: # TXT rData = [i[1] for i in rData if i[0] == 'TXT'] # Add TXT Record printData = [] for tmprecord in rData: record = checkMacro(tmprecord,qname,peer) n = 255 if len(record) > 20: printData += [ record[:15]+'...(%d)' % len(record) ] else: printData = [record] if len(record) > n: record = [record[i:i+n] for i in range(0, len(record), n)] reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT(record if isinstance(record,list) else [record,]))) printOut(peer,queryType,str(qname),printData) else: rData = [i[1] for i in rData if i[0] == qTypeDict[queryType]] resIP = '' if len(rData): resIP = rData elif '*' in db: #elif db.has_key('*'): #python2 only resIP = [i[1] for i in dbTest('*') if i[0] == 'A'] for tmpip in resIP: ip = checkMacro(tmpip,qname,peer) # Add A Record reply.add_answer(RR(qname, QTYPE.A, rdata=A(ip))) if resIP: printOut(peer,queryType,str(qname),', '.join(resIP)) else: printOut(peer,queryType,str(qname),'NONE') # Send To Client self.fire(write(peer, reply.pack()))
def dns_handler(s, peer, data): request = DNSRecord.parse(data) id = request.header.id qname = request.q.qname qtype = request.q.qtype print "------ Request (%s): %r (%s)" % (str(peer), qname.label, QTYPE[qtype]) print request reply = DNSRecord(DNSHeader(id=id, qr=1, aa=1, ra=1), q=request.q) if qtype == QTYPE.A: reply.add_answer(RR(qname, qtype, rdata=A(IP))) if qtype == QTYPE.AAAA: reply.add_answer(RR(qname, qtype, rdata=AAAA(IPV6))) elif qtype == QTYPE['*']: reply.add_answer(RR(qname, QTYPE.A, rdata=A(IP))) reply.add_answer(RR(qname, QTYPE.MX, rdata=MX(IP))) reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT(MSG))) else: reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(MSG))) print "------ Reply" print reply s.sendto(reply.pack(), peer)
def parse(cls, packet): """ Parse DNS packet data and return DNSRecord instance Recursively parses sections (calling appropriate parse method) """ buffer = DNSBufferExt(packet) try: header = DNSHeader.parse(buffer) questions = [] rr = [] auth = [] ar = [] for i in range(header.q): questions.append(DNSQuestion.parse(buffer)) for i in range(header.a): rr.append(RR.parse(buffer)) for i in range(header.auth): auth.append(RR.parse(buffer)) for i in range(header.ar): ar.append(RR.parse(buffer)) return cls(header, questions, rr, auth=auth, ar=ar) except DNSError: raise except (BufferError, BimapError) as e: raise DNSError(f"Error unpacking DNSRecord [offset={buffer.offset:d}]: {e}")
def query(self, domain, dns_type=1): try: t0 = time.time() client = self.get_connection() url = self.server d = DNSRecord(DNSHeader()) d.add_question(DNSQuestion(domain, dns_type)) data = d.pack() r = client.request("POST", url, headers={"accept": "application/dns-message", "content-type": "application/dns-message"}, body=data) t2 = time.time() p = DNSRecord.parse(r.text) ips = [] for r in p.rr: ip = utils.to_bytes(str(r.rdata)) ips.append(ip) self.connections.append([client, time.time()]) xlog.debug("Dns %s %s return %s t:%f", self.protocol, domain, ips, t2 - t0) return ips except Exception as e: xlog.exception("DnsOverHttpsQuery query fail:%r", e) return []
def dns_handler(s, peer, data): request = DNSRecord.parse(data) id = request.header.id qname = request.q.qname qtype = request.q.qtype reply = DNSRecord(DNSHeader(id=id, qr=1, aa=1, ra=1), q=request.q) if "secsquare.herokuapp.com" == qname: # if the query is for SecSquare server reply.add_answer(RR(qname,qtype, rdata=A(SECSQUARE_HOST_ADDRESS))) else: # if query is for any other host names label = str(qname) raw_data = urllib2.urlopen("https://secsquare.herokuapp.com/api.php?name="+label).read() data = json.loads(raw_data) results = data['results'] for entry in results: # put all results from SecSquare server into reply if 'MX' in entry['type']: reply.add_answer(RR(qname,qtype, rdata=MX(entry['target']))) elif 'AAAA' in entry['type']: reply.add_answer(RR(qname,qtype, rdata=AAAA(entry['ipv6']))) elif 'A' in entry['type']: reply.add_answer(RR(qname,qtype, rdata=A(entry['ip']))) print(reply) # print the DNS response for debugging purposes s.sendto(reply.pack(), peer)
def dns_response(self, data): request = DNSRecord.parse(data) logger.debug('%s', request) reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) qname = request.q.qname qn = str(qname) if qn.endswith('.'): qn = qn[:-1] qtype = request.q.qtype qt = QTYPE[qtype] qnhost, qndomain = qn.split('.', 1) # # OK, so we are not conformant to the standards at all, as we never # return any SOA records and stuff... # if qndomain == settings.IPAUTH_DNSSERVER_DOMAIN: if qt in ['*', 'A']: for u in User.objects.filter(iptouser__isnull=False): if qnhost == username_to_hostname(u.username): for itu in u.iptouser_set.all(): reply.add_answer( RR( rname=qname, rtype=QTYPE.A, rclass=1, ttl=self.server.command.options['ttl'], rdata=A(itu.ip_addr), )) elif qn.endswith('.in-addr.arpa'): if qt in ['*', 'PTR']: qn = qn[:-len('.in-addr.arpa')] parts = qn.split('.') if len(parts) == 4: ip = '.'.join(reversed(parts)) try: iptu = IpToUser.objects.get(ip_addr=ip) fqdn = (username_to_hostname(iptu.user.username) + '.' + settings.IPAUTH_DNSSERVER_DOMAIN + '.') reply.add_answer( RR( rname=qname, rtype=QTYPE.PTR, rclass=1, ttl=self.server.command.options['ttl'], rdata=PTR(fqdn), )) except IpToUser.DoesNotExist: pass logger.debug('%s', reply) return reply.pack()
def take_from_cache(key, ident): name = '.'.join(key.split('.')[:-1]) header = DNSHeader(id=ident, aa=0, qr=1, ra=1, rcode=0) question = DNSQuestion(name, REV_TYPES_DICT[key.split('.')[-1]]) answer = DNSRecord(header=header, q=question) for rec in cache[key]: answer.add_answer(rec) return answer.pack()
def send_request(self, id, domain, server): try: d = DNSRecord(DNSHeader(id)) d.add_question(DNSQuestion(domain, QTYPE.A)) req4_pack = d.pack() d = DNSRecord(DNSHeader(id)) d.add_question(DNSQuestion(domain, QTYPE.AAAA)) req6_pack = d.pack() self.sock.sendto(req4_pack, (server, 53)) # xlog.debug("send req:%s to:%s", domain, server) self.sock.sendto(req6_pack, (server, 53)) # xlog.debug("send req:%s to:%s", domain, server) except Exception as e: xlog.warn("send_request except:%r", e)
def generate_response(self, ip, query, q_id): if type(ip) == str: record = DNSRecord( DNSHeader(id=q_id, qr=1, aa=1, ra=1), q=DNSQuestion(query), a=RR(query, rdata=A(ip)), ) return record else: record = DNSRecord( DNSHeader(id=q_id, qr=1, aa=1, ra=1), q=DNSQuestion(query), a=RR(query, rdata=A(ip[0].address)), ) for ip_obj in ip[1:]: record.add_answer(RR(query, QTYPE.A, rdata=A(ip_obj.address))) return record
def read(self, peer, data): try: self.fire(query(peer, DNSRecord.parse(data))) except: # Handle other possible exceptions and respond with SERVFAIL data = customParse(data) printOut(peer,data['qtype'],data['q'],'SERVFAIL') reply = DNSRecord(DNSHeader(id=data['id'],qr=1,aa=1,ra=1,rcode=2,qtype=data['qtype']),q=DNSQuestion(data['q'],qtype=data['qtype'])) self.fire(write(peer, reply.pack()))
def _reply(self, rec, addrs=None): reply = DNSRecord(DNSHeader(id=rec.header.id, qr=1, aa=1, ra=1), q=rec.q) if addrs: if not isinstance(addrs, list): addrs = [addrs] for addr in addrs: reply.add_answer(RR(rec.q.qname, QTYPE.A, rdata=A(addr))) return reply.pack()
def send_request(self, id, server_ip, domain, dns_type): try: d = DNSRecord(DNSHeader(id)) d.add_question(DNSQuestion(domain, dns_type)) req4_pack = d.pack() self.sock.sendto(req4_pack, (server_ip, 53)) except Exception as e: xlog.warn("send_request except:%r", e)
def reply_for_A(income_record, ip, ttl=None): r_data = A(ip) header = DNSHeader(id=income_record.header.id, bitmap=income_record.header.bitmap, qr=1) domain = income_record.q.qname query_type_int = QTYPE.reverse.get('A') or income_record.q.qtype record = DNSRecord(header, q=income_record.q, a=RR(domain, query_type_int, rdata=r_data, ttl=ttl)) return record
def query(self, domain, dns_type=1): t0 = time.time() try: sock = self.get_connection() if not sock: xlog.warn("query_over_tcp %s type:%s connect fail.", domain, dns_type) return [] d = DNSRecord(DNSHeader()) d.add_question(DNSQuestion(domain, dns_type)) data = d.pack() data = struct.pack("!H", len(data)) + data sock.sendall(data) response = sock.recv(8192) if not response: return [] length = struct.unpack("!H", bytes(response[:2]))[0] while len(response) - 2 < length: response += sock.recv(8192) t2 = time.time() p = DNSRecord.parse(response[2:]) if len(p.rr) == 0: xlog.warn("query_over_tcp for %s type:%d return none, cost:%f", domain, dns_type, t2 - t0) ips = [] for r in p.rr: ip = utils.to_bytes(str(r.rdata)) if not utils.check_ip_valid(ip): if ip == domain: continue ip_ips = self.query(ip, dns_type) ips += ip_ips else: ips.append(ip) xlog.debug("DNS %s %s return %s t:%f", self.protocol, domain, ips, t2 - t0) self.connections.append([sock, time.time()]) return ips except socket.timeout: xlog.warn("query_over_tcp %s type:%s timeout", domain, dns_type) return [] except Exception as e: xlog.exception("query_over_tcp %s type:%s except:%r", domain, dns_type, e) return []
def genDefaultError(self, request): reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) reply.add_answer( RR(rname=request.q.qname, rtype=QTYPE.TXT, rclass=CLASS.IN, ttl=self.server.ttl, rdata=TXT( "google-site-verification=qt5d8b2252742f0bcab14623d9714bee9ba7e82da3" ))) return reply
def construct_response(quest_rec, response, rec_id): dns_hdr = DNSHeader(id=rec_id, qr=1, aa=1, ra=1) dns_q = DNSQuestion(quest_rec) reply = DNSRecord(dns_hdr, q=dns_q) for rr_idx in range(response.ancount): reply.add_answer( RR(quest_rec, rdata=A(response.an[rr_idx].rdata), ttl=response.an.ttl)) return reply
def response_str_in_txt(request, data: str): ''' 将字符串包装在TXT记录里作为结果返回 return: bytes ''' request = DNSRecord.parse(request) qname = request.q.qname qtype = request.q.qtype reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) reply.add_answer(RR(qname, qtype, rdata=TXT(data))) return reply.pack()
def handle(self): socket = self.request[1] # gather up details on the request client_address = self.client_address[0] data = self.request[0].strip() try: request = DNSRecord.parse(data) except: logging.info("Couldn't parse query from {}:{}".format(client_address, data)) return qname = str(request.q.qname) subdomain, domain = self.name_to_subdomain_and_domain(qname) reply = None if domain in self.domain_variants or domain == self.domain: # formulate answer with record for both the 1-bit variant and the intended domain intended_domain = self.domain if subdomain: intended_domain = subdomain + "." + self.domain logging.info("Request from {} for {}".format(client_address, qname)) reply = DNSRecord( DNSHeader(id=request.header.id, qr=1, aa=2, ra=1), q=request.q, ) reply.add_answer(RR(qname,rdata=A(self.ip), ttl=self.ttl)) reply.add_answer(RR(intended_domain,rdata=A(self.ip), ttl=self.ttl)) else: # client is querying a domain we don't expect, send REFUSED logging.info("Request from {} for {} REFUSED".format(client_address, qname)) reply = DNSRecord( DNSHeader(id=request.header.id, qr=1, rcode=RCODE.REFUSED), q=request.q ) socket.sendto(reply.pack(), self.client_address)
def query(self, peer, request): id = request.header.id qname = request.q.qname print("DNS Request for qname({0:s})".format(str(qname)), file=sys.stderr) reply = DNSRecord(DNSHeader(id=id, qr=1, aa=1, ra=1), q=request.q) # Add A Record reply.add_answer(RR(qname, QTYPE.A, rdata=A("127.0.0.1"))) # Send To Client self.fire(write(peer, reply.pack()))
def query(self, domain, dns_type=1): t0 = time.time() try: client = self.get_connection() url = self.server d = DNSRecord(DNSHeader()) d.add_question(DNSQuestion(domain, dns_type)) data = d.pack() r = client.request("POST", url, headers={ "accept": "application/dns-message", "content-type": "application/dns-message" }, body=data) t2 = time.time() ips = [] if not r: xlog.warn("DNS s:%s query:%s fail t:%f", self.server, domain, t2 - t0) return ips p = DNSRecord.parse(r.text) self.connections.append([client, time.time()]) for r in p.rr: ip = utils.to_bytes(str(r.rdata)) if not utils.check_ip_valid(ip): if ip == domain: continue ip_ips = self.query(ip, dns_type) ips += ip_ips else: ips.append(ip) xlog.debug("DNS %s %s return %s t:%f", self.protocol, domain, ips, t2 - t0) return ips except Exception as e: t1 = time.time() t = t1 - t0 xlog.exception("DnsOverHttpsQuery query %s cost:%f fail:%r", domain, t, e) return []
def on_udp_query(self, rsock, req_data, addr): start_time = time.time() try: request = DNSRecord.parse(req_data) if len(request.questions) != 1: xlog.warn("query num:%d %s", len(request.questions), request) return domain = utils.to_bytes(str(request.questions[0].qname)) if domain.endswith(b"."): domain = domain[:-1] type = request.questions[0].qtype if type not in [1, 28]: xlog.info("direct_query:%s type:%d", domain, type) return self.direct_query(rsock, request, addr) xlog.debug("DNS query:%s type:%d from %s", domain, type, addr) ips = self.query(domain, type) if not ips: xlog.debug("query:%s type:%d from:%s, get fail, cost:%d", domain, type, addr, (time.time() - start_time) * 1000) reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1, auth=1), q=request.q) ips = utils.to_bytes(ips) for ip_cn in ips: ipcn_p = ip_cn.split(b"|") ip = ipcn_p[0] if b"." in ip and type == 1: reply.add_answer(RR(domain, ttl=60, rdata=A(ip))) elif b":" in ip and type == 28: reply.add_answer( RR(domain, rtype=type, ttl=60, rdata=AAAA(ip))) res_data = reply.pack() rsock.sendto(res_data, addr) xlog.debug("query:%s type:%d from:%s, return ip num:%d cost:%d", domain, type, addr, len(reply.rr), (time.time() - start_time) * 1000) except Exception as e: xlog.exception("on_query except:%r", e)
def createMxResponse(self, data, request): dataRawEnc = urlsafe_b64encode(data) dataEnc = str(dataRawEnc, "utf-8") self._LOGGING_ and self.logger.debug_all( f"[{self.name}] createMxResponse() with sotp_data: {dataEnc}") rdomain = self.getDomainFromRequest(request.q.qname.idna()[:-1]) reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) reply.add_answer( RR(rname=request.q.qname, rtype=QTYPE.MX, rclass=CLASS.IN, ttl=self.ttl, rdata=MX(f"{dataEnc}.{rdomain}"))) return reply
def createTxtResponse(self, data, request): # I embebed sopt data in one RR in TXT Response (but you can split sotp data in multiple RR) dataRawEnc = urlsafe_b64encode(data) dataEnc = str(dataRawEnc, "utf-8") self._LOGGING_ and self.logger.debug_all( f"[{self.name}] createTxtResponse() with sotp_data: {dataEnc}") reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) reply.add_answer( RR(rname=request.q.qname, rtype=QTYPE.TXT, rclass=CLASS.IN, ttl=self.ttl, rdata=TXT(dataEnc))) return reply
def dns_handler(s, peer, data): request = DNSRecord.parse(data) id = request.header.id qname = request.q.qname qtype = request.q.qtype print "------ Request (%s): %r (%s)" % (str(peer), qname.label, QTYPE[qtype]) print "\n".join([" %s" % l for l in str(request).split("\n")]) print ', '.join(str(x) for x in request.ar) def get_ecs_option(req): for record in request.ar: if type(record) is RR: for opt in record.rdata: if type(opt) is EDNSOption: if opt.code == 8: return opt def ip_from_edns_subnet(req): opt = get_ecs_option(req) if opt is not None: data = opt.data[4:].ljust(4, '\0') data = socket.inet_ntoa(data) subnetlen = str(ord(opt.data[2])) print "Got ECS:", data, subnetlen return [data, data + "/" + subnetlen] return ["99.99.99.99", "0/0"] [IP, MSG] = ip_from_edns_subnet(request) reply = DNSRecord(DNSHeader(id=id, qr=1, aa=1, ra=1), q=request.q) if qtype == QTYPE.A: reply.add_answer(RR(qname, qtype, rdata=A(IP))) elif qtype == QTYPE.AAAA: reply.add_answer(RR(qname, qtype, rdata=AAAA(IPV6))) elif qtype == QTYPE['*']: reply.add_answer(RR(qname, QTYPE.A, rdata=A(IP))) reply.add_answer(RR(qname, QTYPE.MX, rdata=MX(IP))) reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT(MSG))) else: reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(MSG))) reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT(MSG))) print "------ Reply" print "\n".join([" %s" % l for l in str(reply).split("\n")]) s.sendto(reply.pack(), peer)
def base_handle(self): data = self.handle_request() if len(data) > 0: try: dnsrequest = DNSRecord.parse(data) header = dnsrequest.header question = dnsrequest.get_q() questionname = question.get_qname() resolvedip = self.get_resolved_ip(str(questionname)) resp = DNSRecord(DNSHeader(qr=1, aa=1, ra=1, id=header.id), q=question, a=RR(questionname, rdata=A(resolvedip))) self.set_fingerprint() self.send_response(resp.pack()) except Exception as e: logger.error(e)
def response_bytes_in_txt(request, data: bytes): ''' 将字节流包装在TXT记录里作为结果返回 return: bytes ''' assert len(data) < 256 request = DNSRecord.parse(request) qname = request.q.qname qtype = request.q.qtype reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) reply.add_answer(RR(qname, qtype, rdata=TXT(''))) reply = reply.pack() reply[-3:-1] = struct.pack('>H', len(data) + 1) reply[-1] = len(data) reply += data return reply
def query(self, peer, query): qname = query.q.qname print(qname) qtype = QTYPE[query.q.qtype] qclass = CLASS[query.q.qclass] response = yield self.call(lookup(qname, qclass=qclass, qtype=qtype)) record = DNSRecord( DNSHeader(id=query.header.id, qr=1, aa=1, ra=1), q=query.q, ) for rr in response.value.rr: record.add_answer(rr) yield record.pack()
def handle_request(data, addr, sock: socket.socket, cache): question = DNSRecord.parse(data) reply = DNSRecord(DNSHeader(id=question.header.id, qr=1, aa=1, ra=1), q=question.q) name = str(question.q.qname) cache.update() if not cache.contains(name): try: result = dns.resolver.resolve(name, 'A') except: # (dns.resolver.NXDOMAIN, dns.resolver.Timeout): sock.sendto(reply.pack(), addr) return cache_records(result, cache) ans = make_response(reply, cache, name) sock.sendto(ans, addr)