diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 5ed7fc737..53afb2c2d 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -2,9 +2,9 @@ import os -from moulinette.utils.process import check_output from moulinette.utils.filesystem import read_file +from yunohost.utils.network import dig from yunohost.diagnosis import Diagnoser from yunohost.domain import domain_list, _build_dns_conf, _get_maindomain @@ -100,20 +100,14 @@ class DNSRecordsDiagnoser(Diagnoser): yield output def get_current_record(self, domain, name, type_): - if name == "@": - command = "dig +short @%s %s %s" % (self.resolver, type_, domain) - else: - command = "dig +short @%s %s %s.%s" % (self.resolver, type_, name, domain) - # FIXME : gotta handle case where this command fails ... - # e.g. no internet connectivity (dependency mechanism to good result from 'ip' diagosis ?) - # or the resolver is unavailable for some reason - output = check_output(command).strip().split("\n") - if len(output) == 0 or not output[0]: + + query = "%s.%s" % (name, domain) if name != "@" else domain + success, answers = dig(query, type_, resolvers="force_external") + + if success != "ok": return None - elif len(output) == 1: - return output[0] else: - return output + return answers[0] if len(answers) == 1 else answers def current_record_match_expected(self, r): if r["value"] is not None and r["current"] is None: diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index afb88f7cf..a60b4f0d4 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -164,7 +164,7 @@ class MailDiagnoser(Diagnoser): query = subdomain + '.' + blacklist['dns_server'] # Do the DNS Query - status, answers = dig(query, 'A') + status, _ = dig(query, 'A') if status != 'ok': continue diff --git a/src/yunohost/utils/network.py b/src/yunohost/utils/network.py index 6dc4c22a0..23b2310f8 100644 --- a/src/yunohost/utils/network.py +++ b/src/yunohost/utils/network.py @@ -25,6 +25,7 @@ import dns.resolver from moulinette.utils.network import download_text from moulinette.utils.process import check_output +from moulinette.utils.filesystem import read_file logger = logging.getLogger('yunohost.utils.network') @@ -85,22 +86,50 @@ def get_gateway(): return addr.popitem()[1] if len(addr) == 1 else None -def dig(qname, rdtype="A", timeout=5, resolvers=["127.0.0.1"], edns_size=1500): +# Lazy dev caching to avoid re-reading the file multiple time when calling +# dig() often during same yunohost operation +external_resolvers_ = [] + + +def external_resolvers(): + + global external_resolvers_ + + if not external_resolvers_: + resolv_dnsmasq_conf = read_file("/etc/resolv.dnsmasq.conf").split("\n") + external_resolvers_ = [r.split(" ")[1] for r in resolv_dnsmasq_conf if r.startswith("nameserver")] + + return external_resolvers_ + + +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 """ + 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) + except (dns.resolver.NXDOMAIN, + dns.resolver.NoNameservers, + dns.resolver.NoAnswer, + dns.exception.Timeout) as e: + return ("nok", (e.__class__.__name__, e)) - return ("ok", [(answer.to_text(), answer) for answer in answers]) + if not full_answers: + answers = [answer.to_text() for answer in answers] + + return ("ok", answers) def _extract_inet(string, skip_netmask=False, skip_loopback=True):