Move dig() to utils.dns

This commit is contained in:
Alexandre Aubin 2021-08-28 20:20:22 +02:00
parent 22aa1f2538
commit 8438efa680
5 changed files with 77 additions and 75 deletions

View file

@ -8,11 +8,10 @@ from publicsuffixlist import PublicSuffixList
from moulinette.utils.process import check_output
from yunohost.utils.network import dig
from yunohost.utils.dns import dig, YNH_DYNDNS_DOMAINS
from yunohost.diagnosis import Diagnoser
from yunohost.domain import domain_list, _build_dns_conf, _get_maindomain
YNH_DYNDNS_DOMAINS = ["nohost.me", "noho.st", "ynh.fr"]
SPECIAL_USE_TLDS = ["local", "localhost", "onion", "test"]

View file

@ -12,7 +12,7 @@ from moulinette.utils.filesystem import read_yaml
from yunohost.diagnosis import Diagnoser
from yunohost.domain import _get_maindomain, domain_list
from yunohost.settings import settings_get
from yunohost.utils.network import dig
from yunohost.utils.dns import dig
DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml"

View file

@ -38,7 +38,8 @@ from moulinette.utils.network import download_json
from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.domain import _get_maindomain, _build_dns_conf
from yunohost.utils.network import get_public_ip, dig
from yunohost.utils.network import get_public_ip
from yunohost.utils.dns import dig
from yunohost.log import is_unit_operation
from yunohost.regenconf import regen_conf

View file

@ -18,11 +18,82 @@
along with this program; if not, see http://www.gnu.org/licenses
"""
import dns.resolver
from publicsuffixlist import PublicSuffixList
from yunohost.utils.network import dig
from moulinette.utils.filesystem import read_file
YNH_DYNDNS_DOMAINS = ["nohost.me", "noho.st", "ynh.fr"]
# 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")
]
# We keep only ipv4 resolvers, otherwise on IPv4-only instances, IPv6
# will be tried anyway resulting in super-slow dig requests that'll wait
# until timeout...
external_resolvers_ = [r for r in external_resolvers_ if ":" not in r]
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
"""
# 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 get_public_suffix(domain):
"""get_public_suffix("www.example.com") -> "example.com"
@ -40,6 +111,7 @@ def get_public_suffix(domain):
return public_suffix
def get_dns_zone_from_domain(domain):
# TODO Check if this function is YNH_DYNDNS_DOMAINS compatible
"""

View file

@ -22,7 +22,6 @@ import os
import re
import logging
import time
import dns.resolver
from moulinette.utils.filesystem import read_file, write_to_file
from moulinette.utils.network import download_text
@ -124,75 +123,6 @@ def get_gateway():
return addr.popitem()[1] if len(addr) == 1 else None
# 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")
]
# We keep only ipv4 resolvers, otherwise on IPv4-only instances, IPv6
# will be tried anyway resulting in super-slow dig requests that'll wait
# until timeout...
external_resolvers_ = [r for r in external_resolvers_ if ":" not in r]
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
"""
# 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 _extract_inet(string, skip_netmask=False, skip_loopback=True):
"""
Extract IP addresses (v4 and/or v6) from a string limited to one