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

bpo-35545: Fix mishandling of scoped IPv6 addresses #11403

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 15 additions & 12 deletions Lib/asyncio/base_events.py
Expand Up @@ -141,6 +141,8 @@ def _ipaddr_info(host, port, family, type, proto):
if '%' in host:
# Linux's inet_pton doesn't accept an IPv6 zone index after host,
# like '::1%lo0'.
# Such hosts should be resolved by getaddrinfo() to obtain the
# appropriate scope ID, and to have the zone part removed.
return None

for af in afs:
Expand Down Expand Up @@ -917,16 +919,16 @@ async def create_connection(
'host/port and sock can not be specified at the same time')

infos = await self._ensure_resolved(
(host, port), family=family,
type=socket.SOCK_STREAM, proto=proto, flags=flags, loop=self)
host, port, family=family,
type=socket.SOCK_STREAM, proto=proto, flags=flags)
if not infos:
raise OSError('getaddrinfo() returned empty list')

if local_addr is not None:
laddr_infos = await self._ensure_resolved(
local_addr, family=family,
local_addr[0], local_addr[1], family=family,
type=socket.SOCK_STREAM, proto=proto,
flags=flags, loop=self)
flags=flags)
if not laddr_infos:
raise OSError('getaddrinfo() returned empty list')

Expand Down Expand Up @@ -1195,8 +1197,8 @@ async def create_datagram_endpoint(self, protocol_factory,
'2-tuple is expected')

infos = await self._ensure_resolved(
addr, family=family, type=socket.SOCK_DGRAM,
proto=proto, flags=flags, loop=self)
*addr, family=family, type=socket.SOCK_DGRAM,
proto=proto, flags=flags)
if not infos:
raise OSError('getaddrinfo() returned empty list')

Expand Down Expand Up @@ -1277,22 +1279,23 @@ async def create_datagram_endpoint(self, protocol_factory,

return transport, protocol

async def _ensure_resolved(self, address, *,
async def _ensure_resolved(self, host, port, *,
family=0, type=socket.SOCK_STREAM,
proto=0, flags=0, loop):
host, port = address[:2]
proto=0, flags=0):
# Resolve (host, port) into a socket address tuple, skipping
# getaddrinfo() when possible.
info = _ipaddr_info(host, port, family, type, proto)
if info is not None:
# "host" is already a resolved IP.
return [info]
else:
return await loop.getaddrinfo(host, port, family=family, type=type,
return await self.getaddrinfo(host, port, family=family, type=type,
proto=proto, flags=flags)

async def _create_server_getaddrinfo(self, host, port, family, flags):
infos = await self._ensure_resolved((host, port), family=family,
infos = await self._ensure_resolved(host, port, family=family,
type=socket.SOCK_STREAM,
flags=flags, loop=self)
flags=flags)
if not infos:
raise OSError(f'getaddrinfo({host!r}) returned empty list')
return infos
Expand Down
9 changes: 4 additions & 5 deletions Lib/asyncio/selector_events.py
Expand Up @@ -458,16 +458,15 @@ def _sock_sendall(self, fut, sock, data):
async def sock_connect(self, sock, address):
"""Connect to a remote socket at address.

`address` must be a socket address tuple(i.e. (ipv4_address, port) for
IPv4 or (ipv6_address, port, flowinfo, scopeid) for IPv6). It must
not be a (host, port) tuple that still needs to be resolved.

This method is a coroutine.
"""
if self._debug and sock.gettimeout() != 0:
raise ValueError("the socket must be non-blocking")

if not hasattr(socket, 'AF_UNIX') or sock.family != socket.AF_UNIX:
resolved = await self._ensure_resolved(
address, family=sock.family, proto=sock.proto, loop=self)
_, _, _, _, address = resolved[0]

fut = self.create_future()
self._sock_connect(fut, sock, address)
return await fut
Expand Down
@@ -0,0 +1,2 @@
Fix mishandling of scoped IPv6 addresses in
BaseEventLoop.create_connection().