Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bpo-35302: Try each (remote addrinfo, local addrinfo) pair when connecting. #11241

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
33 changes: 15 additions & 18 deletions Lib/asyncio/base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,29 +929,26 @@ async def create_connection(
flags=flags, loop=self)
if not laddr_infos:
raise OSError('getaddrinfo() returned empty list')
else:
laddr_infos = [(None, None, None, None, None)]

exceptions = []
for family, type, proto, cname, address in infos:
for addrinfo, laddrinfo in itertools.product(infos, laddr_infos):
family, type, proto, cname, address = addrinfo
laddr = laddrinfo[4]
try:
sock = socket.socket(family=family, type=type, proto=proto)
sock.setblocking(False)
if local_addr is not None:
for _, _, _, _, laddr in laddr_infos:
try:
sock.bind(laddr)
break
except OSError as exc:
msg = (
f'error while attempting to bind on '
f'address {laddr!r}: '
f'{exc.strerror.lower()}'
)
exc = OSError(exc.errno, msg)
exceptions.append(exc)
else:
sock.close()
sock = None
continue
if laddr is not None:
try:
sock.bind(laddr)
except OSError as exc:
msg = (
f'error while attempting to bind on '
f'address {laddr!r}: '
f'{exc.strerror.lower()}'
)
raise OSError(exc.errno, msg)
if self._debug:
logger.debug("connect %r to %r", sock, address)
await self.sock_connect(sock, address)
Expand Down
38 changes: 38 additions & 0 deletions Lib/test/test_asyncio/test_base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,44 @@ def getaddrinfo_task(*args, **kwds):
self.assertRaises(
OSError, self.loop.run_until_complete, coro)

@patch_socket
def test_create_connection_socket_bind_different_family(self, m_socket):
# https://bugs.python.org/issue35302
# Test the case where local_addr resolves to multiple addrinfos, and
# if the socket is bound to the first address, connect() will fail.
sock = m_socket.socket.return_value

self.loop._add_reader = mock.Mock()
self.loop._add_reader._is_coroutine = False
self.loop._add_writer = mock.Mock()
self.loop._add_writer._is_coroutine = False

def mock_connect_check_bind(address):
if sock.bind.call_count:
bind_addr = sock.bind.call_args[0][0]
if bind_addr[0][:4] != address[0][:4]:
raise OSError

sock.connect.side_effect = mock_connect_check_bind

async def getaddrinfo(host, port, *args, **kwargs):
if host == 'bindaddr':
return [(2, 1, 6, '', ('192.0.2.1', port)),
(10, 1, 6, '', ('2001:db8::1', port))]
else:
return [(10, 1, 6, '', ('2001:db8::1:1', port))]

self.loop.getaddrinfo = getaddrinfo

coro = self.loop.create_connection(
MyProto, 'connectaddr', 80, local_addr=('bindaddr', 0))
t, p = self.loop.run_until_complete(coro)
try:
sock.connect.assert_called_with(('2001:db8::1:1', 80))
finally:
t.close()
test_utils.run_briefly(self.loop)

@patch_socket
def test_create_connection_bluetooth(self, m_socket):
# See http://bugs.python.org/issue27136, fallback to getaddrinfo when
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Try each (remote addrinfo, local addrinfo) pair in
BaseEventLoop.create_connection().