Bump dnspython from 2.3.0 to 2.4.2 (#2123)

* Bump dnspython from 2.3.0 to 2.4.2

Bumps [dnspython](https://github.com/rthalley/dnspython) from 2.3.0 to 2.4.2.
- [Release notes](https://github.com/rthalley/dnspython/releases)
- [Changelog](https://github.com/rthalley/dnspython/blob/master/doc/whatsnew.rst)
- [Commits](https://github.com/rthalley/dnspython/compare/v2.3.0...v2.4.2)

---
updated-dependencies:
- dependency-name: dnspython
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update dnspython==2.4.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
This commit is contained in:
dependabot[bot] 2023-08-24 12:05:11 -07:00 committed by GitHub
parent 9f00f5dafa
commit c0aa4e4996
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
108 changed files with 2985 additions and 1136 deletions

View file

@ -17,8 +17,6 @@
"""Talk to a DNS server."""
from typing import Any, Dict, Optional, Tuple, Union
import base64
import contextlib
import enum
@ -28,12 +26,12 @@ import selectors
import socket
import struct
import time
import urllib.parse
from typing import Any, Dict, Optional, Tuple, Union
import dns.exception
import dns.inet
import dns.name
import dns.message
import dns.name
import dns.quic
import dns.rcode
import dns.rdataclass
@ -43,20 +41,32 @@ import dns.transaction
import dns.tsig
import dns.xfr
try:
import requests
from requests_toolbelt.adapters.source import SourceAddressAdapter
from requests_toolbelt.adapters.host_header_ssl import HostHeaderSSLAdapter
_have_requests = True
except ImportError: # pragma: no cover
_have_requests = False
def _remaining(expiration):
if expiration is None:
return None
timeout = expiration - time.time()
if timeout <= 0.0:
raise dns.exception.Timeout
return timeout
def _expiration_for_this_attempt(timeout, expiration):
if expiration is None:
return None
return min(time.time() + timeout, expiration)
_have_httpx = False
_have_http2 = False
try:
import httpcore
import httpcore._backends.sync
import httpx
_CoreNetworkBackend = httpcore.NetworkBackend
_CoreSyncStream = httpcore._backends.sync.SyncStream
_have_httpx = True
try:
# See if http2 support is available.
@ -64,10 +74,87 @@ try:
_have_http2 = True
except Exception:
pass
except ImportError: # pragma: no cover
pass
have_doh = _have_requests or _have_httpx
class _NetworkBackend(_CoreNetworkBackend):
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
def connect_tcp(
self, host, port, timeout, local_address, 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 = self._resolver.resolve_name(
host, family=family, lifetime=timeout
)
addresses = answers.addresses()
for address in addresses:
af = dns.inet.af_for_address(address)
if local_address is not None or self._local_port != 0:
source = dns.inet.low_level_address_tuple(
(local_address, self._local_port), af
)
else:
source = None
sock = _make_socket(af, socket.SOCK_STREAM, source)
attempt_expiration = _expiration_for_this_attempt(2.0, expiration)
try:
_connect(
sock,
dns.inet.low_level_address_tuple((address, port), af),
attempt_expiration,
)
return _CoreSyncStream(sock)
except Exception:
pass
raise httpcore.ConnectError
def connect_unix_socket(
self, path, timeout, socket_options=None
): # pylint: disable=signature-differs
raise NotImplementedError
class _HTTPTransport(httpx.HTTPTransport):
def __init__(
self,
*args,
local_port=0,
bootstrap_address=None,
resolver=None,
family=socket.AF_UNSPEC,
**kwargs,
):
if resolver is None:
# pylint: disable=import-outside-toplevel,redefined-outer-name
import dns.resolver
resolver = dns.resolver.Resolver()
super().__init__(*args, **kwargs)
self._pool._network_backend = _NetworkBackend(
resolver, local_port, bootstrap_address, family
)
except ImportError: # pragma: no cover
class _HTTPTransport: # type: ignore
def connect_tcp(self, host, port, timeout, local_address):
raise NotImplementedError
have_doh = _have_httpx
try:
import ssl
@ -88,7 +175,7 @@ except ImportError: # pragma: no cover
@classmethod
def create_default_context(cls, *args, **kwargs):
raise Exception("no ssl support")
raise Exception("no ssl support") # pylint: disable=broad-exception-raised
# Function used to create a socket. Can be overridden if needed in special
@ -105,7 +192,7 @@ class BadResponse(dns.exception.FormError):
class NoDOH(dns.exception.DNSException):
"""DNS over HTTPS (DOH) was requested but the requests module is not
"""DNS over HTTPS (DOH) was requested but the httpx module is not
available."""
@ -230,7 +317,7 @@ def _destination_and_source(
# We know the destination af, so source had better agree!
if saf != af:
raise ValueError(
"different address families for source " + "and destination"
"different address families for source and destination"
)
else:
# We didn't know the destination af, but we know the source,
@ -240,11 +327,10 @@ def _destination_and_source(
# Caller has specified a source_port but not an address, so we
# need to return a source, and we need to use the appropriate
# wildcard address as the address.
if af == socket.AF_INET:
source = "0.0.0.0"
elif af == socket.AF_INET6:
source = "::"
else:
try:
source = dns.inet.any_for_af(af)
except Exception:
# we catch this and raise ValueError for backwards compatibility
raise ValueError("source_port specified but address family is unknown")
# Convert high-level (address, port) tuples into low-level address
# tuples.
@ -289,6 +375,8 @@ def https(
post: bool = True,
bootstrap_address: Optional[str] = None,
verify: Union[bool, str] = True,
resolver: Optional["dns.resolver.Resolver"] = None,
family: Optional[int] = socket.AF_UNSPEC,
) -> dns.message.Message:
"""Return the response obtained after sending a query via DNS-over-HTTPS.
@ -314,91 +402,78 @@ def https(
*ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the
received message.
*session*, an ``httpx.Client`` or ``requests.session.Session``. If provided, the
client/session to use to send the queries.
*session*, an ``httpx.Client``. If provided, the client session to use to send the
queries.
*path*, a ``str``. If *where* is an IP address, then *path* will be used to
construct the URL to send the DNS query to.
*post*, a ``bool``. If ``True``, the default, POST method will be used.
*bootstrap_address*, a ``str``, the IP address to use to bypass the system's DNS
resolver.
*bootstrap_address*, a ``str``, the IP address to use to bypass resolution.
*verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification
of the server is done using the default CA bundle; if ``False``, then no
verification is done; if a `str` then it specifies the path to a certificate file or
directory which will be used for verification.
*resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use for
resolution of hostnames in URLs. If not specified, a new resolver with a default
configuration will be used; note this is *not* the default resolver as that resolver
might have been configured to use DoH causing a chicken-and-egg problem. This
parameter only has an effect if the HTTP library is httpx.
*family*, an ``int``, the address family. If socket.AF_UNSPEC (the default), both A
and AAAA records will be retrieved.
Returns a ``dns.message.Message``.
"""
if not have_doh:
raise NoDOH("Neither httpx nor requests is available.") # pragma: no cover
_httpx_ok = _have_httpx
raise NoDOH # pragma: no cover
if session and not isinstance(session, httpx.Client):
raise ValueError("session parameter must be an httpx.Client")
wire = q.to_wire()
(af, _, source) = _destination_and_source(where, port, source, source_port, False)
transport_adapter = None
(af, _, the_source) = _destination_and_source(
where, port, source, source_port, False
)
transport = None
headers = {"accept": "application/dns-message"}
if af is not None:
if af is not None and dns.inet.is_address(where):
if af == socket.AF_INET:
url = "https://{}:{}{}".format(where, port, path)
elif af == socket.AF_INET6:
url = "https://[{}]:{}{}".format(where, port, path)
elif bootstrap_address is not None:
_httpx_ok = False
split_url = urllib.parse.urlsplit(where)
if split_url.hostname is None:
raise ValueError("DoH URL has no hostname")
headers["Host"] = split_url.hostname
url = where.replace(split_url.hostname, bootstrap_address)
if _have_requests:
transport_adapter = HostHeaderSSLAdapter()
else:
url = where
if source is not None:
# set source port and source address
if _have_httpx:
if source_port == 0:
transport = httpx.HTTPTransport(local_address=source[0], verify=verify)
else:
_httpx_ok = False
if _have_requests:
transport_adapter = SourceAddressAdapter(source)
if session:
if _have_httpx:
_is_httpx = isinstance(session, httpx.Client)
else:
_is_httpx = False
if _is_httpx and not _httpx_ok:
raise NoDOH(
"Session is httpx, but httpx cannot be used for "
"the requested operation."
)
# set source port and source address
if the_source is None:
local_address = None
local_port = 0
else:
_is_httpx = _httpx_ok
if not _httpx_ok and not _have_requests:
raise NoDOH(
"Cannot use httpx for this operation, and requests is not available."
)
local_address = the_source[0]
local_port = the_source[1]
transport = _HTTPTransport(
local_address=local_address,
http1=True,
http2=_have_http2,
verify=verify,
local_port=local_port,
bootstrap_address=bootstrap_address,
resolver=resolver,
family=family,
)
if session:
cm: contextlib.AbstractContextManager = contextlib.nullcontext(session)
elif _is_httpx:
else:
cm = httpx.Client(
http1=True, http2=_have_http2, verify=verify, transport=transport
)
else:
cm = requests.sessions.Session()
with cm as session:
if transport_adapter and not _is_httpx:
session.mount(url, transport_adapter)
# see https://tools.ietf.org/html/rfc8484#section-4.1.1 for DoH
# GET and POST examples
if post:
@ -408,29 +483,13 @@ def https(
"content-length": str(len(wire)),
}
)
if _is_httpx:
response = session.post(
url, headers=headers, content=wire, timeout=timeout
)
else:
response = session.post(
url, headers=headers, data=wire, timeout=timeout, verify=verify
)
response = session.post(url, headers=headers, content=wire, timeout=timeout)
else:
wire = base64.urlsafe_b64encode(wire).rstrip(b"=")
if _is_httpx:
twire = wire.decode() # httpx does a repr() if we give it bytes
response = session.get(
url, headers=headers, timeout=timeout, params={"dns": twire}
)
else:
response = session.get(
url,
headers=headers,
timeout=timeout,
verify=verify,
params={"dns": wire},
)
twire = wire.decode() # httpx does a repr() if we give it bytes
response = session.get(
url, headers=headers, timeout=timeout, params={"dns": twire}
)
# see https://tools.ietf.org/html/rfc8484#section-4.2.1 for info about DoH
# status codes
@ -1070,6 +1129,7 @@ def quic(
ignore_trailing: bool = False,
connection: Optional[dns.quic.SyncQuicConnection] = None,
verify: Union[bool, str] = True,
server_hostname: Optional[str] = None,
) -> dns.message.Message:
"""Return the response obtained after sending a query via DNS-over-QUIC.
@ -1101,6 +1161,10 @@ def quic(
verification is done; if a `str` then it specifies the path to a certificate file or
directory which will be used for verification.
*server_hostname*, a ``str`` containing the server's hostname. The
default is ``None``, which means that no hostname is known, and if an
SSL context is created, hostname checking will be disabled.
Returns a ``dns.message.Message``.
"""
@ -1115,16 +1179,18 @@ def quic(
manager: contextlib.AbstractContextManager = contextlib.nullcontext(None)
the_connection = connection
else:
manager = dns.quic.SyncQuicManager(verify_mode=verify)
manager = dns.quic.SyncQuicManager(
verify_mode=verify, server_name=server_hostname
)
the_manager = manager # for type checking happiness
with manager:
if not connection:
the_connection = the_manager.connect(where, port, source, source_port)
start = time.time()
with the_connection.make_stream() as stream:
(start, expiration) = _compute_times(timeout)
with the_connection.make_stream(timeout) as stream:
stream.send(wire, True)
wire = stream.receive(timeout)
wire = stream.receive(_remaining(expiration))
finish = time.time()
r = dns.message.from_wire(
wire,