Skip to content

Commit

Permalink
[PR #9032/c693a816 backport][3.10] Fix Link-Local IPv6 Flags in the R…
Browse files Browse the repository at this point in the history
…esolver (#9048)

Co-authored-by: Nathan Lee <nathan.lee@garmin.com>
Co-authored-by: pre-commit-ci[bot]
Co-authored-by: Sam Bull <git@sambull.org>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: GitNMLee <89409038+GitNMLee@users.noreply.github.com>
  • Loading branch information
4 people committed Sep 6, 2024
1 parent 53d3f59 commit b48ebc1
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGES/9032.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fixed the incorrect use of flags for ``getnameinfo()`` in the Resolver --by :user:`GitNMLee`

Link-Local IPv6 addresses can now be handled by the Resolver correctly.
5 changes: 3 additions & 2 deletions aiohttp/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@


_NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV
_NAME_SOCKET_FLAGS = socket.NI_NUMERICHOST | socket.NI_NUMERICSERV
_SUPPORTS_SCOPE_ID = sys.version_info >= (3, 9, 0)


Expand Down Expand Up @@ -54,7 +55,7 @@ async def resolve(
# 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, _NUMERIC_SOCKET_FLAGS
address, _NAME_SOCKET_FLAGS
)
port = int(_port)
else:
Expand Down Expand Up @@ -122,7 +123,7 @@ async def resolve(
# getnameinfo() unconditionally, but performance makes sense.
result = await self._resolver.getnameinfo(
(address[0].decode("ascii"), *address[1:]),
_NUMERIC_SOCKET_FLAGS,
_NAME_SOCKET_FLAGS,
)
resolved_host = result.node
else:
Expand Down
27 changes: 22 additions & 5 deletions tests/test_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import socket
from ipaddress import ip_address
from typing import Any, Awaitable, Callable, Collection, List, NamedTuple, Tuple, Union
from unittest.mock import Mock, patch
from unittest.mock import Mock, create_autospec, patch

import pytest

from aiohttp.resolver import (
_NUMERIC_SOCKET_FLAGS,
_NAME_SOCKET_FLAGS,
_SUPPORTS_SCOPE_ID,
AsyncResolver,
DefaultResolver,
Expand Down Expand Up @@ -157,9 +157,7 @@ async def test_async_resolver_positive_link_local_ipv6_lookup(loop: Any) -> None
port=0,
type=socket.SOCK_STREAM,
)
mock().getnameinfo.assert_called_with(
("fe80::1", 0, 0, 3), _NUMERIC_SOCKET_FLAGS
)
mock().getnameinfo.assert_called_with(("fe80::1", 0, 0, 3), _NAME_SOCKET_FLAGS)


@pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required")
Expand Down Expand Up @@ -218,12 +216,31 @@ async def test_threaded_resolver_positive_ipv6_link_local_lookup() -> None:
loop = Mock()
loop.getaddrinfo = fake_ipv6_addrinfo(["fe80::1"])
loop.getnameinfo = fake_ipv6_nameinfo("fe80::1%eth0")

# Mock the fake function that was returned by helper functions
loop.getaddrinfo = create_autospec(loop.getaddrinfo)
loop.getnameinfo = create_autospec(loop.getnameinfo)

# Set the correct return values for mock functions
loop.getaddrinfo.return_value = await fake_ipv6_addrinfo(["fe80::1"])()
loop.getnameinfo.return_value = await fake_ipv6_nameinfo("fe80::1%eth0")()

resolver = ThreadedResolver()
resolver._loop = loop
real = await resolver.resolve("www.python.org")
assert real[0]["hostname"] == "www.python.org"
ipaddress.ip_address(real[0]["host"])

loop.getaddrinfo.assert_called_with(
"www.python.org",
0,
type=socket.SOCK_STREAM,
family=socket.AF_INET,
flags=socket.AI_ADDRCONFIG,
)

loop.getnameinfo.assert_called_with(("fe80::1", 0, 0, 3), _NAME_SOCKET_FLAGS)


async def test_threaded_resolver_multiple_replies() -> None:
loop = Mock()
Expand Down

0 comments on commit b48ebc1

Please sign in to comment.