[issue35545] asyncio.base_events.create_connection doesn't handle scoped IPv6 addresses
Максим Аристов
report at bugs.python.org
Thu Dec 20 05:56:50 EST 2018
New submission from Максим Аристов <aristovmax at gmail.com>:
loop.create_connection doesn't handle ipv6 RFC4007 addresses right since 3.7
TEST CASE
# Set up listener on link-local address fe80::1%lo
sudo ip a add dev lo fe80::1
# 3.6 handles everything fine
socat file:/dev/null tcp6-listen:12345,REUSEADDR &
python3.6 -c 'import asyncio;loop=asyncio.get_event_loop();loop.run_until_complete(loop.create_connection(lambda:asyncio.Protocol(),host="fe80::1%lo",port="12345"))'
# 3.7 and later fails
socat file:/dev/null tcp6-listen:12345,REUSEADDR &
python3.7 -c 'import asyncio;loop=asyncio.get_event_loop();loop.run_until_complete(loop.create_connection(lambda:asyncio.Protocol(),host="fe80::1%lo",port="12345"))'
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/lib/python3.7/asyncio/base_events.py", line 576, in run_until_complete
return future.result()
File "/usr/lib/python3.7/asyncio/base_events.py", line 951, in create_connection
raise exceptions[0]
File "/usr/lib/python3.7/asyncio/base_events.py", line 938, in create_connection
await self.sock_connect(sock, address)
File "/usr/lib/python3.7/asyncio/selector_events.py", line 475, in sock_connect
return await fut
File "/usr/lib/python3.7/asyncio/selector_events.py", line 480, in _sock_connect
sock.connect(address)
OSError: [Errno 22] Invalid argument
CAUSE
Upon asyncio.base_events.create_connection _ensure_resolved is called twice, first time here:
https://github.com/python/cpython/blob/3.7/Lib/asyncio/base_events.py#L908
then here through sock_connect:
https://github.com/python/cpython/blob/3.7/Lib/asyncio/base_events.py#L946
https://github.com/python/cpython/blob/3.7/Lib/asyncio/selector_events.py#L458
_ensure_resolved calls getaddrinfo, but in 3.7 implementation changed:
% python3.6 -c 'import socket;print(socket.getaddrinfo("fe80::1%lo",12345)[0][4])'
('fe80::1%lo', 12345, 0, 1)
% python3.7 -c 'import socket;print(socket.getaddrinfo("fe80::1%lo",12345)[0][4])'
('fe80::1', 12345, 0, 1)
_ensure_connect only considers host and port parts of the address tuple:
https://github.com/python/cpython/blob/3.7/Lib/asyncio/base_events.py#L1272
In case of 3.7 first call to _ensure_resolved returns
('fe80::1', 12345, 0, 1)
then second call returns
('fe80::1', 12345, 0, 0)
Notice that scope is now completely lost and is set to 0, thus actual call to socket.connect is wrong
In case of 3.6 both first and second call to _ensure_resolved return
('fe80::1%lo', 12345, 0, 1)
because in 3.6 case scope info is preserved in address and second call can derive correct address tuple
----------
components: asyncio
messages: 332211
nosy: asvetlov, yselivanov, Максим Аристов
priority: normal
severity: normal
status: open
title: asyncio.base_events.create_connection doesn't handle scoped IPv6 addresses
type: behavior
versions: Python 3.7, Python 3.8
_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue35545>
_______________________________________
More information about the Python-bugs-list
mailing list