def dig(qname, rdtype="A", timeout=5, resolvers="local", edns_size=1500, full_answers=False): """ Do a quick DNS request and avoid the "search" trap inside /etc/resolv.conf """ # It's very important to do the request with a qname ended by . # If we don't and the domain fail, dns resolver try a second request # by concatenate the qname with the end of the "hostname" if not qname.endswith("."): qname += "." if resolvers == "local": resolvers = ["127.0.0.1"] elif resolvers == "force_external": resolvers = external_resolvers() else: assert isinstance(resolvers, list) resolver = dns.resolver.Resolver(configure=False) resolver.use_edns(0, 0, edns_size) resolver.nameservers = resolvers resolver.timeout = timeout try: answers = resolver.query(qname, rdtype) except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.NoAnswer, dns.exception.Timeout) as e: return ("nok", (e.__class__.__name__, e)) if not full_answers: answers = [answer.to_text() for answer in answers] return ("ok", answers)
def getNS(domain_name): '''Returns all IPs for all NS for the given domain using the system default resolver''' resolver = dns.resolver.Resolver() resolver.use_edns(0,dns.flags.DO,4096) resolver.nameservers=([__default_ns__]) return_ns = set() try: response_ns = resolver.query(domain_name, 'NS') except dns.resolver.NoAnswer: print "no answer returned" except dns.resolver.NXDOMAIN: print "NXDOMAIN" for ns in response_ns: try: response_a = resolver.query(ns.target, 'A') except dns.resolver.NoAnswer: print "no answer returned" except dns.resolver.NXDOMAIN: print "NXDOMAIN" for a in response_a: return_ns.add(a.address) return return_ns
def ping(resolver, hostname, dnsrecord, ttl, src_ip, use_edns=False): global _ttl reached = False dns.query.socket_factory = CustomSocket _ttl = ttl if use_edns: resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO')) try: resolver.resolve(hostname, dnsrecord, source=src_ip, raise_on_no_answer=False) except dns.resolver.NoNameservers as e: if not quiet: print("no or bad response:", e) sys.exit(1) except dns.resolver.NXDOMAIN as e: if not quiet: print("Invalid hostname:", e) sys.exit(1) except dns.resolver.Timeout: pass except dns.resolver.NoAnswer: if not quiet: print("invalid answer") except SystemExit: pass except Exception as e: print("unxpected error: ", e) sys.exit(1) else: reached = True return reached
def get_name_servers(self, domain, record_type): ''' Returns a list of authoritative Name Servers for the domain 'domain', and a list of record_type records (either IPv4 or IPv6) for these NS. :param domain: String, domain name we are testing. :param record_type: String, either 'A' or 'AAAA', inticates the type of IP address we request from the NS :return: List of NameServers List of Ips ''' resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) resolver.nameservers = ([__default_ns__]) ns_names = [] ns_ips = [] try: response_ns = resolver.query(domain, 'NS') except Exception as ex: self.logger.warning("DKIM Error Resolving NS %s (%s)" % (domain, str(ex))) return None, None for ns in response_ns: try: response_a = resolver.query(ns.target, record_type) ns_names.append(ns.target.to_text()) for a in response_a: ns_ips.append(a.address) except Exception as ex: self.logger.warning( f"DKIM Resolving {record_type} {ns.target.to_text()} ({str(ex)})" ) if len(ns_names) == 0 or len(ns_ips) == 0: return None, None return ','.join(ns_names), ns_ips
def getNS(self, domain): ''' Returns a list of authoritative Name Servers for the domain 'domain', and a list of 'A' records for these NS. :param domain: String, SOA domain name we are testing. :return: List of NameServers List of Ips ''' resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) resolver.nameservers = ([__default_ns__]) ns_names = [] ns_ips = [] try: response_ns = resolver.query(domain, 'NS') except Exception as ex: self.logger.warning("Error Resolving NS %s (%s)" % (domain, str(ex))) return None, None for ns in response_ns: try: # self.logger.debug("Resolving A record for NS " + ns.target.to_text() + " (domain=" + domain + ")") response_a = resolver.query(ns.target, 'A') ns_names.append(ns.target.to_text()) for a in response_a: ns_ips.append(a.address) except Exception as ex: self.logger.warning("Resolving A %s (%s)" % (ns.target.to_text(), str(ex))) if len(ns_names) == 0 or len(ns_ips) == 0: return None, None return ','.join(ns_names), ','.join(ns_ips)
def test_DNS(): import dns.resolver import dns.exception import dns.flags import dns.rdtypes import dns.rdatatype import dns.rdataclass import time # DNS stub configured to do DNSSEC enabled queries resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 1232) resolver.flags = dns.flags.AD | dns.flags.RD nameservers = resolver.nameservers for ns in nameservers: resolver.nameservers=[ns] while True: try: result = resolver.resolve('example.org', dns.rdatatype.A, dns.rdataclass.IN, lifetime=10) except Exception as e: log.critical("Your DNS resolver at %s is not working (%s). Please see https://mailu.io/master/faq.html#the-admin-container-won-t-start-and-its-log-says-critical-your-dns-resolver-isn-t-doing-dnssec-validation", ns, e); else: if result.response.flags & dns.flags.AD: break log.critical("Your DNS resolver at %s isn't doing DNSSEC validation; Please see https://mailu.io/master/faq.html#the-admin-container-won-t-start-and-its-log-says-critical-your-dns-resolver-isn-t-doing-dnssec-validation.", ns) time.sleep(5)
def test_DNS(): import dns.resolver import dns.exception import dns.flags import dns.rdtypes import dns.rdatatype import dns.rdataclass import time # DNS stub configured to do DNSSEC enabled queries resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1232) resolver.flags = dns.flags.AD | dns.flags.RD nameservers = resolver.nameservers for ns in nameservers: resolver.nameservers = [ns] while True: try: result = resolver.resolve('example.org', dns.rdatatype.A, dns.rdataclass.IN, lifetime=10) except Exception as e: log.critical( "Your DNS resolver at %s is not working (%s). Please use another resolver or enable unbound via https://setup.mailu.io.", ns, e) else: if result.response.flags & dns.flags.AD: break log.critical( "Your DNS resolver at %s isn't doing DNSSEC validation; Please use another resolver or enable unbound via https://setup.mailu.io.", ns) time.sleep(5)
def getDNSKEYFromNS(nameserver, port, zone): nameserver = getNameserverIP(nameserver) #request = dns.message.make_query(zone, # dns.rdatatype.DNSKEY, # want_dnssec=True) #try: # response = dns.query.udp(request, nameserver, timeout=5, port=port) # return response resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) resolver.nameservers = ([nameserver]) name = dns.name.from_text(zone) rdtype = dns.rdatatype.DNSKEY rdclass = dns.rdataclass.IN try: response = resolver.query(name, rdtype, rdclass, True).response response_rrkey = response.find_rrset(response.answer, name, rdclass, dns.rdatatype.RRSIG, rdtype) return response_rrkey except DNSException as e: print( "CRITICAL: Unable to obtain answer from {nameserver} \ for zone: {zone}\n".format(**locals())) print("Exception - {e}".format(**locals())) exit(2)
def query_dns(self, domain, rdtype_text): if not HAVE_DNSPYTHON: raise RuntimeError("Using DNS requires dnspython. Either install it or use -g to use Google DNS API") # dnspython does not like DNS metaqueries such as ANY requests if rdtype_text == 'ANY': if not self.has_show_dnspython_any_warning: print("Warning: refusing to query DNS for type ANY (dnspython does not like it)") self.has_show_dnspython_any_warning = True return None print("Querying DNS for {} <{}>...".format(domain, rdtype_text)) resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) dot_domain = domain + '.' rdtype = dns.rdatatype.from_text(rdtype_text) rdclass = dns.rdataclass.IN result = { 'Status': 0, 'Question': [ { 'name': dot_domain, 'type': rdtype, }, ], 'Answer': [], } try: answers = resolver.query(dot_domain, rdtype, rdclass, True) except dns.resolver.NoAnswer: pass # Empty answer is successful except dns.resolver.NXDOMAIN: assert dns.rcode.NXDOMAIN == 3 result['Status'] = 3 else: result['Flags'] = { 'raw': answers.response.flags, 'QR': bool(answers.response.flags & dns.flags.QR), # Query Response (0x8000) 'AA': bool(answers.response.flags & dns.flags.AA), # Authoritative Answer (0x0400) 'TC': bool(answers.response.flags & dns.flags.TC), # Truncated Response (0x0200) 'RD': bool(answers.response.flags & dns.flags.RD), # Recursion Desired (0x0100) 'RA': bool(answers.response.flags & dns.flags.RA), # Recursion Available (0x0080) 'AD': bool(answers.response.flags & dns.flags.AD), # Authentic Data (0x0020) 'CD': bool(answers.response.flags & dns.flags.CD), # Checking Disabled (0x0010) } result['Answer'] = [ { 'name': answers.name.to_text(omit_final_dot=False), 'type': answer.rdtype, 'TTL': answers.ttl, 'data': answer.to_text(), } for answer in answers ] return json.dumps(result).encode('ascii')
def dnsping(host, server, dnsrecord, timeout, count, use_tcp=False, use_edns=False): resolver = dns.resolver.Resolver() resolver.nameservers = [server] resolver.timeout = timeout resolver.lifetime = timeout resolver.retry_servfail = 0 flags = 0 ttl = None answers = None if use_edns: resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO')) response_times = [] i = 0 for i in range(count): if shutdown: # user pressed CTRL+C break try: stime = time.time() answers = resolver.query(host, dnsrecord, tcp=use_tcp, raise_on_no_answer=False) # todo: response validation in future etime = time.time() except (dns.resolver.NoNameservers, dns.resolver.NoAnswer): break except dns.resolver.Timeout: pass else: elapsed = (etime - stime) * 1000 # convert to milliseconds response_times.append(elapsed) r_sent = i + 1 r_received = len(response_times) r_lost = r_sent - r_received r_lost_percent = (100 * r_lost) / r_sent if response_times: r_min = min(response_times) r_max = max(response_times) r_avg = sum(response_times) / r_received if len(response_times) > 1: r_stddev = stdev(response_times) else: r_stddev = 0 else: r_min = 0 r_max = 0 r_avg = 0 r_stddev = 0 if answers is not None: flags = answers.response.flags if answers.rrset is not None: ttl = answers.rrset.ttl return server, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags, ttl
def get_resolver(addresses=None, lifetime=5, payload=1420): """ Return resolver object configured to use given list of addresses, and that sets DO=1, RD=1, AD=1, and EDNS payload for queries to the resolver. """ resolver = dns.resolver.Resolver() resolver.set_flags(dns.flags.RD | dns.flags.AD) resolver.use_edns(edns=0, ednsflags=dns.flags.DO, payload=payload) resolver.lifetime = lifetime if addresses is not None: resolver.nameservers = addresses return resolver
def run(self): arguments = self.arguments.__dict__ nameservers = arguments.get("nameservers") resolver = dns.resolver.Resolver(arguments.get("filename"), arguments.get("configure_resolver")) resolver.set_flags(arguments.get("flags")) resolver.use_edns(arguments.get("edns"), arguments.get("edns_flags"), arguments.get("edns_payload")) if nameservers: resolver.nameservers = nameservers resolver.port = arguments.get("port") resolver.timeout = arguments.get("timeout") resolver.lifetime = arguments.get("lifetime") resolver.retry_servfail = arguments.get("retry_servfail") if arguments.pop("metaquery"): kwargs = { v: arguments.get(k) for k, v in { "rdclass": "rdclass", "edns": "use_edns", "want_dnssec": "want_dnssec", "edns_flags": "ednsflags", "edns_payload": "request_payload" }.items() } message = dns.message.make_query(arguments.get("query"), arguments.get("rdtype"), **kwargs) kwargs = { k: arguments.get(k) for k in [ "timeout", "port", "source", "source_port", "one_rr_per_rrset" ] } if arguments.get("tcp"): resp = dns.query.tcp(message, resolver.nameservers[0], **kwargs) else: resp = dns.query.udp(message, resolver.nameservers[0], **kwargs) print(resp) else: kwargs = { k: arguments.get(k) for k in ["rdtype", "rdclass", "tcp", "source", "source_port"] } answer = resolver.query(arguments.pop("query"), **kwargs) print(answer.response)
def isSOA(self, domain): ''' Checks if a domain is a Start of Authority (SOA) and updates the cache domain_soa. :param domain: String, domain name to test if it is SOA :return: Boolean, domain is SOA? True:False String, ipv4 addresses ''' error_msg = None ds_row = self.init_ds_row(domain) try: domain_name = dns.name.from_text(domain) rd_type = dns.rdatatype.from_text('SOA') resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) response = resolver.query(domain_name, rd_type).response rrset = response.find_rrset(response.answer, domain_name, dns.rdataclass.IN, rd_type) if rrset is not None: ds_row['ds_is_soa'] = True (ds_row['ds_ns'], ds_row['ds_ns_ipv4']) = self.getNS(domain) self.logger.debug("[SOA Resolver] Domain %s IS SOA." % domain) self.cache_dnssec.insert_soa(domain, ds_row) return ds_row['ds_is_soa'], ds_row['ds_ns_ipv4'] else: error_msg = "[SOA Resolver] Domain %s NOT SOA." % domain except KeyError as kex: error_msg = '[SOA Resolver] KeyError: %s (%s)' % (domain, str(kex)) except dns.resolver.NXDOMAIN: error_msg = '[SOA Resolver] NXDOMAIN: %s' % (domain) except dns.resolver.Timeout: error_msg = '[SOA Resolver] Timeout: %s' % (domain) except dns.resolver.YXDOMAIN: error_msg = '[SOA Resolver] YXDOMAIN: %s' % (domain) except dns.resolver.NoAnswer: error_msg = '[SOA Resolver] NoAnswer: %s' % (domain) except dns.resolver.NoNameservers: error_msg = '[SOA Resolver] NoNameservers : %s' % (domain) except dns.exception.DNSException as dex: error_msg = '[SOA Resolver] DNSException: %s (%s)' % (domain, str(dex)) except Exception as ex: error_msg = '[SOA Resolver] General Exception %s (%s)' % (domain, str(ex)) self.logger.warning(error_msg) self.cache_dnssec.insert_soa(domain, ds_row) return False, None
def dig( qname, rdtype="A", timeout=5, resolvers="local", edns_size=1500, full_answers=False ): """ Do a quick DNS request and avoid the "search" trap inside /etc/resolv.conf """ # It's very important to do the request with a qname ended by . # If we don't and the domain fail, dns resolver try a second request # by concatenate the qname with the end of the "hostname" if not qname.endswith("."): qname += "." if resolvers == "local": resolvers = ["127.0.0.1"] elif resolvers == "force_external": resolvers = external_resolvers() else: assert isinstance(resolvers, list) resolver = dns.resolver.Resolver(configure=False) resolver.use_edns(0, 0, edns_size) resolver.nameservers = resolvers # resolver.timeout is used to trigger the next DNS query on resolvers list. # In python-dns 1.16, this value is set to 2.0. However, this means that if # the 3 first dns resolvers in list are down, we wait 6 seconds before to # run the DNS query to a DNS resolvers up... # In diagnosis dnsrecords, with 10 domains this means at least 12min, too long. resolver.timeout = 1.0 # resolver.lifetime is the timeout for resolver.query() # By default set it to 5 seconds to allow 4 resolvers to be unreachable. resolver.lifetime = timeout try: answers = resolver.query(qname, rdtype) except ( dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.NoAnswer, dns.exception.Timeout, ) as e: return ("nok", (e.__class__.__name__, e)) if not full_answers: answers = [answer.to_text() for answer in answers] return ("ok", answers)
def process_cds_records(obj, dry_run=True): domain = get_single_attr(obj, "domain") print(f"Domain: {domain}") lm = get_single_attr(obj, "last-modified") lm = datetime.datetime.strptime(lm, "%Y-%m-%dT%H:%M:%SZ") lm = lm.replace(tzinfo=datetime.timezone.utc) ripe_ds_rdataset = set(get_attrs(obj, "ds-rdata")) print(f"RIPE rdataset: {ripe_ds_rdataset}") resolver = dns.resolver.Resolver() resolver.set_flags(dns.flags.RD | dns.flags.AD) resolver.use_edns(0, dns.flags.DO, 512) try: a = resolver.query(domain + ".", "CDS") assert a.response.rcode() == 0, "DNS response failure" assert a.response.flags & dns.flags.AD, "Unauthenticated DNS response" asig = a.response.find_rrset( a.response.answer, a.qname, a.rdclass, dns.rdatatype.RRSIG, a.rdtype, ) inception = datetime.datetime.fromtimestamp( asig[0].inception, datetime.timezone.utc, ) dns_ds_rdataset = {rd.to_text() for rd in a} print(f"DNS rdataset: {dns_ds_rdataset}") print(f"Inception: {inception}, last modified: {lm}") assert inception > lm, "Signature inception too early" if dns_ds_rdataset and dns_ds_rdataset != ripe_ds_rdataset: delete_ds_rdata(obj) if not (len(a) == 1 and # Special Delete DS record a[0].key_tag == 0 and a[0].algorithm == 0 and a[0].digest_type == 0 and a[0].digest == b'\x00'): append_ds_rdata(obj, dns_ds_rdataset) print("updating DB record") o = put_object_to_ripe_db(obj, c.UPDATER_PW, dry_run=dry_run) print_rpsl_object(o) except dns.exception.DNSException as e: print(f"DNS exception: {e}") except AssertionError as e: print(f"Assertion error: {e}")
def getDomainSOA(domain_name): '''Returns the set of SOA records for the given domain using the default system resolver''' resolver = dns.resolver.Resolver() resolver.use_edns(0,dns.flags.DO,4096) resolver.nameservers=([__default_ns__]) if (domain_name == '.'): return domain_name query_domain_parts = domain_name.split('.') query_domain = '.'.join(query_domain_parts) try: soa_response = resolver.query(query_domain, 'SOA') except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN): query_domain_parts.pop(0) query_domain = '.'.join(query_domain_parts) domain_name = getDomainSOA(query_domain) return domain_name
def getDNSKEYFromNS(domain_name, name_server): '''Return the set of DNSKEY records for the given domain, as obtained from the given server''' resolver = dns.resolver.Resolver() resolver.use_edns(0,dns.flags.DO,4096) resolver.nameservers = ([name_server]) name = dns.name.from_text(domain_name) rdtype = dns.rdatatype.DNSKEY rdclass = dns.rdataclass.IN try: response = resolver.query(name, rdtype, rdclass, True).response response_rrkey = response.find_rrset(response.answer, name, rdclass, dns.rdatatype.RRSIG, rdtype) response_dnskey = response.find_rrset(response.answer, name, rdclass, rdtype) except dns.resolver.NoAnswer: print 'no answer returned' except dns.resolver.NXDOMAIN: print 'NXDOMAIN' return (response_dnskey, response_rrkey)
def getCNAME(self, domain): ''' Returns the CNAME that MUST resolve a TLSA record. :param domain: String, TLSA domain name. :return: Boolean, DNS query successful? True:False String, cname String, error (if any) ''' resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) resolver.nameservers = ([__default_ns__]) try: cnames = resolver.query(domain, 'CNAME') if cnames is not None and len(cnames) > 0: return True, str(cnames[0]), None except Exception as ex: error = "Error Resolving CNAME %s (%s)" % (domain, str(ex)) self.logger.warning(error) return False, None, error
def check_dkim(self, domain, ns): ''' Given a domain, query the DNS server for _domainkey.<domain> if the server does not support DKIM, the answer should be NXDOMAIN :param domain: Domain name tested :param ns: List of IP addressedd of for the Name Servers of 'domain' :return: (boolean, String[]) domain has DKIM record? True:False answer sent from the domain Server ''' try: self.logger.info( f'DKIM for domain _domainkey.{domain} with NS ({str(ns)})') resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) resolver.nameservers = ns resolver.query(f'_domainkey.{domain}', 'TXT') has_dkim = True dkim_error = 'NO Error.' except dns.resolver.NXDOMAIN: has_dkim = False dkim_error = f'NXDOMAIN: _domainkey.{domain} [DKIM]' self.logger.warning(dkim_error) except dns.resolver.Timeout: has_dkim = True dkim_error = f'Timeout: _domainkey.{domain} [DKIM]' self.logger.warning(dkim_error) except dns.resolver.NoAnswer: has_dkim = True dkim_error = f'NoAnswer: _domainkey.{domain} [DKIM]' self.logger.warning(dkim_error) except dns.exception.DNSException as dex: has_dkim = True dkim_error = f'DNSException: _domainkey.{domain} [DKIM] {dex}' self.logger.warning(dkim_error) except Exception as ex: has_dkim = True dkim_error = f'General Exception [DKIM] ({ex})' self.logger.warning(dkim_error) return has_dkim, dkim_error
def _refresh_dns_records_online(self, domain, rdtype_text): """Get the DNS records for the specified (domain, type), from online DNS servers""" resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) rdtype = dns.rdatatype.from_text(rdtype_text) rdclass = dns.rdataclass.IN try: response = resolver.query(domain, rdtype, rdclass, True).response except dns.resolver.NoAnswer: response = None except dns.resolver.NXDOMAIN: response = None # Remove old entries from the caches if domain in self.verified_cache and 'IN' in self.verified_cache[ domain]: rrsig_type_text = 'RRSIG ' + rdtype_text if rdtype_text in self.verified_cache[domain]['IN']: del self.verified_cache[domain]['IN'][rdtype_text] if rrsig_type_text in self.verified_cache[domain]['IN']: del self.verified_cache[domain]['IN'][rrsig_type_text] # Update the cache if response is None: logger.debug("DNS request to %s %r: no answer", rdtype_text, domain) return logger.debug("DNS request to %s %r: %d %s", rdtype_text, domain, len(response.answer), 'answers' if len(response.answer) >= 2 else 'answer') for answer in response.answer: for line in answer.to_text().splitlines(): # Strip the TTL (Time to Live) from the result domain, _, remaining = line.split(None, 2) self.load_dns_line('{} {}'.format(domain, remaining)) # Verify the new record before trying to verify other things self.verify_cached_record(domain, 'IN', rdtype_text, refresh=True)
def ping(resolver, hostname, dnsrecord, ttl, use_edns= False): global _ttl reached = False dns.query.socket_factory = CustomSocket _ttl = ttl if use_edns: resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO')) try: resolver.query(hostname, dnsrecord, raise_on_no_answer=False) except dns.resolver.NoNameservers as e: if not quiet: print("no or bad response:", e) sys.exit(1) except dns.resolver.NXDOMAIN as e: if not quiet: print("Invalid hostname:", e) sys.exit(1) except dns.resolver.Timeout: pass except dns.resolver.NoAnswer: if not quiet: print("invalid answer") pass except SystemExit: pass except Exception as e: print("unxpected error: ", e) sys.exit(1) else: reached = True return reached
print(" ", ref.message.get("Subject")) print(" ", ref.source_addr) else: print(sender) iffyDomains = [] for name in multiSenderNames: senders = multiSenderNames[name] for sender in senders: refs = senders[sender] for ref in refs: if ref.sender_domain not in goodDomains and ref.sender_domain not in iffyDomains: iffyDomains.append(ref.sender_domain) resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1410) @asyncio.coroutine def check_domain(domain): try: result = yield from resolver.aquery(domain, "txt", raise_on_no_answer=True) except: return None return domain @asyncio.coroutine def wait_for_answers(loop): tasks = [] for domain in filterDomains: if domain not in iffyDomains: tasks.append(check_domain(domain))
def check_host(ipaddr, domain, sender, debug=False): limiter = [0] resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1410) resolver.nameservers = ["127.0.0.1"] @asyncio.coroutine def check_host_worker(ipaddr, domain, sender, debug=False): limiter[0] = limiter[0] + 1 if limiter[0] > 10: if debug: print("DNS query limiter stopped search.") return "permerror" # RFC 7208 section 4.3: check for invalid domain name try: name = dns.name.from_text(domain) except: if debug: print("failure:", domain, " is not a valid domain name") return "permerror" # RFC 7208 section 4.3: check for existence of local-part # We are assuming that the caller has produced some # vaguely valid sender, either from the MAIL FROM: # or HELO/EHLO. For validating From: headers, # combining the source IP address and sender isn't # sufficient to say that the use isn't permitted, but # it is sufficient to say that the use _is_ permitted. if '@' not in sender: try: sdo = dns.name.from_text(sender) except: if debug: print("failure:", sender, " is not a valid domain name") return "permerror" sender = "postmaster@" + sender else: parts = sender.split("@") if len(parts) != 2: return "permerror" if parts[0] == '': sender = "postmaster@" + parts[1] try: sdo = dns.name.from_text(parts[1]) except: if debug: print("failure:", parts[1], " is not a valid domain name") return "permerror" spfs = [] try: spfRecord = yield from resolver.aquery(domain, "TXT", raise_on_no_answer=True) except dns.resolver.NoAnswer: if debug: print(domain, "IN TXT: No Answer") return None except Exception as x: if debug: print(domain, "IN TXT:", str(x)) return "temperror" else: for rr in spfRecord: text = "".join(rr.strings) if text.startswith("v=spf1 "): spfs.append(text[7:]) if len(spfs) == 0: if debug: print(domain, "IN TXT: No valid SPF record") return None if len(spfs) > 1: if debug: print("More than one SPF record found.") return "permerror" # Terms are separated by exactly one space. # terms = *( 1*SP ( directive / modifier ) ) if debug: print(spfs[0]) terms = spfs[0].split(" ") redirect = None modifiers = {} directives = [] for term in terms: if len(term) == 0: if debug: print("syntax error: zero-length term") return "permerror" # Eliminate the possibility that this is a modifier first, # because they are easy to detect. elif "=" in term: sides = term.split("=") if len(sides) != 2: if debug: print("bogus modifier") return "permerror" # modifiers can only appear once. if sides[0] in modifiers: if debug: print("Duplicate modifier:", sides[0]) return "permerror" exp = False if sides[0] == "exp": exp = True expansion = macro_expand(sides[1], ipaddr, domain, sender, exp) if expansion == None: return "permerror" modifiers[sides[0]] = sides[1] else: # By default, then, this is a directive. directive = Directive(check_host_worker, resolver, debug) if directive.validate(term, ipaddr, domain, sender) == "permerror": return "permerror" directives.append(directive) for directive in directives: status = yield from directive.process(ipaddr, domain, sender) # If this directive matched, don't process any later directives. if directive.matched: if debug: print("Matched:", directive.name) return status # If this directive returned a definite answer, return that answer # (e.g., tempfail, etc.) if status != None and status != "neutral": if debug: print("Status:", status) return status # Since we survived the directives, try the modifiers. for modifier in modifiers: if modifier == "exp": # We could really care less. pass elif modifier == "redirect": if debug: print("Redirect:", modifiers[modifier]) status = yield from check_host_worker(ipaddr, modifiers[modifier], sender, debug) if debug: print("Status:", status) return status return "neutral" status = yield from check_host_worker(ipaddr, domain, sender, debug) if ipaddr == "54.204.34.3": print("status:", status) # If the SPF record is broken, or there is no SPF record, then allow # the A record for the domain, if any. if status == "permerror" or status == None: print("re-checking", ipaddr, "at", domain) directive = Directive(None, resolver, debug) if directive.validate("+a", ipaddr, domain, sender) == "permerror": return "permerror" status = yield from directive.process(ipaddr, domain, sender) print("status:", repr(status)) return status
def get_exchanger_list(domain, resolver, family=socket.AF_UNSPEC, lim=None, implicit=True): resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1410) mxs = {} answer = None addressable = False arecs = None a4recs = None if family == socket.AF_UNSPEC or family == socket.AF_INET: arecs = [] if family == socket.AF_UNSPEC or family == socket.AF_INET6: a4recs = [] if lim: if lim[0] == 0: raise TooManyQueries else: lim[0] = lim[0] - 1 try: answer = yield from resolver.aquery(domain, "MX") except dns.resolver.NoAnswer: if not implicit: return None # No answer means there's no MX record, so look for an A or # AAAA record. yield from fetch_addrs(resolver, domain, arecs, a4recs, lim) if ((arecs and len(arecs) > 0) or a4recs and len(a4recs) > 0): mxs = { 0: [ { "exchange" : domain, "a": arecs, "aaaa": a4recs } ] } addressable = True except (NXDOMAIN, TooManyQueries): raise except: return None else: for mx in answer: if mx.rdtype == dns.rdatatype.MX: # If exchange addresses were included in the additional # section, use those. # XXX for SPF, relying on the additional section may be a mistake: # what if it includes some, but not all, relevant data? for rrset in answer.response.additional: if rrset.name == mx.exchange: if rrset.rdtype == dns.rdatatype.A and arecs != None: for rdata in rrset: arecs.append(rdata.address) addressable = True elif rrset.rdtype == dns.rdatatype.AAAA and a4recs != None: for rdata in rrset: a4recs.append(rdata.address) addressable = True # Otherwise, fetch A and/or AAAA records for exchange if not addressable: yield from fetch_addrs(resolver, mx.exchange, arecs, a4recs, lim) if ((arecs and len(arecs) > 0) or a4recs and len(a4recs) > 0): entry = { "exchange": mx.exchange, "a": arecs, "aaaa": a4recs} if mx.preference in mxs: mxs[mx.preference].append(entry) else: mxs[mx.preference] = [entry] addressable = True # If we didn't get a single server IP address either out of the # MX query chain or the A/AAAA query on the name if there was no # MX, then we can't deliver to this address. if not addressable: return None return mxs
def main(): try: signal.signal(signal.SIGTSTP, signal.SIG_IGN) # ignore CTRL+Z signal.signal(signal.SIGINT, signal_handler) # custom CTRL+C handler except AttributeError: # OS Does not support some signals, probably windows pass if len(sys.argv) == 1: usage() # defaults dnsrecord = 'A' count = 10 timeout = 2 interval = 0 quiet = False verbose = False dnsserver = dns.resolver.get_default_resolver().nameservers[0] dst_port = 53 src_port = 0 src_ip = None use_tcp = False use_edns = True af = socket.AF_INET hostname = 'wikipedia.org' try: opts, args = getopt.getopt(sys.argv[1:], "qhc:s:t:w:i:vp:P:S:T46e", ["help", "count=", "server=", "quiet", "type=", "wait=", "interval=", "verbose", "port=", "srcip=", "tcp", "ipv4", "ipv6", "srcport=", "edns"]) except getopt.GetoptError as err: # print help information and exit: print(err) # will print something like "option -a not recognized" usage() if args and len(args) == 1: hostname = args[0] else: usage() for o, a in opts: if o in ("-h", "--help"): usage() elif o in ("-c", "--count"): count = int(a) elif o in ("-v", "--verbose"): verbose = True elif o in ("-s", "--server"): dnsserver = a elif o in ("-p", "--port"): dst_port = int(a) elif o in ("-q", "--quiet"): quiet = True verbose = False elif o in ("-w", "--wait"): timeout = int(a) elif o in ("-i", "--interval"): interval = int(a) elif o in ("-t", "--type"): dnsrecord = a elif o in ("-T", "--tcp"): use_tcp = True elif o in ("-4", "--ipv4"): af = socket.AF_INET elif o in ("-6", "--ipv6"): af = socket.AF_INET6 elif o in ("-e", "--edns"): use_edns = False elif o in ("-P", "--srcport"): src_port = int(a) if src_port < 1024: print("WARNING: Source ports below 1024 are only available to superuser") elif o in ("-S", "--srcip"): src_ip = a else: usage() # check if we have a valid dns server address try: ipaddress.ip_address(dnsserver) except ValueError: # so it is not a valid IPv4 or IPv6 address, so try to resolve host name try: dnsserver = socket.getaddrinfo(dnsserver, port=None, family=af)[1][4][0] except OSError: print('Error: cannot resolve hostname:', dnsserver) sys.exit(1) resolver = dns.resolver.Resolver() resolver.nameservers = [dnsserver] resolver.timeout = timeout resolver.lifetime = timeout resolver.port = dst_port resolver.retry_servfail = 0 if use_edns: resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO')) response_time = [] i = 0 print("%s DNS: %s:%d, hostname: %s, rdatatype: %s" % (__PROGNAME__, dnsserver, dst_port, hostname, dnsrecord)) for i in range(count): if shutdown: break try: stime = time.time() answers = resolver.query(hostname, dnsrecord, source_port=src_port, source=src_ip, tcp=use_tcp, raise_on_no_answer=False) etime = time.time() except dns.resolver.NoNameservers as e: if not quiet: print("No response to dns request") if verbose: print("error:", e) sys.exit(1) except dns.resolver.NXDOMAIN as e: if not quiet: print("Hostname does not exist") if verbose: print("Error:", e) sys.exit(1) except dns.resolver.Timeout: if not quiet: print("Request timeout") pass except dns.resolver.NoAnswer: if not quiet: print("No answer") pass else: elapsed = (etime - stime) * 1000 # convert to milliseconds response_time.append(elapsed) if not quiet: print( "%d bytes from %s: seq=%-3d time=%3.3f ms" % ( len(str(answers.rrset)), dnsserver, i, elapsed)) if verbose: print(answers.rrset) print("flags:", dns.flags.to_text(answers.response.flags)) time_to_next = (stime + interval) - etime if time_to_next > 0: time.sleep(time_to_next) r_sent = i + 1 r_received = len(response_time) r_lost = r_sent - r_received r_lost_percent = (100 * r_lost) / r_sent if response_time: r_min = min(response_time) r_max = max(response_time) r_avg = sum(response_time) / r_received if len(response_time) > 1: r_stddev = stdev(response_time) else: r_stddev = 0 else: r_min = 0 r_max = 0 r_avg = 0 r_stddev = 0 print('\n--- %s dnsping statistics ---' % dnsserver) print('%d requests transmitted, %d responses received, %3.0f%% lost' % (r_sent, r_received, r_lost_percent)) print('min=%3.3f ms, avg=%3.3f ms, max=%3.3f ms, stddev=%3.3f ms' % (r_min, r_avg, r_max, r_stddev))
parser.add_argument('--domain','-d', action='store', type=str, nargs=1, required=True, help='The domain of the record you wish to query') parser.add_argument('--warn','-w', action='store', type=int, nargs=1, required=True, help='Number of days to warn') parser.add_argument('--crit','-c', action='store', type=int, nargs=1, required=True, help='Number of days to critical') #Parameters args = parser.parse_args() wt = args.warn[0] ct = args.crit[0] name_server = '8.8.8.8' domain_name = dns.name.from_text(args.domain[0]) #Take from argument crit = 0 warn = 0 ok = 0 #Setup Resolver rdtype = dns.rdatatype.DNSKEY resolver = dns.resolver.Resolver() resolver.use_edns(0,dns.flags.DO,4096) resolver.nameservers = ([name_server]) # Get dns RRSIG def get_rrsig(): try: response = resolver.query(domain_name, rdtype, dns.rdataclass.IN,True).response rrsig = response.find_rrset(response.answer, domain_name, dns.rdataclass.IN, dns.rdatatype.RRSIG, rdtype) return rrsig except dns.resolver.NoAnswer: print 'no answer returned' sys.exit(3) except dns.resolver.NXDOMAIN: print 'NXDOMAIN'
def get_connection(self, user, domain): # We're already connected. Just return the connection. if domain in self.connections: connection = self.connections[domain] if self.connections[domain] not in self.connection_list: status = yield from self.send_rcptto(connection, user + "@" + domain) if status: self.connections[user + "@" + domain] = connection self.connection_list.append(connection) else: print("bad status after send_rcptto.") return status return True resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1410) mxs = {} answer = None addressable = False try: answer = yield from resolver.aquery(domain, "MX") except dns.resolver.NoAnswer: # No answer means there's no MX record, so look for an A or # AAAA record. arecs = [] a4recs = [] yield from self.fetch_addrs(resolver, domain, arecs, a4recs) if len(arecs) > 0 or len(a4recs) > 0: mxs = {0: [{"exchange": domain, "a": arecs, "aaaa": a4recs}]} addressable = True except NXDOMAIN: self.push("550 no such domain.") syslog.syslog(syslog.LOG_INFO, "550 no such domain: %s" % domain) print("550 no such domain: %s" % domain) return False except: # Temporary failure; we just have to stash the message for this # address. self.connections[user + "@" + domain] = None self.connections[domain] = None return True else: for mx in answer: if mx.rdtype == dns.rdatatype.MX: arecs = [] a4recs = [] # If exchange addresses were included in the additional # section, use those. for rrset in answer.response.additional: if rrset.name == mx.exchange: if rrset.rdtype == dns.rdatatype.A: for rdata in rrset: arecs.append(rdata.address) elif rrset.rdtype == dns.rdatatype.AAAA: for rdata in rrset: a4recs.append(rdata.address) # Otherwise, fetch A and/or AAAA records for exchange if len(arecs) == 0 and len(a4recs) == 0: yield from self.fetch_addrs(resolver, mx.exchange, arecs, a4recs) if len(arecs) > 0 or len(a4recs) > 0: entry = { "exchange": mx.exchange, "a": arecs, "aaaa": a4recs } if mx.preference in mxs: mxs[mx.preference].append(entry) else: mxs[mx.preference] = [entry] addressable = True # If we didn't get a single server IP address either out of the # MX query chain or the A/AAAA query on the name if there was no # MX, then we can't deliver to this address. if not addressable: self.push("550 no exchanger or addresses for domain.") syslog.syslog(syslog.LOG_INFO, "550 no exchanger or addresses for: %s" % domain) print("550 no exchanger or addresses for: %s" % domain) return False # Our task now is to get a connection to the most preferable # Mail Exchanger (MX) we can reach. # Make a list of all the addresses to try, in order of preference. # We prefer IPv6 for the first attempt, but interleave IPv6 and # IPv4 addresses in case one transport is working and the other # is not. The interleaving is per-exchange, so we always try # exchanges in order of preference and, among exchanges with the # same preference, one exchange at a time. addrs = [] preferences = list(mxs.keys()) preferences.sort() # Iterate across preference levels for pref in preferences: exchanges = mxs[pref] # Iterate across exchanges at a given preference level for exchange in exchanges: arecs = exchange['a'] qrecs = exchange['aaaa'] name = exchange['exchange'] # Interleave the IPv6 and IPv4 addresses for this exchange. lim = max(len(arecs), len(qrecs)) for i in range(0, lim): if i < len(qrecs): addrs.append((qrecs[i], socket.AF_INET6, name)) if i < len(arecs): addrs.append((arecs[i], socket.AF_INET, name)) # Time is of the essence here, because the mail user agent is # waiting, and we want to give the user quick feedback, but we # also want to follow the rules and not boost our spam score # by delivering to a low-preference MX, so we allow about five # seconds to complete a connection rather than the usual 90 # seconds. We start connecting every five seconds, and take # the first connection that completes, dropping the others. # It should be rare that a connection takes longer than five # seconds to complete if the exchange is reachable. connection = yield from self.connect_to_addresses(addrs, 5) if connection != None: status = yield from self.send_rcptto(connection, user + "@" + domain) if status: self.connections[user + "@" + domain] = connection self.connections[domain] = connection self.connection_list.append(connection) else: print("horked in send_rcptto") return status print("no connection returned.") return False
def getRRSIG(self, domain, name_server, record, use_tcp=True, attempt=1): ''' Return the set of RRSIG records for the given domain, as obtained from the given server: 'name_server' :param domain: String, domain name assessed :param name_server: String, IPv4 address of the Name Server used in the DNS query :param record: String, DNS record type requested e.g. (TXT, A, ...) :param use_tcp: Boolean, indicates if the DNS request is TCP or UDP. :param attempt: integer, to ensure we repeat the DNS request only once, when the result is Timeout. :return: Set of RRSIG values Set of RRSET values String, error (if any) ''' resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) resolver.nameservers = ([name_server]) domain_name = dns.name.from_text(domain) rd_type = dns.rdatatype.from_text(record) rd_class = dns.rdataclass.IN error_msg, rrsig, rrset = (None, None, None) try: response = resolver.query(domain_name, rd_type, rd_class, tcp=use_tcp).response rrsig = response.find_rrset(response.answer, domain_name, rd_class, dns.rdatatype.RRSIG, rd_type) rrset = response.find_rrset(response.answer, domain_name, rd_class, rd_type) except dns.resolver.NXDOMAIN: error_msg = ("getRRSIG %s, %s NXDOMAIN " % (record, domain)) self.logger.warning(error_msg) except dns.resolver.Timeout: error_msg = ("getRRSIG %s, %s Timeout " % (record, domain)) self.logger.warning(error_msg) if attempt == 1: self.logger.warning( "TCP Query Failed, attempting UDP for %s %s" % (record, domain)) rrsig, rrset, error_msg = self.getRRSIG(domain, name_server, record, use_tcp=False, attempt=2) except dns.resolver.NoAnswer: error_msg = ("getRRSIG %s, %s No Answer Returned " % (record, domain)) self.logger.warning(error_msg) except dns.exception.DNSException as dex: error_msg = ("getRRSIG %s, %s DNSException (%s)" % (record, domain, str(dex))) self.logger.warning(error_msg) except KeyError as kex: error_msg = ("getRRSIG %s, %s KeyError (%s)" % (record, domain, str(kex))) self.logger.warning(error_msg) except Exception as ex: error_msg = ("getRRSIG %s, %s GeneralException (%s)" % (record, domain, str(ex))) self.logger.warning(error_msg) return rrsig, rrset, error_msg
def check_DS_records(self, children_domain, parent_ns): ''' It will compare the answers from all NameServers, and returns the ones that are common to all of the NSs. If there is no common answer, it will return False. :param children_domain: String, domain assessed :param parent_ns: List of String, Ip addresses of the Name Servers of the parent domain od 'children_domain' :return: Return the set of DS records for the given domain, using name_server as DNS server (domain=children, name_server=parent_ns) String, Error (if any) ''' resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) responses = None try: sets = [] dskeys = [] for ns in parent_ns: try: ns_set = set() ns_dskeys = [] ns_dskey = {} resolver.nameservers = ([ns]) responses = resolver.query(children_domain, 'DS') for response in responses: key = hashlib.sha256( response.to_text().encode("utf-8")).hexdigest() ns_set.add(key) ns_dskey[key] = response ns_dskeys.append(ns_dskey) sets.append(ns_set) dskeys.append(ns_dskeys) except dns.resolver.NXDOMAIN: error_msg = "getDSrecord NXDOMAIN: %s" % children_domain self.logger.warning(error_msg) return None, error_msg except dns.resolver.Timeout: error_msg = "getDSrecord Timeout: %s" % children_domain self.logger.warning(error_msg) return None, error_msg except dns.resolver.NoAnswer: error_msg = "getDSrecord NoAnswer: %s" % children_domain self.logger.warning(error_msg) return None, error_msg except dns.exception.DNSException as dex: error_msg = "getDSrecord DNSException: %s (%s)" % ( children_domain, str(dex)) self.logger.warning(error_msg) return None, error_msg except Exception as ex: error_msg = "getDSrecord Exception: %s (%s)" % ( children_domain, str(ex)) self.logger.warning(error_msg) return None, error_msg ds_common = set.intersection(*sets) if ds_common is None or len(ds_common) == 0: error_msg = "Inconsistent DS records %s" % children_domain self.logger.warning(error_msg) return None, error_msg # What happens if we have: R1 = [a,b]; R2 = [a,b,c] # should the 'c' result raise a Warning or Erorr? # should we have only One common result? return responses, None except Exception as ex: error_msg = "getDSrecord: %s (%s)" % (children_domain, str(ex)) self.logger.error() return None, error_msg
def walk_nsec3(raindict, origin="sk"): resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1200) # Workaround dnspython bug #546 nsset = resolver.resolve(f"{origin}.", dns.rdatatype.NS, search=False) nsec3cache = dict() secureddomains = set() secureddomains2 = set() originhash = get_nsec3_hash(origin) d = origin h = originhash print("Walking NSEC3 hashes…") print( ".=indirect discovery d=direct discovery " "u=unknown domain discovered\n", ) iters, reqs, brokes, unknowns = 0, 0, 0, 0 for retries in range(10): try: nameserver = random.choice( [ns.target.to_text() for ns in nsset.rrset], ) print("Using nameserver", nameserver) with dns_socket(nameserver) as s: while iters == 0 or (h != originhash and d != origin): # print(f"i: {iters:6} q: {reqs:5} h: {h} d: {d}") iters += 1 if h not in nsec3cache: # print("Querying", d) q = dns.message.make_query( f"{d}.", "DS", want_dnssec=True, ) res = tcp_query(s, q) reqs += 1 ns3rr = [( rrset.name.labels[0].decode("ascii", ).upper(), rrset[0], ) for rrset in res.authority if rrset.rdtype == dns.rdatatype.NSEC3] nsec3cache.update(ns3rr) if [ rrset for rrset in res.answer if rrset.rdtype == dns.rdatatype.DS ]: # print(d, "discovered directly") print("d", end="", flush=True) if d in secureddomains2: sys.exit("Cycle detected!") secureddomains2.add(d) h = get_nsec3_hash(d) _, d = _next_odict_item(raindict, h) continue if h not in nsec3cache and len(ns3rr) > 0: newh = min([k for k, v in ns3rr if k > h]) print( "\nBroken NSEC3 chain: " "expected", h, "got", newh, ) brokes += 1 h = newh if dict(nsec3cache[h].windows)[0][5] & 0x10: # this owner has a DS record if h in raindict: d = raindict[h] print(".", end="", flush=True) else: d = f"UNKNOWN_{h}" unknowns += 1 print("u", end="", flush=True) # print(d, flush=True) if d in secureddomains: sys.exit("Cycle detected!") secureddomains.add(d) h = digest_to_ascii(nsec3cache[h].next) if h in raindict: _, d = _next_odict_item(raindict, h) if d == origin: # Hack for corner case when # last domain in chain is signed. d = raindict[h] else: print("\nHash not in rainbow table:", h) d = _guess_next_domain(raindict, h) print("Next domain guessed:", d) except (EOFError, ConnectionError): # Retry in case of server closing TCP connection print("\nConnection lost, reconnecting…", flush=True) else: # No exception, work is done break else: raise RuntimeError("Too many retries. Giving up.") indirectonly = secureddomains - secureddomains2 directonly = secureddomains2 - secureddomains indirectanddirect = secureddomains & secureddomains2 secureddomains = secureddomains.union(secureddomains2) print("\nIterations: ", iters) print("DNS requests: ", reqs) print("DS records discovered: ", len(secureddomains)) print( f"{len(indirectonly)} discovered only indirectly, " f"{len(directonly)} only directly, " f"{len(indirectanddirect)} both ways.", ) print("Unknown domain names: ", unknowns) print("Broken NSEC3 chain incidents: ", brokes) print("TCP connection retries: ", retries) with open("domains-secured.txt", "w") as outf: for d in sorted(secureddomains): outf.write(d) outf.write("\n") print(f"Walking {len(nsec3cache)} NSEC3 cache records…") # print("\n".join((f"{k} {digest_to_ascii(v.next)}" # for k, v in nsec3cache.items()))) i1, i2 = tee(sorted(nsec3cache)) brokes = 0 for h1, h2 in zip_longest(i1, i2, fillvalue=next(i2)): h1next = digest_to_ascii(nsec3cache[h1].next) if h1next != h2: # print(f"Broken chain, {h1next} expected, {h2} found.") brokes += 1 print(f"Finished. {brokes} incidents found.") return secureddomains
from werkzeug.middleware.proxy_fix import ProxyFix # Login configuration login = flask_login.LoginManager() login.login_view = "sso.login" @login.unauthorized_handler def handle_needs_login(): """ redirect unauthorized requests to login page """ return flask.redirect(flask.url_for('sso.login')) # DNS stub configured to do DNSSEC enabled queries resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 1232) resolver.flags = dns.flags.AD | dns.flags.RD def has_dane_record(domain, timeout=10): try: result = resolver.resolve(f'_25._tcp.{domain}', dns.rdatatype.TLSA, dns.rdataclass.IN, lifetime=timeout) if result.response.flags & dns.flags.AD: for record in result: if isinstance(record, dns.rdtypes.ANY.TLSA.TLSA): if record.usage in [2, 3] and record.selector in [ 0, 1 ] and record.mtype in [0, 1, 2]:
def get_exchanger_list(self): resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1410) mxs = {} answer = None addressable = False try: answer = yield from resolver.aquery(domain, "MX") except dns.resolver.NoAnswer: # No answer means there's no MX record, so look for an A or # AAAA record. arecs = [] a4recs = [] yield from self.fetch_addrs(resolver, domain, arecs, a4recs) if len(arecs) > 0 or len(a4recs) > 0: mxs = { 0: [ { "exchange" : domain, "a": arecs, "aaaa": a4recs } ] } addressable = True except NXDOMAIN: self.push("550 no such domain.") syslog.syslog(syslog.LOG_INFO, "550 no such domain: %s" % domain) print("550 no such domain: %s" % domain) return False except: return True else: for mx in answer: if mx.rdtype == dns.rdatatype.MX: arecs = [] a4recs = [] # If exchange addresses were included in the additional # section, use those. for rrset in answer.response.additional: if rrset.name == mx.exchange: if rrset.rdtype == dns.rdatatype.A: for rdata in rrset: arecs.append(rdata.address) elif rrset.rdtype == dns.rdatatype.AAAA: for rdata in rrset: a4recs.append(rdata.address) # Otherwise, fetch A and/or AAAA records for exchange if len(arecs) == 0 and len(a4recs) == 0: yield from self.fetch_addrs(resolver, mx.exchange, arecs, a4recs) if len(arecs) > 0 or len(a4recs) > 0: entry = { "exchange": mx.exchange, "a": arecs, "aaaa": a4recs} if mx.preference in mxs: mxs[mx.preference].append(entry) else: mxs[mx.preference] = [entry] addressable = True # If we didn't get a single server IP address either out of the # MX query chain or the A/AAAA query on the name if there was no # MX, then we can't deliver to this address. if not addressable: raise smtp.PermanentFailure(code=550, data=["no exchanger/address for domain"]) return mxs
def testResolveEdnsOptions(self, message_use_edns_mock): resolver = dns.resolver.Resolver() options = [dns.edns.ECSOption("1.1.1.1")] resolver.use_edns(True, options=options) resolver.resolve("dns.google.", "A") assert {"options": options} in message_use_edns_mock.call_args
def process_mail(self, message): print("received mail from %s, proccessing..." % message.get("From")) self.message = message # apply filters #print("Before:") #print(self.message.get_payload()) #for f in self.filters: # f.apply(self.message) #print("After:") #print(self.message.get_payload()) ### DNSSEC checks # get domain from Recipient domain = self.message.get('X-RcptTo').split('@')[1] # create resolver, set DO flag resolver = dns.resolver.Resolver() resolver.use_edns(0, dns.flags.DO, 4096) # make query print("requesting MX records") response = resolver.query(domain, 'MX') # take the MX record mx_server = str(response.rrset[0].exchange) # look for flags in the response for line in response.response.to_text().split('\n'): if line.startswith('flag') and "AD" in line: print('Found AD bit in dns response, DNSSEC record is valid') break # get TLSA record print("requesting TLSA records") response = resolver.query('_10587._tcp.' + mx_server, 'TLSA') tlsa_cert_text = response.rrset[0].to_text() tlsa_cert_bit = response.rrset[0].cert tlsa_cert = response.rrset[0].to_text()[6:] # look for flags in the response for line in response.response.to_text().split('\n'): if line.startswith('flag') and "AD" in line: print('Found AD bit in dns response, DNSSEC record is valid') break # connect to SMTP server print('bla') try: # connect to the mail server, initiate TLS connection smtp = smtplib.SMTP(mx_server, self.smtp_port) smtp.ehlo() smtp.starttls() # retreive TLS certificate cert = smtp.sock.getpeercert(True) # TODO: check tlsa fingerprint against smtp server's tls certificate self.smtp.send_message(self.message, from_addr=USER, to_addrs=self.message.get("X-RcptTo")) print("mail forwarded") except smtplib.SMTPAuthenticationError: sys.exit("SMTP auth failed") except: sys.exit("Could not send mail")
def get_exchanger_list(domain, resolver, family=socket.AF_UNSPEC, lim=None, implicit=True): resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1410) mxs = {} answer = None addressable = False arecs = None a4recs = None if family == socket.AF_UNSPEC or family == socket.AF_INET: arecs = [] if family == socket.AF_UNSPEC or family == socket.AF_INET6: a4recs = [] if lim: if lim[0] == 0: raise TooManyQueries else: lim[0] = lim[0] - 1 try: answer = yield from resolver.aquery(domain, "MX") except dns.resolver.NoAnswer: if not implicit: return None # No answer means there's no MX record, so look for an A or # AAAA record. yield from fetch_addrs(resolver, domain, arecs, a4recs, lim) if ((arecs and len(arecs) > 0) or a4recs and len(a4recs) > 0): mxs = {0: [{"exchange": domain, "a": arecs, "aaaa": a4recs}]} addressable = True except (NXDOMAIN, TooManyQueries): raise except: return None else: for mx in answer: if mx.rdtype == dns.rdatatype.MX: # If exchange addresses were included in the additional # section, use those. # XXX for SPF, relying on the additional section may be a mistake: # what if it includes some, but not all, relevant data? for rrset in answer.response.additional: if rrset.name == mx.exchange: if rrset.rdtype == dns.rdatatype.A and arecs != None: for rdata in rrset: arecs.append(rdata.address) addressable = True elif rrset.rdtype == dns.rdatatype.AAAA and a4recs != None: for rdata in rrset: a4recs.append(rdata.address) addressable = True # Otherwise, fetch A and/or AAAA records for exchange if not addressable: yield from fetch_addrs(resolver, mx.exchange, arecs, a4recs, lim) if ((arecs and len(arecs) > 0) or a4recs and len(a4recs) > 0): entry = { "exchange": mx.exchange, "a": arecs, "aaaa": a4recs } if mx.preference in mxs: mxs[mx.preference].append(entry) else: mxs[mx.preference] = [entry] addressable = True # If we didn't get a single server IP address either out of the # MX query chain or the A/AAAA query on the name if there was no # MX, then we can't deliver to this address. if not addressable: return None return mxs
def main(): try: signal.signal(signal.SIGTSTP, signal.SIG_IGN) # ignore CTRL+Z signal.signal(signal.SIGINT, signal_handler) # custom CTRL+C handler except AttributeError: # OS Does not support some signals, probably windows pass if len(sys.argv) == 1: usage() # defaults dnsrecord = 'A' count = 10 timeout = 2 interval = 1 quiet = False verbose = False dnsserver = dns.resolver.get_default_resolver().nameservers[0] dst_port = 53 src_port = 0 src_ip = None use_tcp = False use_edns = True af = socket.AF_INET hostname = 'wikipedia.org' try: opts, args = getopt.getopt(sys.argv[1:], "qhc:s:t:w:i:vp:P:S:T46e", [ "help", "count=", "server=", "quiet", "type=", "wait=", "interval=", "verbose", "port=", "srcip=", "tcp", "ipv4", "ipv6", "srcport=", "edns" ]) except getopt.GetoptError as err: # print help information and exit: print(err, file=sys.stderr ) # will print something like "option -a not recognized" usage() if args and len(args) == 1: hostname = args[0] else: usage() for o, a in opts: if o in ("-h", "--help"): usage() elif o in ("-c", "--count"): count = abs(int(a)) elif o in ("-v", "--verbose"): verbose = True elif o in ("-s", "--server"): dnsserver = a elif o in ("-p", "--port"): dst_port = int(a) elif o in ("-q", "--quiet"): quiet = True verbose = False elif o in ("-w", "--wait"): timeout = int(a) elif o in ("-i", "--interval"): interval = int(a) elif o in ("-t", "--type"): dnsrecord = a elif o in ("-T", "--tcp"): use_tcp = True elif o in ("-4", "--ipv4"): af = socket.AF_INET elif o in ("-6", "--ipv6"): af = socket.AF_INET6 elif o in ("-e", "--edns"): use_edns = False elif o in ("-P", "--srcport"): src_port = int(a) if src_port < 1024: print( "WARNING: Source ports below 1024 are only available to superuser", flush=True) elif o in ("-S", "--srcip"): src_ip = a else: usage() # check if we have a valid dns server address try: ipaddress.ip_address(dnsserver) except ValueError: # so it is not a valid IPv4 or IPv6 address, so try to resolve host name try: dnsserver = socket.getaddrinfo(dnsserver, port=None, family=af)[1][4][0] except OSError: print('Error: cannot resolve hostname:', dnsserver, file=sys.stderr, flush=True) sys.exit(1) resolver = dns.resolver.Resolver(configure=False) resolver.nameservers = [dnsserver] resolver.timeout = timeout resolver.lifetime = timeout resolver.port = dst_port resolver.retry_servfail = 0 if use_edns: resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO')) response_time = [] i = 0 print("%s DNS: %s:%d, hostname: %s, rdatatype: %s" % (__progname__, dnsserver, dst_port, hostname, dnsrecord), flush=True) while not shutdown: if 0 < count <= i: break else: i += 1 try: stime = time.perf_counter() answers = resolver.resolve(hostname, dnsrecord, source_port=src_port, source=src_ip, tcp=use_tcp, raise_on_no_answer=False) etime = time.perf_counter() except dns.resolver.NoNameservers as e: if not quiet: print("No response to DNS request", file=sys.stderr, flush=True) if verbose: print("error:", e, file=sys.stderr, flush=True) sys.exit(1) except dns.resolver.NXDOMAIN as e: if not quiet: print("Hostname does not exist (NXDOMAIN)", file=sys.stderr, flush=True) if verbose: print("Error:", e, file=sys.stderr, flush=True) sys.exit(1) except dns.resolver.Timeout: if not quiet: print("Request timeout", flush=True) except dns.resolver.NoAnswer: if not quiet: print("No answer", flush=True) else: elapsed = answers.response.time * 1000 # convert to milliseconds response_time.append(elapsed) if not quiet: print("%d bytes from %s: seq=%-3d time=%.3f ms" % (len(str(answers.rrset)), dnsserver, i, elapsed), flush=True) if verbose: print(answers.rrset, flush=True) print("flags:", dns.flags.to_text(answers.response.flags), flush=True) time_to_next = (stime + interval) - etime if time_to_next > 0: time.sleep(time_to_next) r_sent = i r_received = len(response_time) r_lost = r_sent - r_received r_lost_percent = (100 * r_lost) / r_sent if response_time: r_min = min(response_time) r_max = max(response_time) r_avg = sum(response_time) / r_received if len(response_time) > 1: r_stddev = stdev(response_time) else: r_stddev = 0 else: r_min = 0 r_max = 0 r_avg = 0 r_stddev = 0 print('\n--- %s dnsping statistics ---' % dnsserver, flush=True) print('%d requests transmitted, %d responses received, %.0f%% lost' % (r_sent, r_received, r_lost_percent), flush=True) print('min=%.3f ms, avg=%.3f ms, max=%.3f ms, stddev=%.3f ms' % (r_min, r_avg, r_max, r_stddev), flush=True)
def dnsping(host, server, dnsrecord, timeout, count, use_tcp=False, use_edns=False): resolver = dns.resolver.Resolver() resolver.nameservers = [server] resolver.timeout = timeout resolver.lifetime = timeout resolver.retry_servfail = 0 flags = 0 ttl = None answers = None if use_edns: resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO')) response_times = [] i = 0 for i in range(count): if shutdown: # user pressed CTRL+C break try: stime = time.perf_counter() answers = resolver.query(host, dnsrecord, tcp=use_tcp, raise_on_no_answer=False ) # todo: response validation in future etime = time.perf_counter() except (dns.resolver.NoNameservers, dns.resolver.NoAnswer): break except dns.resolver.Timeout: pass else: elapsed = (etime - stime) * 1000 # convert to milliseconds response_times.append(elapsed) r_sent = i + 1 r_received = len(response_times) r_lost = r_sent - r_received r_lost_percent = (100 * r_lost) / r_sent if response_times: r_min = min(response_times) r_max = max(response_times) r_avg = sum(response_times) / r_received if len(response_times) > 1: r_stddev = stdev(response_times) else: r_stddev = 0 else: r_min = 0 r_max = 0 r_avg = 0 r_stddev = 0 if answers is not None: flags = answers.response.flags if len(answers.response.answer) > 0: ttl = answers.response.answer[0].ttl return server, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags, ttl, answers