diff --git a/CHANGES/6195.bugfix b/CHANGES/6195.bugfix new file mode 100644 index 0000000000..2d749380b1 --- /dev/null +++ b/CHANGES/6195.bugfix @@ -0,0 +1 @@ +Fix the threaded dns resolver skip the correct dns response when connecting a IPv6-only host. diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index e222001082..817e820202 100644 --- a/aiohttp/resolver.py +++ b/aiohttp/resolver.py @@ -37,9 +37,10 @@ async def resolve( hosts = [] for family, _, proto, _, address in infos: - if family == socket.AF_INET6: - if not (socket.has_ipv6 and address[3]): # type: ignore[misc] - continue + if family == socket.AF_INET6 and not socket.has_ipv6: + continue + + if family == socket.AF_INET6 and address[3]: # type: ignore[misc] # 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. diff --git a/tests/test_resolver.py b/tests/test_resolver.py index e84733ff4f..64daec681c 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -143,6 +143,29 @@ async def unknown_addrinfo(*args: Any, **kwargs: Any) -> List[Any]: assert len(res) == 0 +async def test_threaded_resolver_ipv6_only_host() -> None: + loop = Mock() + + async def ipv6_addrinfo(*args: Any, **kwargs: Any) -> List[Any]: + return [ + ( + socket.AF_INET6, + socket.SOCK_STREAM, + 6, + "", + ("2404:6800:4004:819::200", 443, 0, 0), + ) + ] + + loop.getaddrinfo = ipv6_addrinfo + resolver = ThreadedResolver() + resolver._loop = loop + res = await resolver.resolve("https://ipv6.google.com") + assert len(res) == 1 + assert res[0]["hostname"] == "https://ipv6.google.com" + assert res[0]["host"] == "2404:6800:4004:819::200" + + async def test_close_for_threaded_resolver(loop: Any) -> None: resolver = ThreadedResolver() await resolver.close()