Example #1
0
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
Example #3
0
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
Example #4
0
    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
Example #5
0
    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)
Example #6
0
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)
Example #7
0
File: start.py Project: lub/Mailu
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)
Example #8
0
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)
Example #9
0
    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')
Example #10
0
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
Example #11
0
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
Example #12
0
 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)
Example #13
0
    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
Example #14
0
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)
Example #18
0
    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
Example #19
0
    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
Example #20
0
    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)
Example #21
0
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
Example #22
0
        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))
Example #23
0
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
Example #24
0
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
Example #25
0
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'
Example #27
0
    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
Example #28
0
    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
Example #29
0
    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
Example #30
0
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
Example #31
0
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]:
Example #32
0
  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
Example #33
0
 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
Example #34
0
    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")
Example #35
0
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
Example #36
0
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)
Example #37
0
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