Skip to content

Commit 9470404

Browse files
bpo-35545: Fix asyncio discarding IPv6 scopes (GH-11271)
This PR proposes a solution to [bpo-35545](https://bugs.python.org/issue35545) by adding an optional `flowinfo` and `scopeid` to `asyncio.base_events._ipaddr_info` to carry the full address information into `_ipaddr_info` and avoid discarding IPv6 specific information. Changelog entry & regression tests to come. https://bugs.python.org/issue35545 (cherry picked from commit ac8eb8f) Co-authored-by: Erwan Le Pape <lepaperwan@users.noreply.github.com>
1 parent 4be0720 commit 9470404

3 files changed

Lines changed: 27 additions & 3 deletions

File tree

Lib/asyncio/base_events.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def _set_reuseport(sock):
9696
'SO_REUSEPORT defined but not implemented.')
9797

9898

99-
def _ipaddr_info(host, port, family, type, proto):
99+
def _ipaddr_info(host, port, family, type, proto, flowinfo=0, scopeid=0):
100100
# Try to skip getaddrinfo if "host" is already an IP. Users might have
101101
# handled name resolution in their own code and pass in resolved IPs.
102102
if not hasattr(socket, 'inet_pton'):
@@ -145,7 +145,7 @@ def _ipaddr_info(host, port, family, type, proto):
145145
socket.inet_pton(af, host)
146146
# The host has already been resolved.
147147
if _HAS_IPv6 and af == socket.AF_INET6:
148-
return af, type, proto, '', (host, port, 0, 0)
148+
return af, type, proto, '', (host, port, flowinfo, scopeid)
149149
else:
150150
return af, type, proto, '', (host, port)
151151
except OSError:
@@ -1271,7 +1271,7 @@ async def _ensure_resolved(self, address, *,
12711271
family=0, type=socket.SOCK_STREAM,
12721272
proto=0, flags=0, loop):
12731273
host, port = address[:2]
1274-
info = _ipaddr_info(host, port, family, type, proto)
1274+
info = _ipaddr_info(host, port, family, type, proto, *address[2:])
12751275
if info is not None:
12761276
# "host" is already a resolved IP.
12771277
return [info]

Lib/test/test_asyncio/test_base_events.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,28 @@ def _test_create_connection_ip_addr(self, m_socket, allow_inet_pton):
12921292
t.close()
12931293
test_utils.run_briefly(self.loop) # allow transport to close
12941294

1295+
@patch_socket
1296+
def test_create_connection_ipv6_scope(self, m_socket):
1297+
m_socket.getaddrinfo = socket.getaddrinfo
1298+
sock = m_socket.socket.return_value
1299+
sock.family = socket.AF_INET6
1300+
1301+
self.loop._add_reader = mock.Mock()
1302+
self.loop._add_reader._is_coroutine = False
1303+
self.loop._add_writer = mock.Mock()
1304+
self.loop._add_writer._is_coroutine = False
1305+
1306+
coro = self.loop.create_connection(asyncio.Protocol, 'fe80::1%1', 80)
1307+
t, p = self.loop.run_until_complete(coro)
1308+
try:
1309+
sock.connect.assert_called_with(('fe80::1', 80, 0, 1))
1310+
_, kwargs = m_socket.socket.call_args
1311+
self.assertEqual(kwargs['family'], m_socket.AF_INET6)
1312+
self.assertEqual(kwargs['type'], m_socket.SOCK_STREAM)
1313+
finally:
1314+
t.close()
1315+
test_utils.run_briefly(self.loop) # allow transport to close
1316+
12951317
@patch_socket
12961318
def test_create_connection_ip_addr(self, m_socket):
12971319
self._test_create_connection_ip_addr(m_socket, True)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix asyncio discarding IPv6 scopes when ensuring hostname resolutions
2+
internally

0 commit comments

Comments
 (0)