From 0fc5105da515f6453889d87ab4f0347cbb8b0c58 Mon Sep 17 00:00:00 2001 From: KiekerJan Date: Sun, 15 Jan 2023 14:20:08 +0100 Subject: [PATCH] Fixes to DNS lookups during status checks when there are timeouts, enforce timeouts better (#2191) * add dns query handling changes * replace exception pass with error message * simplify dns exception catching * Add not set case to blacklist lookup result handling --- management/dns_update.py | 24 +++++++++++++++++------- management/status_checks.py | 13 +++++++++++-- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/management/dns_update.py b/management/dns_update.py index 2bfc104f..2fb2baf5 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -992,6 +992,7 @@ def set_custom_dns_record(qname, rtype, value, action, env): def get_secondary_dns(custom_dns, mode=None): resolver = dns.resolver.get_default_resolver() resolver.timeout = 10 + resolver.lifetime = 10 values = [] for qname, rtype, value in custom_dns: @@ -1009,10 +1010,17 @@ def get_secondary_dns(custom_dns, mode=None): # doesn't. if not hostname.startswith("xfr:"): if mode == "xfr": - response = dns.resolver.resolve(hostname+'.', "A", raise_on_no_answer=False) - values.extend(map(str, response)) - response = dns.resolver.resolve(hostname+'.', "AAAA", raise_on_no_answer=False) - values.extend(map(str, response)) + try: + response = resolver.resolve(hostname+'.', "A", raise_on_no_answer=False) + values.extend(map(str, response)) + except dns.exception.DNSException: + pass + + try: + response = resolver.resolve(hostname+'.', "AAAA", raise_on_no_answer=False) + values.extend(map(str, response)) + except dns.exception.DNSException: + pass continue values.append(hostname) @@ -1030,15 +1038,17 @@ def set_secondary_dns(hostnames, env): # Validate that all hostnames are valid and that all zone-xfer IP addresses are valid. resolver = dns.resolver.get_default_resolver() resolver.timeout = 5 + resolver.lifetime = 5 + for item in hostnames: if not item.startswith("xfr:"): # Resolve hostname. try: response = resolver.resolve(item, "A") - except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): + except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.Timeout): try: response = resolver.resolve(item, "AAAA") - except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): + except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.Timeout): raise ValueError("Could not resolve the IP address of %s." % item) else: # Validate IP address. @@ -1071,7 +1081,7 @@ def get_custom_dns_records(custom_dns, qname, rtype): def build_recommended_dns(env): ret = [] for (domain, zonefile, records) in build_zones(env): - # remove records that we don't dislay + # remove records that we don't display records = [r for r in records if r[3] is not False] # put Required at the top, then Recommended, then everythiing else diff --git a/management/status_checks.py b/management/status_checks.py index 0d555441..4b3713ba 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -308,6 +308,8 @@ def run_network_checks(env, output): output.print_ok("IP address is not blacklisted by zen.spamhaus.org.") elif zen == "[timeout]": output.print_warning("Connection to zen.spamhaus.org timed out. We could not determine whether your server's IP address is blacklisted. Please try again later.") + elif zen == "[Not Set]": + output.print_warning("Could not connect to zen.spamhaus.org. We could not determine whether your server's IP address is blacklisted. Please try again later.") else: output.print_error("""The IP address of this machine %s is listed in the Spamhaus Block List (code %s), which may prevent recipients from receiving your email. See http://www.spamhaus.org/query/ip/%s.""" @@ -541,7 +543,7 @@ def check_dns_zone(domain, env, output, dns_zonefiles): for ns in custom_secondary_ns: # We must first resolve the nameserver to an IP address so we can query it. ns_ips = query_dns(ns, "A") - if not ns_ips: + if not ns_ips or ns_ips in {'[Not Set]', '[timeout]'}: output.print_error("Secondary nameserver %s is not valid (it doesn't resolve to an IP address)." % ns) continue # Choose the first IP if nameserver returns multiple @@ -744,6 +746,8 @@ def check_mail_domain(domain, env, output): output.print_ok("Domain is not blacklisted by dbl.spamhaus.org.") elif dbl == "[timeout]": output.print_warning("Connection to dbl.spamhaus.org timed out. We could not determine whether the domain {} is blacklisted. Please try again later.".format(domain)) + elif dbl == "[Not Set]": + output.print_warning("Could not connect to dbl.spamhaus.org. We could not determine whether the domain {} is blacklisted. Please try again later.".format(domain)) else: output.print_error("""This domain is listed in the Spamhaus Domain Block List (code %s), which may prevent recipients from receiving your mail. @@ -788,12 +792,17 @@ def query_dns(qname, rtype, nxdomain='[Not Set]', at=None, as_list=False): # running bind server), or if the 'at' argument is specified, use that host # as the nameserver. resolver = dns.resolver.get_default_resolver() - if at: + + # Make sure at is not a string that cannot be used as a nameserver + if at and at not in {'[Not set]', '[timeout]'}: resolver = dns.resolver.Resolver() resolver.nameservers = [at] # Set a timeout so that a non-responsive server doesn't hold us back. resolver.timeout = 5 + # The number of seconds to spend trying to get an answer to the question. If the + # lifetime expires a dns.exception.Timeout exception will be raised. + resolver.lifetime = 5 # Do the query. try: