Spaces:
Runtime error
Runtime error
| import asyncio | |
| import socket | |
| from typing import Any, Dict, List, Optional, Type, Union | |
| from .abc import AbstractResolver | |
| from .helpers import get_running_loop | |
| __all__ = ("ThreadedResolver", "AsyncResolver", "DefaultResolver") | |
| try: | |
| import aiodns | |
| # aiodns_default = hasattr(aiodns.DNSResolver, 'gethostbyname') | |
| except ImportError: # pragma: no cover | |
| aiodns = None | |
| aiodns_default = False | |
| class ThreadedResolver(AbstractResolver): | |
| """Threaded resolver. | |
| Uses an Executor for synchronous getaddrinfo() calls. | |
| concurrent.futures.ThreadPoolExecutor is used by default. | |
| """ | |
| def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: | |
| self._loop = get_running_loop(loop) | |
| async def resolve( | |
| self, hostname: str, port: int = 0, family: int = socket.AF_INET | |
| ) -> List[Dict[str, Any]]: | |
| infos = await self._loop.getaddrinfo( | |
| hostname, | |
| port, | |
| type=socket.SOCK_STREAM, | |
| family=family, | |
| flags=socket.AI_ADDRCONFIG, | |
| ) | |
| hosts = [] | |
| for family, _, proto, _, address in infos: | |
| if family == socket.AF_INET6: | |
| if len(address) < 3: | |
| # IPv6 is not supported by Python build, | |
| # or IPv6 is not enabled in the host | |
| continue | |
| if address[3]: | |
| # This is essential for link-local IPv6 addresses. | |
| # LL IPv6 is a VERY rare case. Strictly speaking, we should use | |
| # getnameinfo() unconditionally, but performance makes sense. | |
| host, _port = socket.getnameinfo( | |
| address, socket.NI_NUMERICHOST | socket.NI_NUMERICSERV | |
| ) | |
| port = int(_port) | |
| else: | |
| host, port = address[:2] | |
| else: # IPv4 | |
| assert family == socket.AF_INET | |
| host, port = address # type: ignore[misc] | |
| hosts.append( | |
| { | |
| "hostname": hostname, | |
| "host": host, | |
| "port": port, | |
| "family": family, | |
| "proto": proto, | |
| "flags": socket.AI_NUMERICHOST | socket.AI_NUMERICSERV, | |
| } | |
| ) | |
| return hosts | |
| async def close(self) -> None: | |
| pass | |
| class AsyncResolver(AbstractResolver): | |
| """Use the `aiodns` package to make asynchronous DNS lookups""" | |
| def __init__( | |
| self, | |
| loop: Optional[asyncio.AbstractEventLoop] = None, | |
| *args: Any, | |
| **kwargs: Any | |
| ) -> None: | |
| if aiodns is None: | |
| raise RuntimeError("Resolver requires aiodns library") | |
| self._loop = get_running_loop(loop) | |
| self._resolver = aiodns.DNSResolver(*args, loop=loop, **kwargs) | |
| if not hasattr(self._resolver, "gethostbyname"): | |
| # aiodns 1.1 is not available, fallback to DNSResolver.query | |
| self.resolve = self._resolve_with_query # type: ignore | |
| async def resolve( | |
| self, host: str, port: int = 0, family: int = socket.AF_INET | |
| ) -> List[Dict[str, Any]]: | |
| try: | |
| resp = await self._resolver.gethostbyname(host, family) | |
| except aiodns.error.DNSError as exc: | |
| msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" | |
| raise OSError(msg) from exc | |
| hosts = [] | |
| for address in resp.addresses: | |
| hosts.append( | |
| { | |
| "hostname": host, | |
| "host": address, | |
| "port": port, | |
| "family": family, | |
| "proto": 0, | |
| "flags": socket.AI_NUMERICHOST | socket.AI_NUMERICSERV, | |
| } | |
| ) | |
| if not hosts: | |
| raise OSError("DNS lookup failed") | |
| return hosts | |
| async def _resolve_with_query( | |
| self, host: str, port: int = 0, family: int = socket.AF_INET | |
| ) -> List[Dict[str, Any]]: | |
| if family == socket.AF_INET6: | |
| qtype = "AAAA" | |
| else: | |
| qtype = "A" | |
| try: | |
| resp = await self._resolver.query(host, qtype) | |
| except aiodns.error.DNSError as exc: | |
| msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" | |
| raise OSError(msg) from exc | |
| hosts = [] | |
| for rr in resp: | |
| hosts.append( | |
| { | |
| "hostname": host, | |
| "host": rr.host, | |
| "port": port, | |
| "family": family, | |
| "proto": 0, | |
| "flags": socket.AI_NUMERICHOST, | |
| } | |
| ) | |
| if not hosts: | |
| raise OSError("DNS lookup failed") | |
| return hosts | |
| async def close(self) -> None: | |
| self._resolver.cancel() | |
| _DefaultType = Type[Union[AsyncResolver, ThreadedResolver]] | |
| DefaultResolver: _DefaultType = AsyncResolver if aiodns_default else ThreadedResolver | |