Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PR #9032/c693a816 backport][3.11] Fix Link-Local IPv6 Flags in the Resolver #9047

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -17,6 +17,7 @@


_NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV
_NAME_SOCKET_FLAGS = socket.NI_NUMERICHOST | socket.NI_NUMERICSERV


class ThreadedResolver(AbstractResolver):
Expand Down Expand Up @@ -52,7 +53,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 @@ -120,7 +121,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,
AsyncResolver,
DefaultResolver,
ThreadedResolver,
Expand Down Expand Up @@ -153,9 +153,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 @@ -211,12 +209,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
Loading