Message332211
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 |
|
Date |
User |
Action |
Args |
2018-12-20 10:56:50 | maxifree | set | recipients:
+ maxifree, asvetlov, yselivanov |
2018-12-20 10:56:50 | maxifree | set | messageid: <1545303410.27.0.788709270274.issue35545@psf.upfronthosting.co.za> |
2018-12-20 10:56:50 | maxifree | link | issue35545 messages |
2018-12-20 10:56:50 | maxifree | create | |
|