about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/aiohttp/resolver.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/aiohttp/resolver.py')
-rw-r--r--.venv/lib/python3.12/site-packages/aiohttp/resolver.py190
1 files changed, 190 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/aiohttp/resolver.py b/.venv/lib/python3.12/site-packages/aiohttp/resolver.py
new file mode 100644
index 00000000..e14179cc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/aiohttp/resolver.py
@@ -0,0 +1,190 @@
+import asyncio
+import socket
+from typing import Any, Dict, List, Optional, Tuple, Type, Union
+
+from .abc import AbstractResolver, ResolveResult
+
+__all__ = ("ThreadedResolver", "AsyncResolver", "DefaultResolver")
+
+
+try:
+    import aiodns
+
+    aiodns_default = hasattr(aiodns.DNSResolver, "getaddrinfo")
+except ImportError:  # pragma: no cover
+    aiodns = None  # type: ignore[assignment]
+    aiodns_default = False
+
+
+_NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV
+_NAME_SOCKET_FLAGS = socket.NI_NUMERICHOST | socket.NI_NUMERICSERV
+_AI_ADDRCONFIG = socket.AI_ADDRCONFIG
+if hasattr(socket, "AI_MASK"):
+    _AI_ADDRCONFIG &= socket.AI_MASK
+
+
+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 = loop or asyncio.get_running_loop()
+
+    async def resolve(
+        self, host: str, port: int = 0, family: socket.AddressFamily = socket.AF_INET
+    ) -> List[ResolveResult]:
+        infos = await self._loop.getaddrinfo(
+            host,
+            port,
+            type=socket.SOCK_STREAM,
+            family=family,
+            flags=_AI_ADDRCONFIG,
+        )
+
+        hosts: List[ResolveResult] = []
+        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.
+                    resolved_host, _port = await self._loop.getnameinfo(
+                        address, _NAME_SOCKET_FLAGS
+                    )
+                    port = int(_port)
+                else:
+                    resolved_host, port = address[:2]
+            else:  # IPv4
+                assert family == socket.AF_INET
+                resolved_host, port = address  # type: ignore[misc]
+            hosts.append(
+                ResolveResult(
+                    hostname=host,
+                    host=resolved_host,
+                    port=port,
+                    family=family,
+                    proto=proto,
+                    flags=_NUMERIC_SOCKET_FLAGS,
+                )
+            )
+
+        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._resolver = aiodns.DNSResolver(*args, **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: socket.AddressFamily = socket.AF_INET
+    ) -> List[ResolveResult]:
+        try:
+            resp = await self._resolver.getaddrinfo(
+                host,
+                port=port,
+                type=socket.SOCK_STREAM,
+                family=family,
+                flags=_AI_ADDRCONFIG,
+            )
+        except aiodns.error.DNSError as exc:
+            msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed"
+            raise OSError(None, msg) from exc
+        hosts: List[ResolveResult] = []
+        for node in resp.nodes:
+            address: Union[Tuple[bytes, int], Tuple[bytes, int, int, int]] = node.addr
+            family = node.family
+            if family == socket.AF_INET6:
+                if len(address) > 3 and 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.
+                    result = await self._resolver.getnameinfo(
+                        (address[0].decode("ascii"), *address[1:]),
+                        _NAME_SOCKET_FLAGS,
+                    )
+                    resolved_host = result.node
+                else:
+                    resolved_host = address[0].decode("ascii")
+                    port = address[1]
+            else:  # IPv4
+                assert family == socket.AF_INET
+                resolved_host = address[0].decode("ascii")
+                port = address[1]
+            hosts.append(
+                ResolveResult(
+                    hostname=host,
+                    host=resolved_host,
+                    port=port,
+                    family=family,
+                    proto=0,
+                    flags=_NUMERIC_SOCKET_FLAGS,
+                )
+            )
+
+        if not hosts:
+            raise OSError(None, "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(None, 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(None, "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