mit neuen venv und exe-Files
This commit is contained in:
355
venv3_12/Lib/site-packages/gevent/resolver/ares.py
Normal file
355
venv3_12/Lib/site-packages/gevent/resolver/ares.py
Normal file
@@ -0,0 +1,355 @@
|
||||
# Copyright (c) 2011-2015 Denis Bilenko. See LICENSE for details.
|
||||
"""
|
||||
c-ares based hostname resolver.
|
||||
"""
|
||||
from __future__ import absolute_import, print_function, division
|
||||
import os
|
||||
import warnings
|
||||
|
||||
from _socket import gaierror
|
||||
from _socket import herror
|
||||
from _socket import error
|
||||
from _socket import EAI_NONAME
|
||||
|
||||
from gevent._compat import text_type
|
||||
from gevent._compat import integer_types
|
||||
|
||||
from gevent.hub import Waiter
|
||||
from gevent.hub import get_hub
|
||||
|
||||
from gevent.socket import AF_UNSPEC
|
||||
from gevent.socket import AF_INET
|
||||
from gevent.socket import AF_INET6
|
||||
from gevent.socket import SOCK_DGRAM
|
||||
from gevent.socket import SOCK_STREAM
|
||||
from gevent.socket import SOL_TCP
|
||||
from gevent.socket import SOL_UDP
|
||||
|
||||
|
||||
from gevent._config import config
|
||||
from gevent._config import AresSettingMixin
|
||||
|
||||
from .cares import channel, InvalidIP # pylint:disable=import-error,no-name-in-module
|
||||
from . import _lookup_port as lookup_port
|
||||
from . import AbstractResolver
|
||||
|
||||
__all__ = ['Resolver']
|
||||
|
||||
|
||||
class Resolver(AbstractResolver):
|
||||
"""
|
||||
Implementation of the resolver API using the `c-ares`_ library.
|
||||
|
||||
This implementation uses the c-ares library to handle name
|
||||
resolution. c-ares is natively asynchronous at the socket level
|
||||
and so integrates well into gevent's event loop.
|
||||
|
||||
In comparison to :class:`gevent.resolver_thread.Resolver` (which
|
||||
delegates to the native system resolver), the implementation is
|
||||
much more complex. In addition, there have been reports of it not
|
||||
properly honoring certain system configurations (for example, the
|
||||
order in which IPv4 and IPv6 results are returned may not match
|
||||
the threaded resolver). However, because it does not use threads,
|
||||
it may scale better for applications that make many lookups.
|
||||
|
||||
There are some known differences from the system resolver.
|
||||
|
||||
- ``gethostbyname_ex`` and ``gethostbyaddr`` may return
|
||||
different for the ``aliaslist`` tuple member. (Sometimes the
|
||||
same, sometimes in a different order, sometimes a different
|
||||
alias altogether.)
|
||||
|
||||
- ``gethostbyname_ex`` may return the ``ipaddrlist`` in a
|
||||
different order.
|
||||
|
||||
- ``getaddrinfo`` does not return ``SOCK_RAW`` results.
|
||||
|
||||
- ``getaddrinfo`` may return results in a different order.
|
||||
|
||||
- Handling of ``.local`` (mDNS) names may be different, even
|
||||
if they are listed in the hosts file.
|
||||
|
||||
- c-ares will not resolve ``broadcasthost``, even if listed in
|
||||
the hosts file prior to 2020-04-30.
|
||||
|
||||
- This implementation may raise ``gaierror(4)`` where the
|
||||
system implementation would raise ``herror(1)`` or vice versa,
|
||||
with different error numbers. However, after 2020-04-30, this should be
|
||||
much reduced.
|
||||
|
||||
- The results for ``localhost`` may be different. In
|
||||
particular, some system resolvers will return more results
|
||||
from ``getaddrinfo`` than c-ares does, such as SOCK_DGRAM
|
||||
results, and c-ares may report more ips on a multi-homed
|
||||
host.
|
||||
|
||||
- The system implementation may return some names fully qualified, where
|
||||
this implementation returns only the host name. This appears to be
|
||||
the case only with entries found in ``/etc/hosts``.
|
||||
|
||||
- c-ares supports a limited set of flags for ``getnameinfo`` and
|
||||
``getaddrinfo``; unknown flags are ignored. System-specific flags
|
||||
such as ``AI_V4MAPPED_CFG`` are not supported.
|
||||
|
||||
- ``getaddrinfo`` may return canonical names even without the ``AI_CANONNAME``
|
||||
being set.
|
||||
|
||||
- ``getaddrinfo`` does not appear to support IPv6 symbolic scope IDs.
|
||||
|
||||
.. caution::
|
||||
|
||||
This module is considered extremely experimental on PyPy, and
|
||||
due to its implementation in cython, it may be slower. It may also lead to
|
||||
interpreter crashes.
|
||||
|
||||
.. versionchanged:: 1.5.0
|
||||
This version of gevent typically embeds c-ares 1.15.0 or newer. In
|
||||
that version of c-ares, domains ending in ``.onion`` `are never
|
||||
resolved <https://github.com/c-ares/c-ares/issues/196>`_ or even
|
||||
sent to the DNS server.
|
||||
|
||||
.. versionchanged:: 20.5.0
|
||||
``getaddrinfo`` is now implemented using the native c-ares function
|
||||
from c-ares 1.16 or newer.
|
||||
|
||||
.. versionchanged:: 20.5.0
|
||||
Now ``herror`` and ``gaierror`` are raised more consistently with
|
||||
the standard library resolver, and have more consistent errno values.
|
||||
|
||||
Handling of localhost and broadcast names is now more consistent.
|
||||
|
||||
.. versionchanged:: 22.10.1
|
||||
Now has a ``__del__`` method that warns if the object is destroyed
|
||||
without being properly closed.
|
||||
|
||||
.. _c-ares: http://c-ares.haxx.se
|
||||
"""
|
||||
|
||||
cares_class = channel
|
||||
|
||||
def __init__(self, hub=None, use_environ=True, **kwargs):
|
||||
AbstractResolver.__init__(self)
|
||||
if hub is None:
|
||||
hub = get_hub()
|
||||
self.hub = hub
|
||||
if use_environ:
|
||||
for setting in config.settings.values():
|
||||
if isinstance(setting, AresSettingMixin):
|
||||
value = setting.get()
|
||||
if value is not None:
|
||||
kwargs.setdefault(setting.kwarg_name, value)
|
||||
self.cares = self.cares_class(hub.loop, **kwargs)
|
||||
self.pid = os.getpid()
|
||||
self.params = kwargs
|
||||
self.fork_watcher = hub.loop.fork(ref=False) # We shouldn't keep the loop alive
|
||||
self.fork_watcher.start(self._on_fork)
|
||||
|
||||
def __repr__(self):
|
||||
return '<gevent.resolver_ares.Resolver at 0x%x ares=%r>' % (id(self), self.cares)
|
||||
|
||||
def _on_fork(self):
|
||||
# NOTE: See comment in gevent.hub.reinit.
|
||||
pid = os.getpid()
|
||||
if pid != self.pid:
|
||||
self.hub.loop.run_callback(self.cares.destroy)
|
||||
self.cares = self.cares_class(self.hub.loop, **self.params)
|
||||
self.pid = pid
|
||||
|
||||
def close(self):
|
||||
AbstractResolver.close(self)
|
||||
if self.cares is not None:
|
||||
self.hub.loop.run_callback(self.cares.destroy)
|
||||
self.cares = None
|
||||
self.fork_watcher.stop()
|
||||
|
||||
def __del__(self):
|
||||
if self.cares is not None:
|
||||
warnings.warn("cares Resolver destroyed while not closed",
|
||||
ResourceWarning)
|
||||
self.close()
|
||||
|
||||
def _gethostbyname_ex(self, hostname_bytes, family):
|
||||
while True:
|
||||
ares = self.cares
|
||||
try:
|
||||
waiter = Waiter(self.hub)
|
||||
ares.gethostbyname(waiter, hostname_bytes, family)
|
||||
result = waiter.get()
|
||||
if not result[-1]:
|
||||
raise herror(EAI_NONAME, self.EAI_NONAME_MSG)
|
||||
return result
|
||||
except herror as ex:
|
||||
if ares is self.cares:
|
||||
if ex.args[0] == 1:
|
||||
# Somewhere along the line, the internal
|
||||
# implementation of gethostbyname_ex changed to invoke
|
||||
# getaddrinfo() as a first pass, much like we do for ``getnameinfo()``;
|
||||
# this means it raises a different error for not-found hosts.
|
||||
raise gaierror(EAI_NONAME, self.EAI_NONAME_MSG)
|
||||
raise
|
||||
# "self.cares is not ares" means channel was destroyed (because we were forked)
|
||||
|
||||
def _lookup_port(self, port, socktype):
|
||||
return lookup_port(port, socktype)
|
||||
|
||||
def __getaddrinfo(
|
||||
self, host, port,
|
||||
family=0, socktype=0, proto=0, flags=0,
|
||||
fill_in_type_proto=True
|
||||
):
|
||||
"""
|
||||
Returns a list ``(family, socktype, proto, canonname, sockaddr)``
|
||||
|
||||
:raises gaierror: If no results are found.
|
||||
"""
|
||||
# pylint:disable=too-many-locals,too-many-branches
|
||||
if isinstance(host, text_type):
|
||||
host = host.encode('idna')
|
||||
|
||||
|
||||
if isinstance(port, text_type):
|
||||
port = port.encode('ascii')
|
||||
elif isinstance(port, integer_types):
|
||||
if port == 0:
|
||||
port = None
|
||||
else:
|
||||
port = str(port).encode('ascii')
|
||||
|
||||
waiter = Waiter(self.hub)
|
||||
self.cares.getaddrinfo(
|
||||
waiter,
|
||||
host,
|
||||
port,
|
||||
family,
|
||||
socktype,
|
||||
proto,
|
||||
flags,
|
||||
)
|
||||
# Result is a list of:
|
||||
# (family, socktype, proto, canonname, sockaddr)
|
||||
# Where sockaddr depends on family; for INET it is
|
||||
# (address, port)
|
||||
# and INET6 is
|
||||
# (address, port, flow info, scope id)
|
||||
result = waiter.get()
|
||||
|
||||
if not result:
|
||||
raise gaierror(EAI_NONAME, self.EAI_NONAME_MSG)
|
||||
|
||||
if fill_in_type_proto:
|
||||
# c-ares 1.16 DOES NOT fill in socktype or proto in the results,
|
||||
# ever. It's at least supposed to do that if they were given as
|
||||
# hints, but it doesn't (https://github.com/c-ares/c-ares/issues/317)
|
||||
# Sigh.
|
||||
# The SOL_* constants are another (older?) name for IPPROTO_*
|
||||
if socktype:
|
||||
hard_type_proto = [
|
||||
(socktype, SOL_TCP if socktype == SOCK_STREAM else SOL_UDP),
|
||||
]
|
||||
elif proto:
|
||||
hard_type_proto = [
|
||||
(SOCK_STREAM if proto == SOL_TCP else SOCK_DGRAM, proto),
|
||||
]
|
||||
else:
|
||||
hard_type_proto = [
|
||||
(SOCK_STREAM, SOL_TCP),
|
||||
(SOCK_DGRAM, SOL_UDP),
|
||||
]
|
||||
|
||||
# pylint:disable=not-an-iterable,unsubscriptable-object
|
||||
result = [
|
||||
(rfamily,
|
||||
hard_type if not rtype else rtype,
|
||||
hard_proto if not rproto else rproto,
|
||||
rcanon,
|
||||
raddr)
|
||||
for rfamily, rtype, rproto, rcanon, raddr
|
||||
in result
|
||||
for hard_type, hard_proto
|
||||
in hard_type_proto
|
||||
]
|
||||
return result
|
||||
|
||||
def _getaddrinfo(self, host_bytes, port, family, socktype, proto, flags):
|
||||
while True:
|
||||
ares = self.cares
|
||||
try:
|
||||
return self.__getaddrinfo(host_bytes, port, family, socktype, proto, flags)
|
||||
except gaierror:
|
||||
if ares is self.cares:
|
||||
raise
|
||||
|
||||
def __gethostbyaddr(self, ip_address):
|
||||
waiter = Waiter(self.hub)
|
||||
try:
|
||||
self.cares.gethostbyaddr(waiter, ip_address)
|
||||
return waiter.get()
|
||||
except InvalidIP:
|
||||
result = self._getaddrinfo(ip_address, None,
|
||||
family=AF_UNSPEC, socktype=SOCK_DGRAM,
|
||||
proto=0, flags=0)
|
||||
if not result:
|
||||
raise
|
||||
# pylint:disable=unsubscriptable-object
|
||||
_ip_address = result[0][-1][0]
|
||||
if isinstance(_ip_address, text_type):
|
||||
_ip_address = _ip_address.encode('ascii')
|
||||
if _ip_address == ip_address:
|
||||
raise
|
||||
waiter.clear()
|
||||
self.cares.gethostbyaddr(waiter, _ip_address)
|
||||
return waiter.get()
|
||||
|
||||
def _gethostbyaddr(self, ip_address_bytes):
|
||||
while True:
|
||||
ares = self.cares
|
||||
try:
|
||||
return self.__gethostbyaddr(ip_address_bytes)
|
||||
except herror:
|
||||
if ares is self.cares:
|
||||
raise
|
||||
|
||||
def __getnameinfo(self, hostname, port, sockaddr, flags):
|
||||
result = self.__getaddrinfo(
|
||||
hostname, port,
|
||||
family=AF_UNSPEC, socktype=SOCK_DGRAM,
|
||||
proto=0, flags=0,
|
||||
fill_in_type_proto=False)
|
||||
if len(result) != 1:
|
||||
raise error('sockaddr resolved to multiple addresses')
|
||||
|
||||
family, _socktype, _proto, _name, address = result[0]
|
||||
|
||||
if family == AF_INET:
|
||||
if len(sockaddr) != 2:
|
||||
raise error("IPv4 sockaddr must be 2 tuple")
|
||||
elif family == AF_INET6:
|
||||
address = address[:2] + sockaddr[2:]
|
||||
|
||||
waiter = Waiter(self.hub)
|
||||
self.cares.getnameinfo(waiter, address, flags)
|
||||
node, service = waiter.get()
|
||||
|
||||
if service is None:
|
||||
# ares docs: "If the query did not complete
|
||||
# successfully, or one of the values was not
|
||||
# requested, node or service will be NULL ". Python 2
|
||||
# allows that for the service, but Python 3 raises
|
||||
# an error. This is tested by test_socket in py 3.4
|
||||
err = gaierror(EAI_NONAME, self.EAI_NONAME_MSG)
|
||||
err.errno = EAI_NONAME
|
||||
raise err
|
||||
|
||||
return node, service or '0'
|
||||
|
||||
def _getnameinfo(self, address_bytes, port, sockaddr, flags):
|
||||
while True:
|
||||
ares = self.cares
|
||||
try:
|
||||
return self.__getnameinfo(address_bytes, port, sockaddr, flags)
|
||||
except gaierror:
|
||||
if ares is self.cares:
|
||||
raise
|
||||
|
||||
# # Things that need proper error handling
|
||||
# gethostbyaddr = AbstractResolver.convert_gaierror_to_herror(AbstractResolver.gethostbyaddr)
|
||||
Reference in New Issue
Block a user