diff --git a/CHANGES/4554.bugfix b/CHANGES/4554.bugfix new file mode 100644 index 00000000000..3e9f970dd94 --- /dev/null +++ b/CHANGES/4554.bugfix @@ -0,0 +1 @@ +Fix connecting to link-local IPv6 addresses. diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index 53ad3fed70f..e8828ebc6ce 100644 --- a/aiohttp/resolver.py +++ b/aiohttp/resolver.py @@ -31,11 +31,23 @@ async def resolve(self, host: str, port: int=0, hosts = [] for family, _, proto, _, address in infos: - hosts.append( - {'hostname': host, - 'host': address[0], 'port': address[1], - 'family': family, 'proto': proto, - 'flags': socket.AI_NUMERICHOST}) + if family == socket.AF_INET6 and address[3]: # type: ignore + # 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] + hosts.append({ + 'hostname': host, + 'host': host, + 'port': port, + 'family': family, + 'proto': proto, + 'flags': socket.AI_NUMERICHOST | socket.AI_NUMERICSERV, + }) return hosts @@ -62,11 +74,14 @@ async def resolve(self, host: str, port: int=0, 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}) + 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") diff --git a/tests/test_connector.py b/tests/test_connector.py index 133e52300ab..fed5377f7fa 100644 --- a/tests/test_connector.py +++ b/tests/test_connector.py @@ -652,10 +652,10 @@ async def test_tcp_connector_resolve_host(loop) -> None: for rec in res: if rec['family'] == socket.AF_INET: assert rec['host'] == '127.0.0.1' - assert rec['hostname'] == 'localhost' + assert rec['hostname'] == '127.0.0.1' assert rec['port'] == 8080 elif rec['family'] == socket.AF_INET6: - assert rec['hostname'] == 'localhost' + assert rec['hostname'] == '::1' assert rec['port'] == 8080 if platform.system() == 'Darwin': assert rec['host'] in ('::1', 'fe80::1', 'fe80::1%lo0')