Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_ddr.py155 linesDownload Raw Back to dns
1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license2#3# Support for Discovery of Designated Resolvers4 5import socket6import time7from urllib.parse import urlparse8 9import dns.asyncbackend10import dns.inet11import dns.name12import dns.nameserver13import dns.query14import dns.rdtypes.svcbbase15 16# The special name of the local resolver when using DDR17_local_resolver_name = dns.name.from_text("_dns.resolver.arpa")18 19 20#21# Processing is split up into I/O independent and I/O dependent parts to22# make supporting sync and async versions easy.23#24 25 26class _SVCBInfo:27    def __init__(self, bootstrap_address, port, hostname, nameservers):28        self.bootstrap_address = bootstrap_address29        self.port = port30        self.hostname = hostname31        self.nameservers = nameservers32 33    def ddr_check_certificate(self, cert):34        """Verify that the _SVCBInfo's address is in the cert's subjectAltName (SAN)"""35        for name, value in cert["subjectAltName"]:36            if name == "IP Address" and value == self.bootstrap_address:37                return True38        return False39 40    def make_tls_context(self):41        ssl = dns.query.ssl42        ctx = ssl.create_default_context()43        ctx.minimum_version = ssl.TLSVersion.TLSv1_244        return ctx45 46    def ddr_tls_check_sync(self, lifetime):47        ctx = self.make_tls_context()48        expiration = time.time() + lifetime49        with socket.create_connection(50            (self.bootstrap_address, self.port), lifetime51        ) as s:52            with ctx.wrap_socket(s, server_hostname=self.hostname) as ts:53                ts.settimeout(dns.query._remaining(expiration))54                ts.do_handshake()55                cert = ts.getpeercert()56                return self.ddr_check_certificate(cert)57 58    async def ddr_tls_check_async(self, lifetime, backend=None):59        if backend is None:60            backend = dns.asyncbackend.get_default_backend()61        ctx = self.make_tls_context()62        expiration = time.time() + lifetime63        async with await backend.make_socket(64            dns.inet.af_for_address(self.bootstrap_address),65            socket.SOCK_STREAM,66            0,67            None,68            (self.bootstrap_address, self.port),69            lifetime,70            ctx,71            self.hostname,72        ) as ts:73            cert = await ts.getpeercert(dns.query._remaining(expiration))74            return self.ddr_check_certificate(cert)75 76 77def _extract_nameservers_from_svcb(answer):78    bootstrap_address = answer.nameserver79    if not dns.inet.is_address(bootstrap_address):80        return []81    infos = []82    for rr in answer.rrset.processing_order():83        nameservers = []84        param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.ALPN)85        if param is None:86            continue87        alpns = set(param.ids)88        host = rr.target.to_text(omit_final_dot=True)89        port = None90        param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.PORT)91        if param is not None:92            port = param.port93        # For now we ignore address hints and address resolution and always use the94        # bootstrap address95        if b"h2" in alpns:96            param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.DOHPATH)97            if param is None or not param.value.endswith(b"{?dns}"):98                continue99            path = param.value[:-6].decode()100            if not path.startswith("/"):101                path = "/" + path102            if port is None:103                port = 443104            url = f"https://{host}:{port}{path}"105            # check the URL106            try:107                urlparse(url)108                nameservers.append(dns.nameserver.DoHNameserver(url, bootstrap_address))109            except Exception:110                # continue processing other ALPN types111                pass112        if b"dot" in alpns:113            if port is None:114                port = 853115            nameservers.append(116                dns.nameserver.DoTNameserver(bootstrap_address, port, host)117            )118        if b"doq" in alpns:119            if port is None:120                port = 853121            nameservers.append(122                dns.nameserver.DoQNameserver(bootstrap_address, port, True, host)123            )124        if len(nameservers) > 0:125            infos.append(_SVCBInfo(bootstrap_address, port, host, nameservers))126    return infos127 128 129def _get_nameservers_sync(answer, lifetime):130    """Return a list of TLS-validated resolver nameservers extracted from an SVCB131    answer."""132    nameservers = []133    infos = _extract_nameservers_from_svcb(answer)134    for info in infos:135        try:136            if info.ddr_tls_check_sync(lifetime):137                nameservers.extend(info.nameservers)138        except Exception:139            pass140    return nameservers141 142 143async def _get_nameservers_async(answer, lifetime):144    """Return a list of TLS-validated resolver nameservers extracted from an SVCB145    answer."""146    nameservers = []147    infos = _extract_nameservers_from_svcb(answer)148    for info in infos:149        try:150            if await info.ddr_tls_check_async(lifetime):151                nameservers.extend(info.nameservers)152        except Exception:153            pass154    return nameservers155 
codekingpro/portable-devtools · Team Ai