From 23da79f85e9c763307ef358ba196cf3ee3945e72 Mon Sep 17 00:00:00 2001 From: Hanaasagi Date: Wed, 21 Jul 2021 16:28:29 +0000 Subject: [PATCH] Fix: fix the code about handling `getaddrinfo` when disable ipv6 in CPython and enable ipv6 in system. Related to #5901; `getaddrinfo` will return an `(int, bytes)` tuple, if CPython could not handle the address family. It will cause a index out of range error in aiohttp. --- CHANGES/5901.bugfix | 4 ++++ aiohttp/resolver.py | 4 +++- tests/test_resolver.py | 24 ++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 CHANGES/5901.bugfix diff --git a/CHANGES/5901.bugfix b/CHANGES/5901.bugfix new file mode 100644 index 0000000000..5c5940fde1 --- /dev/null +++ b/CHANGES/5901.bugfix @@ -0,0 +1,4 @@ +Fix the error in handling the return value of `getaddrinfo`. +`getaddrinfo` will return an `(int, bytes)` tuple, if CPython could not handle the address family. +It will cause a index out of range error in aiohttp. For example, if user compile CPython with +`--disable-ipv6` option but his system enable the ipv6. diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index e62e7f377b..e222001082 100644 --- a/aiohttp/resolver.py +++ b/aiohttp/resolver.py @@ -37,7 +37,9 @@ async def resolve( hosts = [] for family, _, proto, _, address in infos: - if family == socket.AF_INET6 and address[3]: # type: ignore[misc] + if family == socket.AF_INET6: + if not (socket.has_ipv6 and address[3]): # type: ignore[misc] + continue # 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 b74764525c..6d617cc997 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -119,6 +119,30 @@ async def test_threaded_negative_lookup() -> None: await resolver.resolve("doesnotexist.bla") +async def test_threaded_negative_lookup_with_unknown_result() -> None: + loop = Mock() + + # If compile CPython with `--disable-ipv6` option, + # we will get an (int, bytes) tuple, instead of a Exception. + async def unknown_addrinfo(*args: Any, **kwargs: Any) -> List[Any]: + return [ + ( + socket.AF_INET6, + socket.SOCK_STREAM, + 6, + "", + (10, b"\x01\xbb\x00\x00\x00\x00*\x04NB\x00\x1a\x00\x00"), + ) + ] + + loop.getaddrinfo = unknown_addrinfo + resolver = ThreadedResolver() + resolver._loop = loop + with patch("socket.has_ipv6", False): + res = await resolver.resolve("www.python.org") + assert len(res) == 0 + + async def test_close_for_threaded_resolver(loop: Any) -> None: resolver = ThreadedResolver() await resolver.close()