def cloak(self, request, handler): domain = GetDomainNameFromRequest(request) for rule in self.cloakrules: if rule.search(domain): reply = request.reply() # Return an A record if target is IPV4 address if rule.targetIsIp4(): reply.add_answer( RR(request.questions[0].qname, rtype=QTYPE.A, rdata=dnslib.A(rule.target), ttl=self.DEFAULT_TTL)) return reply # Add a CNAME if the target domain is different if domain != rule.target: reply.add_answer( RR(request.questions[0].qname, rtype=QTYPE.CNAME, rdata=dnslib.CNAME(rule.target), ttl=self.DEFAULT_TTL)) subquery = DNSRecord.question(rule.target) subresp = self.upstream_resolve(subquery, handler) for record in subresp.rr: reply.add_answer(record) return reply
def pack_dns(dns, answers, soa=None): def content_type(x): # valid ip if socket.inet_aton(x): return 'A' else: return 'CNAME' if answers: for ans in answers: # logger.info('ans ' + ans) if content_type(ans[1]) == 'A': dns.add_answer( dnslib.RR(ans[0], dnslib.QTYPE.A, rdata=dnslib.A(ans[1]))) elif content_type(ans[1]) == 'CNAME': dns.add_answer( dnslib.RR(ans[0], dnslib.QTYPE.CNAME, rdata=dnslib.CNAME(ans[1]))) elif soa: soa_content = soa[1].split() dns.add_auth( dnslib.RR(soa[0], dnslib.QTYPE.SOA, rdata=dnslib.SOA(soa_content[0], soa_content[1], (int(i) for i in soa_content[2:])))) return dns
def pack_dns(self, dns, answers, soa=None): content_type = lambda x: 'A' if re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', x) else 'CNAME' if answers: for ans in answers: if content_type(ans[1]) == 'A': dns.add_answer(dnslib.RR(ans[0], dnslib.QTYPE.A, rdata=dnslib.A(ans[1]))) elif content_type(ans[1]) == 'CNAME': dns.add_answer(dnslib.RR(ans[0], dnslib.QTYPE.CNAME, rdata=dnslib.CNAME(ans[1]))) elif soa: soa_content = soa[1].split() dns.add_auth(dnslib.RR(soa[0], dnslib.QTYPE.SOA, rdata=dnslib.SOA(soa_content[0], soa_content[1], (int(i) for i in soa_content[2:])))) return dns
def test_on_upstream_response_not_A(greendns): qname = "www.microsoft.com" qresult = "www.microsoft.com-c-2.edgekey.net." s = init_greendns_session(greendns, qname, dnslib.QTYPE.CNAME) res = dnslib.DNSRecord(dnslib.DNSHeader(qr=1, aa=1, ra=1), q=dnslib.DNSQuestion(qname), a=dnslib.RR(qname, rtype=dnslib.QTYPE.CNAME, rdata=dnslib.CNAME(qresult), ttl=3)) s.server_resps[local_dns1] = bytes(res.pack()) resp = greendns.on_upstream_response(s, local_dns1) assert resp d = dnslib.DNSRecord.parse(resp) assert str(d.rr[0].rdata) == qresult
def _cname_search(record, rr_list, auth_list, addi_list): """ Searches and adds any CNAME records for the domain. :param record: Overall record for domain :param rr_list: Current record list for the domain :param auth_list: Authority list for the domain :param addi_list: Additional list for the domain """ try: cname_record = record["CNAME"] ttl = int(cname_record["ttl"]) rr_list.append(dnslib.RR(rname = record["domain"], rtype = dnslib.QTYPE.CNAME, rdata = dnslib.CNAME(label = cname_record["domain"]), ttl = ttl)) _add_authority(record["domain"], auth_list) _add_additional(addi_list) except: pass
def test_shuffer_A(greendns): qname = "qq.com" id = 1024 s = init_greendns_session(greendns, qname, dnslib.QTYPE.A, id) res = dnslib.DNSRecord(dnslib.DNSHeader(qr=1, aa=1, ra=1), q=dnslib.DNSQuestion(qname), a=dnslib.RR(qname, dnslib.QTYPE.CNAME, rdata=dnslib.CNAME("https.qq.com"), ttl=3)) res.add_answer(dnslib.RR(qname, rdata=dnslib.A("101.226.103.106"), ttl=3)) res.add_answer(dnslib.RR(qname, rdata=dnslib.A("101.226.103.107"), ttl=3)) greendns.cache.add(("qq.com.", 1), res, 3) d = None for i in range(10): is_continue, raw_resp = greendns.on_client_request(s) assert not is_continue assert raw_resp d = dnslib.DNSRecord.parse(raw_resp) if str(d.rr[1].rdata) == "101.226.103.107": break assert d.rr[0].rtype == dnslib.QTYPE.CNAME assert str(d.rr[1].rdata) == "101.226.103.107"
def py3dns(serverip='', serverport=0): class DNSify(str): def __getattr__(self, item): return DNSify(item + '.' + self) def get_lan_ip4(forcelocalhost=False): if forcelocalhost: return '127.0.0.1' s = dnslib.socket.socket(dnslib.socket.AF_INET, dnslib.socket.SOCK_DGRAM) try: s.connect(('10.255.255.255', 0)) IP = s.getsockname()[0] except: IP = '127.0.0.1' finally: s.close() return IP def is_domain_blacklisted(sqname, domain_blacklist): for test_domain in domain_blacklist: if test_domain in sqname: return True return False # Init server settings killcommand = 'stop.py3dns.now' serve_forever = True udp_buffer_size = 1024 server_name = dnslib.socket.getfqdn() server_ip = get_lan_ip4() if serverip: server_ip = serverip reverse_server_ip = '.'.join(reversed( server_ip.split('.'))) + '.in-addr.arpa' server_port = 53 if serverport: server_port = int(serverport) server_protocol = 'UDP' public_dns_resolvers = [ '91.239.100.100', '89.233.43.71', '8.8.8.8', '8.8.4.4' ] # Init simple host cache host_cache = {} host_cache[server_ip] = server_name # Init blacklists use_blacklists = True domain_blacklist = make_domain_blacklist() ipaddr_blacklist = make_ipaddr_blacklist() rpz_domain = DNSify('getthefuckaway.net') rpz_ip4 = '10.20.30.40' rpz_ip6 = '10:20:30:40:50:60:70:80' # Init UDP socket server udpsrv = dnslib.socket.socket(dnslib.socket.AF_INET, dnslib.socket.SOCK_DGRAM) udpsrv.bind((server_ip, server_port)) udpsrv.setblocking(False) # Init external resolvers external_resolver = dns.resolver.Resolver() external_resolver_cache = dns.resolver.Cache(cleaning_interval=600.0) external_resolver.cache = external_resolver_cache external_resolver.nameservers = public_dns_resolvers external_resolver.retry_servfail = False external_resolver.port = 53 ##external_resolver.timeout = 1.0 ##external_resolver.lifetime = 2.0 # Timestamp UTC now = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f') # App Header print('+' + ('-' * 71)) print('| PY3DNS v/1.0 by BikerDroid') print('+' + ('-' * 71)) print('| Server :', server_name) print('| Address :', server_ip) print('| Port Used :', server_port) print('| Protocol :', server_protocol) print('| Recv Size :', udp_buffer_size, 'bytes') print('| Solvers :', str(public_dns_resolvers).strip("[]").replace("'", "")) print('+' + ('-' * 71)) print('| Stop PY3DNS by sending "nslookup ' + killcommand + ' ' + server_ip + '"') print('+' + ('-' * 71)) print(now, ': Ready to serve...') # Main Loop while serve_forever: # Clear vars sres = sip4 = sip6 = smx = scname = sns = stxt = sptr = ssoa = sany = hostip = '' # Main: Get client request, add hostip and host name to host_cache try: data, addr = udpsrv.recvfrom(udp_buffer_size) hostip = str(addr[0]) if not hostip in host_cache: host_cache[hostip] = dnslib.socket.getfqdn( str(dnslib.socket.gethostbyaddr(hostip)[0])) now = datetime.datetime.utcnow().strftime( '%Y-%m-%d %H:%M:%S.%f') # UTC except: continue # Client request -> id, qname (domain), qtype (A,AAAA,MX etc) request = dnslib.DNSRecord.parse(data) qid = request.header.id qname = request.q.qname qtype = request.q.qtype slabel = str(qname.label) sqname = str(qname) sqtype = str(dnslib.QTYPE[qtype]) # Rem this line if rpz_domain is to overwrite # blacklisted domains with their real names. sdomain = sqname # Dirty trick to shut down server from commandline: # Syntax : nslookup stop.dns.srv <server_ip> # Example: nslookup stop.dns.srv 127.0.0.1 # Must be before external_resolver.query() if sqname.rstrip('.') == killcommand: serve_forever = False continue # Dirty Reverse Lookup of local server # Allows requesting client to get server_name if qtype == dnslib.QTYPE.PTR: if sqname.rstrip('.') == reverse_server_ip: reply = dnslib.DNSRecord(header=dnslib.DNSHeader(id=qid, qr=1, aa=1, ra=1, rcode=0), q=dnslib.DNSQuestion(sqname, qtype)).reply() reply.add_answer( dnslib.RR(sqname, dnslib.QTYPE.PTR, rdata=dnslib.PTR(server_name), ttl=3600)) udpsrv.sendto(reply.pack(), addr) print(now, ':', sqname, '|', sqtype, '=', qtype, '|', server_name, '|', hostip, '=', host_cache[hostip]) continue # Get DNS record from public_dns_resolvers. # This section can be changed to serve from # own database, stationary files or similar. try: if not sqname.rstrip('.') in domain_blacklist: external_resolver_result = external_resolver.query( sqname.rstrip('.'), sqtype) found = True except: print(now, '> qtype', sqtype, '(' + str(qtype) + ') was not found for', sqname) external_resolver_result = [] sres = sip4 = sip6 = '' found = False if not found: # Create DNSRecord Header reply: rcode = 5 (Query Refused). See RFC2136 for rcode's. reply = dnslib.DNSRecord(header=dnslib.DNSHeader(id=qid, qr=1, aa=1, ra=1, rcode=5), q=dnslib.DNSQuestion(sdomain, qtype)).reply() else: # Create DNSRecord Header reply: rcode = 0 (No Error) reply = dnslib.DNSRecord(header=dnslib.DNSHeader(id=qid, qr=1, aa=1, ra=1, rcode=0), q=dnslib.DNSQuestion(sdomain, qtype)).reply() # Add A record answer for domain and IP # Filter blacklisted IP4/6 addresses. if qtype == dnslib.QTYPE.A: if is_domain_blacklisted( sqname.rstrip('.'), domain_blacklist): # Simple domain blacklist check sres = sip4 = rpz_ip4 reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.A, rdata=dnslib.A(sip4), ttl=60)) else: for data in external_resolver_result: sres = sip4 = str(data).strip() if sip4: if sip4 in ipaddr_blacklist: # Simple IP (4+6) blacklist check sres = sip4 = rpz_ip4 reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.A, rdata=dnslib.A(sip4), ttl=60)) # Add AAAA record answer for domain and IP # Filter blacklisted IP4/6 addresses. elif qtype == dnslib.QTYPE.AAAA: if is_domain_blacklisted( sqname.rstrip('.'), domain_blacklist): # Simple domain blacklist check sres = sip6 = rpz_ip6 reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.AAAA, rdata=dnslib.AAAA(sip6), ttl=60)) else: for data in external_resolver_result: sres = sip6 = str(data).strip() if sip6: if sip6 in ipaddr_blacklist: # Simple IP (4+6) blacklist check sres = sip6 = rpz_ip6 reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.AAAA, rdata=dnslib.AAAA(sip6), ttl=60)) # Add NS record answer for domain elif qtype == dnslib.QTYPE.NS: for data in external_resolver_result: sres = sns = str(data).strip() if sns: reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.NS, rdata=dnslib.NS(sns), ttl=60)) # Add MX record answer for domain and IP elif qtype == dnslib.QTYPE.MX: for data in external_resolver_result: sres = smx = str(data).strip() if smx: reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.MX, rdata=dnslib.MX(smx), ttl=60)) # Add CNAME record answer for domain elif qtype == dnslib.QTYPE.CNAME: for data in external_resolver_result: sres = scname = str(data).strip() if scname: reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.CNAME, rdata=dnslib.CNAME(scname), ttl=60)) # Add TXT record answer for domain elif qtype == dnslib.QTYPE.TXT: for data in external_resolver_result: sres = stxt = str(data).strip() if stxt: reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.TXT, rdata=dnslib.TXT(stxt), ttl=60)) # Add PTR record answer for domain elif qtype == dnslib.QTYPE.PTR: for data in external_resolver_result: sres = sptr = str(data).strip() if sptr: reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.PTR, rdata=dnslib.PTR(sptr), ttl=60)) # Add ANY record answer for domain elif qtype == dnslib.QTYPE.ANY: for data in external_resolver_result: sres = sany = str(data).strip() if sany: reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.ANY, rdata=dnslib.ANY(sany), ttl=60)) # Add SOA record answer for domain elif qtype == dnslib.QTYPE.SOA: for data in external_resolver_result: if str(data).strip(): soa_data = str(data).strip().split(' ') soa_mname = soa_data[0] soa_rname = soa_data[1] soa_serial = soa_data[2] soa_refresh = soa_data[3] soa_retry = soa_data[4] soa_expire = soa_data[5] soa_minimum = soa_data[6] soa_time = (int(soa_serial), int(soa_refresh), int(soa_retry), int(soa_expire), int(soa_minimum)) sres = soa_mname + ',' + soa_rname + ',' + soa_serial + ',' + soa_refresh + ',' + soa_retry + ',' + soa_expire + ',' + soa_minimum reply.add_answer( dnslib.RR(sdomain, dnslib.QTYPE.SOA, rdata=dnslib.SOA(soa_mname, soa_rname, soa_time), ttl=60)) else: # Unknown qtype - add CNAME as answer :) reply.add_answer( dnslib.RR(qname, dnslib.QTYPE.CNAME, rdata=dnslib.CNAME(server_name), ttl=60)) # Send DNS reply to client address using UDP udpsrv.sendto(reply.pack(), addr) # Show status in console print(now, ':', sqname, '|', sqtype, '=', qtype, '|', sres, '|', hostip, '=', host_cache[hostip]) #,reply.pack() # Shutting down print(now, ': Shutting down...') udpsrv.shutdown(0) print(now, ': Done.')
def parse_server_config(server_config): import copy _server_config = copy.deepcopy(server_config) dns_servers = _server_config.get('dns_servers', []) for server in dns_servers: # parse url scheme, hostname, port = DNSServerLoader.parse_url(server['url']) server['scheme'] = scheme server['hostname'] = hostname server['port'] = port # parse rules for rule in server['rules']: rtype = rule['type'] if rule['domain-type'] in ('FQDN', 'PREFIX', 'SUFFIX'): rule['domain'] = DNSLabel(rule['domain']) if rtype == 'FORWARD': _scheme, _hostname, _port = DNSServerLoader.parse_url(rule['value']) rule['pvalue'] = { 'scheme': _scheme, 'hostname': _hostname, 'port': _port, 'url': _scheme + '://' + _hostname + ':' + str(_port) } elif rtype in ('A', 'CNAME', 'MX', 'NS', 'PTR', 'AAAA', 'SRV', 'SOA'): if isinstance(rule['value'], six.string_types): rule['value'] = [rule['value']] if rtype == 'A': rule['pvalue'] = [dnslib.A(item) for item in rule['value']] elif rtype == 'CNAME': rule['pvalue'] = [dnslib.CNAME(DNSLabel(item)) for item in rule['value']] elif rtype == 'MX': rule['pvalue'] = [dnslib.MX(DNSLabel(item)) for item in rule['value']] elif rtype == 'NS': rule['pvalue'] = [dnslib.NS(DNSLabel(item)) for item in rule['value']] elif rtype == 'PTR': rule['pvalue'] = [dnslib.PTR(DNSLabel(item)) for item in rule['value']] elif rtype == 'AAAA': rule['pvalue'] = [dnslib.AAAA(item) for item in rule['value']] elif rtype == 'SRV': srv_arr = [] for item in rule['value']: item_arr = item.split(' ') srv_arr.append(dnslib.SRV( priority=num.safe_int(item_arr[0]), weight=num.safe_int(item_arr[1]), port=num.safe_int(item_arr[2]), target=item_arr[3])) rule['pvalue'] = srv_arr elif rtype == 'SOA': soa_arr = [] for item in rule['value']: item_arr = item.split(' ') soa_arr.append(dnslib.SOA( mname=DNSLabel(item_arr[0]), rname=DNSLabel(item_arr[1]), times=(num.safe_int(t) for t in item_arr[2:]) # serial, refresh, retry, expire, minimun )) rule['pvalue'] = soa_arr return dns_servers
def dns_resolve(self, query, tcp): """ Resolves DNS request from one client at the time. Encodes its actual data and forms DNS response from it. """ request = dns.DNSRecord.parse(query) reply = request.reply() domain = request.q.get_qname() qtype = request.q.qtype data = exf.domain_decode(str(domain), base64.urlsafe_b64decode) # If encryption key is present - decode it for futher data decryption if (len(request.questions) > 1): enc_domain = str(request.questions[1].get_qname()) enc_key = exf.domain_decode(enc_domain, base64.urlsafe_b64decode) # Descramble key enc_key = exf.scramble(enc_key, (4, 12), True) # Check if the key is scramble offset if len(enc_key) < 3: enc_key = tuple(enc_key) data = exf.scramble(data, enc_key, True) # Or AES decryption key else: enc_key = enc_key.decode() data = exf.aes_decrypt(data, enc_key) # reply.add_question(request.questions[1]) if DEBUG: print_with_time("***", f"DNS QTYPE is {qtype}") print_with_time("***", f"Original data length {len(data)} bytes") print_with_time("***", f"{data[:24]}...") data = base64.b64encode(data) core_domain = deepcopy(domain) # Get TLD domain from original object core_domain.label = domain.label[-2:] if (qtype == dns.QTYPE.A): data = exf.ip_encode(data, False) elif (qtype == dns.QTYPE.AAAA): data = exf.ip_encode(data, True) elif (qtype == dns.QTYPE.TXT): data = [dns.TXT(data)] elif (qtype == 10): # NULL type data = [dns.RD(data)] else: data = exf.domain_encode(data, str(core_domain), base64.urlsafe_b64encode) if (qtype == dns.QTYPE.CNAME): data = [dns.CNAME(data)] elif (qtype == dns.QTYPE.MX): data = [dns.MX(data)] elif (qtype == dns.QTYPE.NS): data = [dns.NS(data)] for rd in data: reply.add_answer(dns.RR(str(domain), rtype=qtype, rdata=rd)) raw_reply = reply.pack() # Truncate large (> 512 bytes) data for UDP payload if (len(raw_reply) > exf.MAX_DNS_LEN and not tcp): print_with_time("DNS", f"Response message is big! Truncate it...") reply.header.set_tc(1) raw_reply = reply.pack()[:exf.MAX_DNS_LEN] print_with_time("DNS", f"Sending back the request in size {len(raw_reply)} bytes\n") return raw_reply