def resolve(self, request, handler): assert isinstance(request, DNSRecord) reply = request.reply() qname = request.q.qname D = self.D if request.q.qtype == QTYPE.NS and qname.matchSuffix(D): reply.add_answer( RR(rname=qname, rtype=QTYPE.NS, ttl=NS_TTL, rdata=NS(D.add('ns')))) reply.add_ar( RR(D.add('ns'), QTYPE.A, ttl=self.ttl, rdata=A(self.ip))) # valid queries: data.data...data.9kw_api_key.operation.rest of domain elif request.q.qtype == QTYPE.CNAME\ and len(qname.label) >= (len(D.label) + 2)\ and qname.label[-(len(D.label) + 1)] in self.OPERATIONS\ and qname.label[-(len(D.label) + 2)] in self.allowed_keys: operation = qname.label[-(len(D.label) + 1)] api_key = qname.label[-(len(D.label) + 2)] data = qname.label[:-(len(D.label) + 2)] ret = self.OPERATIONS[operation](api_key, data) reply.add_answer(RR(qname, QTYPE.CNAME, ttl=0, rdata=CNAME(ret))) return reply
def craftPayload(self, msg): query = DNSRecord.parse(msg) response = query.reply() qname = query.q.qname vic_name = self.vic_name fakeip = self.fakeip if query.q.qtype == QTYPE.NS: print("[+] required NS record.") assert_ns = RR(qname, QTYPE.NS, ttl=60, rdata=NS("ns.%s" % qname)) response.add_answer(assert_ns) elif query.q.qtype == QTYPE.A: print("[+] required any A record.") cnamerr = RR(qname, QTYPE.CNAME, ttl=60, rdata=CNAME(vic_name)) fakea = RR(vic_name, QTYPE.A, ttl=86400, rdata=A(fakeip)) response.add_answer(cnamerr) response.add_answer(fakea) if response.__class__.__name__ == "DNSRecord": print("[!] Debug: print reply data") print("===============================") print(response) print("===============================") return response.pack() else: return None
def createNsResponse(self, data, request): dataRawEnc = urlsafe_b64encode(data) dataEnc = str(dataRawEnc, "utf-8") self._LOGGING_ and self.logger.debug_all( f"[{self.name}] createNSResponse() 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.NS, rclass=CLASS.IN, ttl=self.ttl, rdata=NS(f"{dataEnc}.{rdomain}"))) return reply
def RecordFactory(qtype: str, data: str, logger: Any) -> Any: if qtype == "A": return A(data) elif qtype == "CNAME": return CNAME(data) elif qtype == "AAAA": return AAAA(data) elif qtype == "NS": return NS(data) elif qtype == "MX": pref, entry = data.split() print(pref, entry) return MX(preference=int(pref), label=entry) else: logger.error("not implemented query type")
def main(): root_path = DEFAULT_ROOT_PATH config = load_config(root_path, "config.yaml", SERVICE_NAME) initialize_logging(SERVICE_NAME, config["logging"], root_path) global D global ns global TTL global soa_record global ns_records D = DomainName(config["domain_name"]) ns = DomainName(config["nameserver"]) TTL = config["ttl"] soa_record = SOA( mname=ns, # primary name server rname=config["soa"]["rname"], # email of the domain administrator times=( config["soa"]["serial_number"], config["soa"]["refresh"], config["soa"]["retry"], config["soa"]["expire"], config["soa"]["minimum"], ), ) ns_records = [NS(ns)] def signal_received(): asyncio.create_task(kill_processes()) loop = asyncio.get_event_loop() try: loop.add_signal_handler(signal.SIGINT, signal_received) loop.add_signal_handler(signal.SIGTERM, signal_received) except NotImplementedError: log.info("signal handlers unsupported") try: loop.run_until_complete(serve_dns()) finally: loop.close()
def parse_request(data): """Parsing incomming data""" request = DNSRecord.parse(data) fqdn = [x.decode() for x in request.q.qname.label] city = '.'.join(fqdn[-3:-2]) if '.'.join(fqdn[-2:]) == parse_config(): # declare id_ = request.header.id rq_ = request.q qn_ = request.q.qname qt_ = request.q.qtype # generate message msg_ = get_weather(city, record=qt_) # generate dns reponse reply = DNSRecord(DNSHeader(id=id_, qr=1, aa=1, ra=1), q=rq_) if qt_ == QTYPE.A: if ('.'.join(fqdn) == parse_config(2)[0]) or ('.'.join(fqdn) == parse_config()): ip_, ttl_ = parse_config(qt_) reply.add_answer(RR(qn_, qt_, rdata=A(ip_), ttl=ttl_)) else: for ip_ in msg_: reply.add_answer(RR(qn_, qt_, rdata=A('.'.join(ip_)))) elif qt_ == QTYPE.TXT: if len(msg_) > 255: for msg in textwrap.wrap(msg_.decode(), 255): reply.add_answer(RR(qn_, qt_, rdata=TXT(msg))) else: reply.add_answer(RR(qn_, qt_, rdata=TXT(msg_))) elif qt_ == QTYPE.NS: ns_, ttl_ = parse_config(qt_) reply.add_answer(RR(qn_, qt_, rdata=NS(ns_), ttl=ttl_)) elif qt_ == QTYPE.SRV: reply.add_answer(RR(qn_, qt_, rdata=SRV(*msg_))) return reply.pack()
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(queryType,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(queryType,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) elif queryType == QTYPE.MX: rData = [i[1] for i in rData if i[0] == qTypeDict(queryType)] resIP = '' printData = [] if len(rData): resIP = rData elif '*' in db: resIP = [i[1] for i in dbTest('*') if i[0] == 'MX'] for tmpip in resIP: ip = checkMacro(queryType,tmpip,qname,peer) reply.add_answer(RR(qname, QTYPE.MX, rdata=MX(ip))) 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: # answer to ALL (*) resIP = [i[1] for i in dbTest('*') if i[0] == qTypeDict(queryType)] for tmpip in resIP: tip = checkMacro(queryType,tmpip,qname,peer) if not isinstance(tip,list): tip = [tip] for ip in tip: # Add A Record if queryType == QTYPE.NS: reply.add_answer(RR(qname, QTYPE.NS, rdata=NS(ip))) elif queryType == QTYPE.AAAA: if isValidIP(ip): reply.add_answer(RR(qname, QTYPE.AAAA, rdata=AAAA(ip))) else: # Handle invalid IPv6, encode it in hex and send in form of IPv6 # Converting 'simpletext' -> ::7369:6d70:6c65:7465:7874 # To be used in 'file' macro print("Invalid IPv6 provided: {!r}... Answering as HEX -> IPv6".format(ip[:20])) n = 16 # if len(ip) > n: if isinstance(ip,str): ip = ip.encode() record = [longToIP(int((ip[i:i+n]).hex(),16)) for i in range(0, len(ip), n)] for i in record: reply.add_answer(RR(qname, QTYPE.AAAA, rdata=AAAA(i))) else: reply.add_answer(RR(qname, QTYPE.A, rdata=A(ip), ttl=30)) 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 resolve(self, request, handler): reply = request.reply() # IP of inbound DNS request yourip = ipaddress.ip_address(unicode(handler.client_address[0])) # Wildcards if the --host supplied is 'any', otherwise limits to predefined --host if str(self.host).lower() == "any": hostname = str(request.q.qname) else: hostname = self.host # Build standard record contents for reply # e.g. local.test. 60 IN SOA ns1.local.test. hostmaster.local.test. 1 3600 600 604800 60 soar = SOA("ns1." + hostname, "hostmaster." + hostname, (1, 3600, 600, 604800, 60)) # e.g. local.test. 60 IN NS ns1.local.test. # local.test. 60 IN NS ns2.local.test. ns1r = NS("ns1." + hostname) ns2r = NS("ns2." + hostname) """ Request construction Verify that the request is valid, which records were requested, and construct the response Uses add_answer for Answer and add_auth for Authority response sections """ # Matching hostname or --host 'any' was used if request.q.qname == hostname: # A record if QTYPE[request.q.qtype] == "A": # If A was requested but inbound is v6, return SOA with NOERROR ("no problem but nothing found") if type(yourip) == ipaddress.IPv6Address: reply.add_auth(RR( hostname, QTYPE.SOA, rdata=soar, ttl=60)) reply.header.rcode = RCODE.NOERROR # Otherwise return NS1, NS2, and A else: reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns1r, ttl=60)) reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns2r, ttl=60)) reply.add_answer(RR( hostname, QTYPE.A, rdata=A(str(yourip)), ttl=60)) # AAAA record elif QTYPE[request.q.qtype] == "AAAA": # If AAAA was requested but inbound is v4, return SOA with NOERROR ("no problem but nothing found") if type(yourip) == ipaddress.IPv4Address: reply.add_auth(RR( hostname, QTYPE.SOA, rdata=soar, ttl=60)) reply.header.rcode = RCODE.NOERROR # Otherwise return NS1, NS2, and AAAA else: reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns1r, ttl=60)) reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns2r, ttl=60)) reply.add_answer(RR( hostname, QTYPE.A, rdata=AAAA(str(yourip)), ttl=60)) # TXT record # Return NS1, NS2, and human-formatted TXT with IP address elif QTYPE[request.q.qtype] == "TXT": reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns1r, ttl=60)) reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns2r, ttl=60)) reply.add_answer(RR( hostname, QTYPE.TXT, rdata=TXT("Your IP is " + str(yourip)), ttl=60)) # NS record # Return just NS1 and NS2 elif QTYPE[request.q.qtype] == "NS": reply.add_answer(RR( hostname, QTYPE.NS, rdata=ns1r, ttl=43200)) reply.add_answer(RR( hostname, QTYPE.NS, rdata=ns2r, ttl=43200)) # SOA record # Return just SOA elif QTYPE[request.q.qtype] == "SOA": reply.add_answer(RR( hostname, QTYPE.SOA, rdata=soar, ttl=60)) # ANY record # Returns A/AAAA, TXT, NS1, NS2, and SOA elif QTYPE[request.q.qtype] == "ANY": # AAAA for IPv6 if type(yourip) == ipaddress.IPv6Address: reply.add_answer(RR( hostname, QTYPE.A, rdata=AAAA(str(yourip)), ttl=60)) # A for IPv4 else: reply.add_answer(RR( hostname, QTYPE.A, rdata=A(str(yourip)), ttl=60)) # TXT reply.add_answer(RR( hostname, QTYPE.TXT, rdata=TXT("Your IP is " + str(yourip)), ttl=60)) # NS1 reply.add_answer(RR( hostname, QTYPE.NS, rdata=ns1r, ttl=60)) # NS2 reply.add_answer(RR( hostname, QTYPE.NS, rdata=ns2r, ttl=60)) # SOA reply.add_answer(RR( hostname, QTYPE.SOA, rdata=soar, ttl=60)) # All other types are invalid, e.g. MX or CNAME; # Return only SOA and NOERROR ("no problem but nothing found") else: reply.add_auth(RR( hostname, QTYPE.SOA, rdata=soar, ttl=60)) reply.header.rcode = RCODE.NOERROR # Respond with appropriate rset for NS1.hostname.tld (A/IPv4 only) # Skipped for --host any, since we'd just use the same IP for NS1/NS2 anyway elif request.q.qname == "ns1." + hostname: # Only respond for A records, all others receive SOA and NOERROR ("no problem but nothing found") if QTYPE[request.q.qtype] == "A": reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns1r, ttl=60)) reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns2r, ttl=60)) reply.add_answer(RR( "ns1." + hostname, QTYPE.A, rdata=A(myip), ttl=60)) else: reply.add_auth(RR( hostname, QTYPE.SOA, rdata=soar, ttl=60)) reply.header.rcode = RCODE.NOERROR # NS2.hostname.tld (A only) elif request.q.qname == "ns2." + hostname: # Only respond for A records, all others receive SOA and NOERROR ("no problem but nothing found") if QTYPE[request.q.qtype] == "A": reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns1r, ttl=60)) reply.add_auth(RR( hostname, QTYPE.NS, rdata=ns2r, ttl=60)) reply.add_answer(RR( "ns2." + hostname, QTYPE.A, rdata=A(myip), ttl=60)) else: reply.add_auth(RR( hostname, QTYPE.SOA, rdata=soar, ttl=60)) reply.header.rcode = RCODE.NOERROR # Invalid host, correct domain? NXDOMAIN elif hostname in str(request.q.qname): reply.header.rcode = RCODE.NXDOMAIN reply.add_auth(RR( hostname, QTYPE.SOA, rdata=soar, ttl=60)) # Invalid domain without --host any? REFUSED else: reply.header.rcode = RCODE.REFUSED # Return constructed rrset return reply
def resolve(self, request, handler): """ @type request: DNSRecord @type handler: DNSHandler """ def nxdomain(): reply = request.reply() reply.header.rcode = RCODE.NXDOMAIN return reply def refused(): reply = request.reply() reply.header.rcode = RCODE.REFUSED return reply name = request.q.qname any_q = (request.q.qtype == QTYPE.ANY) # If they requested something other than our root domain that would be ~recursive, which we REFUSE. if not name.matchSuffix(settings.ROOT_DOMAIN): return refused() # If they requested our root domain exactly, they might want some DNS metadata stuff. if name == settings.ROOT_DOMAIN: reply = request.reply() if request.q.qtype == QTYPE.SOA or any_q: reply.add_answer( RR(request.q.qname, ttl=settings.TTL, rtype=QTYPE.SOA, rdata=SOA(mname=settings.NAMESERVERS[0], rname=settings.RNAME, times=(settings.SOA_TIMESTAMP, 300, 60, 604800, 10)))) if request.q.qtype == QTYPE.NS or any_q: for ns in settings.NAMESERVERS: reply.add_answer( RR(request.q.qname, ttl=settings.TTL, rtype=QTYPE.NS, rdata=NS(ns))) return reply name = str(name.stripSuffix(settings.ROOT_DOMAIN)) match = re.match(r'ip-(\d{1,3})-(\d{1,3})-(\d{1,3})-(\d{1,3})\.?', name) if match is None: return nxdomain() ip = tuple(map(int, match.groups())) for octet in ip: if octet > 255: return nxdomain() # Now we return an empty response for not-A, or an actual response for A. # This is because we should not NXDOMAIN for domains that exist but have no records of # the specified type, so we have to go through the motions of checking validity first. reply = request.reply() if request.q.qtype == QTYPE.A or any_q: reply.add_answer( RR(request.q.qname, ttl=settings.TTL, rdata=A('%d.%d.%d.%d' % ip))) return reply