Загрузить файлы в «venv/Lib/site-packages/dns»
This commit is contained in:
72
venv/Lib/site-packages/dns/__init__.py
Normal file
72
venv/Lib/site-packages/dns/__init__.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
|
# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc.
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
|
# provided that the above copyright notice and this permission notice
|
||||||
|
# appear in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
|
||||||
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
|
||||||
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
"""dnspython DNS toolkit"""
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"asyncbackend",
|
||||||
|
"asyncquery",
|
||||||
|
"asyncresolver",
|
||||||
|
"btree",
|
||||||
|
"btreezone",
|
||||||
|
"dnssec",
|
||||||
|
"dnssecalgs",
|
||||||
|
"dnssectypes",
|
||||||
|
"e164",
|
||||||
|
"edns",
|
||||||
|
"entropy",
|
||||||
|
"exception",
|
||||||
|
"flags",
|
||||||
|
"immutable",
|
||||||
|
"inet",
|
||||||
|
"ipv4",
|
||||||
|
"ipv6",
|
||||||
|
"message",
|
||||||
|
"name",
|
||||||
|
"namedict",
|
||||||
|
"node",
|
||||||
|
"opcode",
|
||||||
|
"query",
|
||||||
|
"quic",
|
||||||
|
"rcode",
|
||||||
|
"rdata",
|
||||||
|
"rdataclass",
|
||||||
|
"rdataset",
|
||||||
|
"rdatatype",
|
||||||
|
"renderer",
|
||||||
|
"resolver",
|
||||||
|
"reversename",
|
||||||
|
"rrset",
|
||||||
|
"serial",
|
||||||
|
"set",
|
||||||
|
"tokenizer",
|
||||||
|
"transaction",
|
||||||
|
"tsig",
|
||||||
|
"tsigkeyring",
|
||||||
|
"ttl",
|
||||||
|
"rdtypes",
|
||||||
|
"update",
|
||||||
|
"version",
|
||||||
|
"versioned",
|
||||||
|
"wire",
|
||||||
|
"xfr",
|
||||||
|
"zone",
|
||||||
|
"zonetypes",
|
||||||
|
"zonefile",
|
||||||
|
]
|
||||||
|
|
||||||
|
from dns.version import version as __version__ # noqa
|
||||||
100
venv/Lib/site-packages/dns/_asyncbackend.py
Normal file
100
venv/Lib/site-packages/dns/_asyncbackend.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
|
# This is a nullcontext for both sync and async. 3.7 has a nullcontext,
|
||||||
|
# but it is only for sync use.
|
||||||
|
|
||||||
|
|
||||||
|
class NullContext:
|
||||||
|
def __init__(self, enter_result=None):
|
||||||
|
self.enter_result = enter_result
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self.enter_result
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self.enter_result
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc_value, traceback):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# These are declared here so backends can import them without creating
|
||||||
|
# circular dependencies with dns.asyncbackend.
|
||||||
|
|
||||||
|
|
||||||
|
class Socket: # pragma: no cover
|
||||||
|
def __init__(self, family: int, type: int):
|
||||||
|
self.family = family
|
||||||
|
self.type = type
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def getpeername(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def getsockname(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def getpeercert(self, timeout):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc_value, traceback):
|
||||||
|
await self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class DatagramSocket(Socket): # pragma: no cover
|
||||||
|
async def sendto(self, what, destination, timeout):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def recvfrom(self, size, timeout):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class StreamSocket(Socket): # pragma: no cover
|
||||||
|
async def sendall(self, what, timeout):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def recv(self, size, timeout):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class NullTransport:
|
||||||
|
async def connect_tcp(self, host, port, timeout, local_address):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class Backend: # pragma: no cover
|
||||||
|
def name(self) -> str:
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
async def make_socket(
|
||||||
|
self,
|
||||||
|
af,
|
||||||
|
socktype,
|
||||||
|
proto=0,
|
||||||
|
source=None,
|
||||||
|
destination=None,
|
||||||
|
timeout=None,
|
||||||
|
ssl_context=None,
|
||||||
|
server_hostname=None,
|
||||||
|
):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def datagram_connection_required(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def sleep(self, interval):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_transport_class(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def wait_for(self, awaitable, timeout):
|
||||||
|
raise NotImplementedError
|
||||||
276
venv/Lib/site-packages/dns/_asyncio_backend.py
Normal file
276
venv/Lib/site-packages/dns/_asyncio_backend.py
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
|
"""asyncio library query support"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import dns._asyncbackend
|
||||||
|
import dns._features
|
||||||
|
import dns.exception
|
||||||
|
import dns.inet
|
||||||
|
|
||||||
|
_is_win32 = sys.platform == "win32"
|
||||||
|
|
||||||
|
|
||||||
|
def _get_running_loop():
|
||||||
|
try:
|
||||||
|
return asyncio.get_running_loop()
|
||||||
|
except AttributeError: # pragma: no cover
|
||||||
|
return asyncio.get_event_loop()
|
||||||
|
|
||||||
|
|
||||||
|
class _DatagramProtocol:
|
||||||
|
def __init__(self):
|
||||||
|
self.transport = None
|
||||||
|
self.recvfrom = None
|
||||||
|
|
||||||
|
def connection_made(self, transport):
|
||||||
|
self.transport = transport
|
||||||
|
|
||||||
|
def datagram_received(self, data, addr):
|
||||||
|
if self.recvfrom and not self.recvfrom.done():
|
||||||
|
self.recvfrom.set_result((data, addr))
|
||||||
|
|
||||||
|
def error_received(self, exc): # pragma: no cover
|
||||||
|
if self.recvfrom and not self.recvfrom.done():
|
||||||
|
self.recvfrom.set_exception(exc)
|
||||||
|
|
||||||
|
def connection_lost(self, exc):
|
||||||
|
if self.recvfrom and not self.recvfrom.done():
|
||||||
|
if exc is None:
|
||||||
|
# EOF we triggered. Is there a better way to do this?
|
||||||
|
try:
|
||||||
|
raise EOFError("EOF")
|
||||||
|
except EOFError as e:
|
||||||
|
self.recvfrom.set_exception(e)
|
||||||
|
else:
|
||||||
|
self.recvfrom.set_exception(exc)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.transport is not None:
|
||||||
|
self.transport.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def _maybe_wait_for(awaitable, timeout):
|
||||||
|
if timeout is not None:
|
||||||
|
try:
|
||||||
|
return await asyncio.wait_for(awaitable, timeout)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
raise dns.exception.Timeout(timeout=timeout)
|
||||||
|
else:
|
||||||
|
return await awaitable
|
||||||
|
|
||||||
|
|
||||||
|
class DatagramSocket(dns._asyncbackend.DatagramSocket):
|
||||||
|
def __init__(self, family, transport, protocol):
|
||||||
|
super().__init__(family, socket.SOCK_DGRAM)
|
||||||
|
self.transport = transport
|
||||||
|
self.protocol = protocol
|
||||||
|
|
||||||
|
async def sendto(self, what, destination, timeout): # pragma: no cover
|
||||||
|
# no timeout for asyncio sendto
|
||||||
|
self.transport.sendto(what, destination)
|
||||||
|
return len(what)
|
||||||
|
|
||||||
|
async def recvfrom(self, size, timeout):
|
||||||
|
# ignore size as there's no way I know to tell protocol about it
|
||||||
|
done = _get_running_loop().create_future()
|
||||||
|
try:
|
||||||
|
assert self.protocol.recvfrom is None
|
||||||
|
self.protocol.recvfrom = done
|
||||||
|
await _maybe_wait_for(done, timeout)
|
||||||
|
return done.result()
|
||||||
|
finally:
|
||||||
|
self.protocol.recvfrom = None
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
self.protocol.close()
|
||||||
|
|
||||||
|
async def getpeername(self):
|
||||||
|
return self.transport.get_extra_info("peername")
|
||||||
|
|
||||||
|
async def getsockname(self):
|
||||||
|
return self.transport.get_extra_info("sockname")
|
||||||
|
|
||||||
|
async def getpeercert(self, timeout):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class StreamSocket(dns._asyncbackend.StreamSocket):
|
||||||
|
def __init__(self, af, reader, writer):
|
||||||
|
super().__init__(af, socket.SOCK_STREAM)
|
||||||
|
self.reader = reader
|
||||||
|
self.writer = writer
|
||||||
|
|
||||||
|
async def sendall(self, what, timeout):
|
||||||
|
self.writer.write(what)
|
||||||
|
return await _maybe_wait_for(self.writer.drain(), timeout)
|
||||||
|
|
||||||
|
async def recv(self, size, timeout):
|
||||||
|
return await _maybe_wait_for(self.reader.read(size), timeout)
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
self.writer.close()
|
||||||
|
|
||||||
|
async def getpeername(self):
|
||||||
|
return self.writer.get_extra_info("peername")
|
||||||
|
|
||||||
|
async def getsockname(self):
|
||||||
|
return self.writer.get_extra_info("sockname")
|
||||||
|
|
||||||
|
async def getpeercert(self, timeout):
|
||||||
|
return self.writer.get_extra_info("peercert")
|
||||||
|
|
||||||
|
|
||||||
|
if dns._features.have("doh"):
|
||||||
|
import anyio
|
||||||
|
import httpcore
|
||||||
|
import httpcore._backends.anyio
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
_CoreAsyncNetworkBackend = httpcore.AsyncNetworkBackend
|
||||||
|
_CoreAnyIOStream = httpcore._backends.anyio.AnyIOStream # pyright: ignore
|
||||||
|
|
||||||
|
from dns.query import _compute_times, _expiration_for_this_attempt, _remaining
|
||||||
|
|
||||||
|
class _NetworkBackend(_CoreAsyncNetworkBackend):
|
||||||
|
def __init__(self, resolver, local_port, bootstrap_address, family):
|
||||||
|
super().__init__()
|
||||||
|
self._local_port = local_port
|
||||||
|
self._resolver = resolver
|
||||||
|
self._bootstrap_address = bootstrap_address
|
||||||
|
self._family = family
|
||||||
|
if local_port != 0:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"the asyncio transport for HTTPX cannot set the local port"
|
||||||
|
)
|
||||||
|
|
||||||
|
async def connect_tcp(
|
||||||
|
self, host, port, timeout=None, local_address=None, socket_options=None
|
||||||
|
): # pylint: disable=signature-differs
|
||||||
|
addresses = []
|
||||||
|
_, expiration = _compute_times(timeout)
|
||||||
|
if dns.inet.is_address(host):
|
||||||
|
addresses.append(host)
|
||||||
|
elif self._bootstrap_address is not None:
|
||||||
|
addresses.append(self._bootstrap_address)
|
||||||
|
else:
|
||||||
|
timeout = _remaining(expiration)
|
||||||
|
family = self._family
|
||||||
|
if local_address:
|
||||||
|
family = dns.inet.af_for_address(local_address)
|
||||||
|
answers = await self._resolver.resolve_name(
|
||||||
|
host, family=family, lifetime=timeout
|
||||||
|
)
|
||||||
|
addresses = answers.addresses()
|
||||||
|
for address in addresses:
|
||||||
|
try:
|
||||||
|
attempt_expiration = _expiration_for_this_attempt(2.0, expiration)
|
||||||
|
timeout = _remaining(attempt_expiration)
|
||||||
|
with anyio.fail_after(timeout):
|
||||||
|
stream = await anyio.connect_tcp(
|
||||||
|
remote_host=address,
|
||||||
|
remote_port=port,
|
||||||
|
local_host=local_address,
|
||||||
|
)
|
||||||
|
return _CoreAnyIOStream(stream)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
raise httpcore.ConnectError
|
||||||
|
|
||||||
|
async def connect_unix_socket(
|
||||||
|
self, path, timeout=None, socket_options=None
|
||||||
|
): # pylint: disable=signature-differs
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def sleep(self, seconds): # pylint: disable=signature-differs
|
||||||
|
await anyio.sleep(seconds)
|
||||||
|
|
||||||
|
class _HTTPTransport(httpx.AsyncHTTPTransport):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
local_port=0,
|
||||||
|
bootstrap_address=None,
|
||||||
|
resolver=None,
|
||||||
|
family=socket.AF_UNSPEC,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
if resolver is None and bootstrap_address is None:
|
||||||
|
# pylint: disable=import-outside-toplevel,redefined-outer-name
|
||||||
|
import dns.asyncresolver
|
||||||
|
|
||||||
|
resolver = dns.asyncresolver.Resolver()
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self._pool._network_backend = _NetworkBackend(
|
||||||
|
resolver, local_port, bootstrap_address, family
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
_HTTPTransport = dns._asyncbackend.NullTransport # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class Backend(dns._asyncbackend.Backend):
|
||||||
|
def name(self):
|
||||||
|
return "asyncio"
|
||||||
|
|
||||||
|
async def make_socket(
|
||||||
|
self,
|
||||||
|
af,
|
||||||
|
socktype,
|
||||||
|
proto=0,
|
||||||
|
source=None,
|
||||||
|
destination=None,
|
||||||
|
timeout=None,
|
||||||
|
ssl_context=None,
|
||||||
|
server_hostname=None,
|
||||||
|
):
|
||||||
|
loop = _get_running_loop()
|
||||||
|
if socktype == socket.SOCK_DGRAM:
|
||||||
|
if _is_win32 and source is None:
|
||||||
|
# Win32 wants explicit binding before recvfrom(). This is the
|
||||||
|
# proper fix for [#637].
|
||||||
|
source = (dns.inet.any_for_af(af), 0)
|
||||||
|
transport, protocol = await loop.create_datagram_endpoint(
|
||||||
|
_DatagramProtocol, # pyright: ignore
|
||||||
|
source,
|
||||||
|
family=af,
|
||||||
|
proto=proto,
|
||||||
|
remote_addr=destination,
|
||||||
|
)
|
||||||
|
return DatagramSocket(af, transport, protocol)
|
||||||
|
elif socktype == socket.SOCK_STREAM:
|
||||||
|
if destination is None:
|
||||||
|
# This shouldn't happen, but we check to make code analysis software
|
||||||
|
# happier.
|
||||||
|
raise ValueError("destination required for stream sockets")
|
||||||
|
(r, w) = await _maybe_wait_for(
|
||||||
|
asyncio.open_connection(
|
||||||
|
destination[0],
|
||||||
|
destination[1],
|
||||||
|
ssl=ssl_context,
|
||||||
|
family=af,
|
||||||
|
proto=proto,
|
||||||
|
local_addr=source,
|
||||||
|
server_hostname=server_hostname,
|
||||||
|
),
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
return StreamSocket(af, r, w)
|
||||||
|
raise NotImplementedError(
|
||||||
|
"unsupported socket " + f"type {socktype}"
|
||||||
|
) # pragma: no cover
|
||||||
|
|
||||||
|
async def sleep(self, interval):
|
||||||
|
await asyncio.sleep(interval)
|
||||||
|
|
||||||
|
def datagram_connection_required(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_transport_class(self):
|
||||||
|
return _HTTPTransport
|
||||||
|
|
||||||
|
async def wait_for(self, awaitable, timeout):
|
||||||
|
return await _maybe_wait_for(awaitable, timeout)
|
||||||
154
venv/Lib/site-packages/dns/_ddr.py
Normal file
154
venv/Lib/site-packages/dns/_ddr.py
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
#
|
||||||
|
# Support for Discovery of Designated Resolvers
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import dns.asyncbackend
|
||||||
|
import dns.inet
|
||||||
|
import dns.name
|
||||||
|
import dns.nameserver
|
||||||
|
import dns.query
|
||||||
|
import dns.rdtypes.svcbbase
|
||||||
|
|
||||||
|
# The special name of the local resolver when using DDR
|
||||||
|
_local_resolver_name = dns.name.from_text("_dns.resolver.arpa")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Processing is split up into I/O independent and I/O dependent parts to
|
||||||
|
# make supporting sync and async versions easy.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class _SVCBInfo:
|
||||||
|
def __init__(self, bootstrap_address, port, hostname, nameservers):
|
||||||
|
self.bootstrap_address = bootstrap_address
|
||||||
|
self.port = port
|
||||||
|
self.hostname = hostname
|
||||||
|
self.nameservers = nameservers
|
||||||
|
|
||||||
|
def ddr_check_certificate(self, cert):
|
||||||
|
"""Verify that the _SVCBInfo's address is in the cert's subjectAltName (SAN)"""
|
||||||
|
for name, value in cert["subjectAltName"]:
|
||||||
|
if name == "IP Address" and value == self.bootstrap_address:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def make_tls_context(self):
|
||||||
|
ssl = dns.query.ssl
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
def ddr_tls_check_sync(self, lifetime):
|
||||||
|
ctx = self.make_tls_context()
|
||||||
|
expiration = time.time() + lifetime
|
||||||
|
with socket.create_connection(
|
||||||
|
(self.bootstrap_address, self.port), lifetime
|
||||||
|
) as s:
|
||||||
|
with ctx.wrap_socket(s, server_hostname=self.hostname) as ts:
|
||||||
|
ts.settimeout(dns.query._remaining(expiration))
|
||||||
|
ts.do_handshake()
|
||||||
|
cert = ts.getpeercert()
|
||||||
|
return self.ddr_check_certificate(cert)
|
||||||
|
|
||||||
|
async def ddr_tls_check_async(self, lifetime, backend=None):
|
||||||
|
if backend is None:
|
||||||
|
backend = dns.asyncbackend.get_default_backend()
|
||||||
|
ctx = self.make_tls_context()
|
||||||
|
expiration = time.time() + lifetime
|
||||||
|
async with await backend.make_socket(
|
||||||
|
dns.inet.af_for_address(self.bootstrap_address),
|
||||||
|
socket.SOCK_STREAM,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
(self.bootstrap_address, self.port),
|
||||||
|
lifetime,
|
||||||
|
ctx,
|
||||||
|
self.hostname,
|
||||||
|
) as ts:
|
||||||
|
cert = await ts.getpeercert(dns.query._remaining(expiration))
|
||||||
|
return self.ddr_check_certificate(cert)
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_nameservers_from_svcb(answer):
|
||||||
|
bootstrap_address = answer.nameserver
|
||||||
|
if not dns.inet.is_address(bootstrap_address):
|
||||||
|
return []
|
||||||
|
infos = []
|
||||||
|
for rr in answer.rrset.processing_order():
|
||||||
|
nameservers = []
|
||||||
|
param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.ALPN)
|
||||||
|
if param is None:
|
||||||
|
continue
|
||||||
|
alpns = set(param.ids)
|
||||||
|
host = rr.target.to_text(omit_final_dot=True)
|
||||||
|
port = None
|
||||||
|
param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.PORT)
|
||||||
|
if param is not None:
|
||||||
|
port = param.port
|
||||||
|
# For now we ignore address hints and address resolution and always use the
|
||||||
|
# bootstrap address
|
||||||
|
if b"h2" in alpns:
|
||||||
|
param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.DOHPATH)
|
||||||
|
if param is None or not param.value.endswith(b"{?dns}"):
|
||||||
|
continue
|
||||||
|
path = param.value[:-6].decode()
|
||||||
|
if not path.startswith("/"):
|
||||||
|
path = "/" + path
|
||||||
|
if port is None:
|
||||||
|
port = 443
|
||||||
|
url = f"https://{host}:{port}{path}"
|
||||||
|
# check the URL
|
||||||
|
try:
|
||||||
|
urlparse(url)
|
||||||
|
nameservers.append(dns.nameserver.DoHNameserver(url, bootstrap_address))
|
||||||
|
except Exception:
|
||||||
|
# continue processing other ALPN types
|
||||||
|
pass
|
||||||
|
if b"dot" in alpns:
|
||||||
|
if port is None:
|
||||||
|
port = 853
|
||||||
|
nameservers.append(
|
||||||
|
dns.nameserver.DoTNameserver(bootstrap_address, port, host)
|
||||||
|
)
|
||||||
|
if b"doq" in alpns:
|
||||||
|
if port is None:
|
||||||
|
port = 853
|
||||||
|
nameservers.append(
|
||||||
|
dns.nameserver.DoQNameserver(bootstrap_address, port, True, host)
|
||||||
|
)
|
||||||
|
if len(nameservers) > 0:
|
||||||
|
infos.append(_SVCBInfo(bootstrap_address, port, host, nameservers))
|
||||||
|
return infos
|
||||||
|
|
||||||
|
|
||||||
|
def _get_nameservers_sync(answer, lifetime):
|
||||||
|
"""Return a list of TLS-validated resolver nameservers extracted from an SVCB
|
||||||
|
answer."""
|
||||||
|
nameservers = []
|
||||||
|
infos = _extract_nameservers_from_svcb(answer)
|
||||||
|
for info in infos:
|
||||||
|
try:
|
||||||
|
if info.ddr_tls_check_sync(lifetime):
|
||||||
|
nameservers.extend(info.nameservers)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return nameservers
|
||||||
|
|
||||||
|
|
||||||
|
async def _get_nameservers_async(answer, lifetime):
|
||||||
|
"""Return a list of TLS-validated resolver nameservers extracted from an SVCB
|
||||||
|
answer."""
|
||||||
|
nameservers = []
|
||||||
|
infos = _extract_nameservers_from_svcb(answer)
|
||||||
|
for info in infos:
|
||||||
|
try:
|
||||||
|
if await info.ddr_tls_check_async(lifetime):
|
||||||
|
nameservers.extend(info.nameservers)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return nameservers
|
||||||
95
venv/Lib/site-packages/dns/_features.py
Normal file
95
venv/Lib/site-packages/dns/_features.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
|
import importlib.metadata
|
||||||
|
import itertools
|
||||||
|
import string
|
||||||
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
def _tuple_from_text(version: str) -> Tuple:
|
||||||
|
text_parts = version.split(".")
|
||||||
|
int_parts = []
|
||||||
|
for text_part in text_parts:
|
||||||
|
digit_prefix = "".join(
|
||||||
|
itertools.takewhile(lambda x: x in string.digits, text_part)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
int_parts.append(int(digit_prefix))
|
||||||
|
except Exception:
|
||||||
|
break
|
||||||
|
return tuple(int_parts)
|
||||||
|
|
||||||
|
|
||||||
|
def _version_check(
|
||||||
|
requirement: str,
|
||||||
|
) -> bool:
|
||||||
|
"""Is the requirement fulfilled?
|
||||||
|
|
||||||
|
The requirement must be of the form
|
||||||
|
|
||||||
|
package>=version
|
||||||
|
"""
|
||||||
|
package, minimum = requirement.split(">=")
|
||||||
|
try:
|
||||||
|
version = importlib.metadata.version(package)
|
||||||
|
# This shouldn't happen, but it apparently can.
|
||||||
|
if version is None:
|
||||||
|
return False
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
t_version = _tuple_from_text(version)
|
||||||
|
t_minimum = _tuple_from_text(minimum)
|
||||||
|
if t_version < t_minimum:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
_cache: Dict[str, bool] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def have(feature: str) -> bool:
|
||||||
|
"""Is *feature* available?
|
||||||
|
|
||||||
|
This tests if all optional packages needed for the
|
||||||
|
feature are available and recent enough.
|
||||||
|
|
||||||
|
Returns ``True`` if the feature is available,
|
||||||
|
and ``False`` if it is not or if metadata is
|
||||||
|
missing.
|
||||||
|
"""
|
||||||
|
value = _cache.get(feature)
|
||||||
|
if value is not None:
|
||||||
|
return value
|
||||||
|
requirements = _requirements.get(feature)
|
||||||
|
if requirements is None:
|
||||||
|
# we make a cache entry here for consistency not performance
|
||||||
|
_cache[feature] = False
|
||||||
|
return False
|
||||||
|
ok = True
|
||||||
|
for requirement in requirements:
|
||||||
|
if not _version_check(requirement):
|
||||||
|
ok = False
|
||||||
|
break
|
||||||
|
_cache[feature] = ok
|
||||||
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
def force(feature: str, enabled: bool) -> None:
|
||||||
|
"""Force the status of *feature* to be *enabled*.
|
||||||
|
|
||||||
|
This method is provided as a workaround for any cases
|
||||||
|
where importlib.metadata is ineffective, or for testing.
|
||||||
|
"""
|
||||||
|
_cache[feature] = enabled
|
||||||
|
|
||||||
|
|
||||||
|
_requirements: Dict[str, List[str]] = {
|
||||||
|
### BEGIN generated requirements
|
||||||
|
"dnssec": ["cryptography>=45"],
|
||||||
|
"doh": ["httpcore>=1.0.0", "httpx>=0.28.0", "h2>=4.2.0"],
|
||||||
|
"doq": ["aioquic>=1.2.0"],
|
||||||
|
"idna": ["idna>=3.10"],
|
||||||
|
"trio": ["trio>=0.30"],
|
||||||
|
"wmi": ["wmi>=1.5.1"],
|
||||||
|
### END generated requirements
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user