]> granicus.if.org Git - python/commitdiff
bpo-35545: Fix asyncio discarding IPv6 scopes (GH-11271)
authorErwan Le Pape <lepaperwan@users.noreply.github.com>
Fri, 17 May 2019 08:28:39 +0000 (10:28 +0200)
committerMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 17 May 2019 08:28:39 +0000 (01:28 -0700)
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

Lib/asyncio/base_events.py
Lib/test/test_asyncio/test_base_events.py
Misc/NEWS.d/next/Library/2019-05-11-16-21-29.bpo-35545.FcvJvP.rst [new file with mode: 0644]

index 9613ac2a114f0d1b72486a2dbcd7567fe3ab87f0..de9fa4f4f7f3b6bb0868dbaf49cf43ca777ef7cc 100644 (file)
@@ -102,7 +102,7 @@ def _set_reuseport(sock):
                              'SO_REUSEPORT defined but not implemented.')
 
 
-def _ipaddr_info(host, port, family, type, proto):
+def _ipaddr_info(host, port, family, type, proto, flowinfo=0, scopeid=0):
     # Try to skip getaddrinfo if "host" is already an IP. Users might have
     # handled name resolution in their own code and pass in resolved IPs.
     if not hasattr(socket, 'inet_pton'):
@@ -151,7 +151,7 @@ def _ipaddr_info(host, port, family, type, proto):
             socket.inet_pton(af, host)
             # The host has already been resolved.
             if _HAS_IPv6 and af == socket.AF_INET6:
-                return af, type, proto, '', (host, port, 0, 0)
+                return af, type, proto, '', (host, port, flowinfo, scopeid)
             else:
                 return af, type, proto, '', (host, port)
         except OSError:
@@ -1348,7 +1348,7 @@ class BaseEventLoop(events.AbstractEventLoop):
                                family=0, type=socket.SOCK_STREAM,
                                proto=0, flags=0, loop):
         host, port = address[:2]
-        info = _ipaddr_info(host, port, family, type, proto)
+        info = _ipaddr_info(host, port, family, type, proto, *address[2:])
         if info is not None:
             # "host" is already a resolved IP.
             return [info]
index 27e21b33d360a3c50ae849fb59d5a528e2b0279f..f068fc781f5d7c46f28b97c7f4ded5e824324529 100644 (file)
@@ -1299,6 +1299,28 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase):
             t.close()
             test_utils.run_briefly(self.loop)  # allow transport to close
 
+    @patch_socket
+    def test_create_connection_ipv6_scope(self, m_socket):
+        m_socket.getaddrinfo = socket.getaddrinfo
+        sock = m_socket.socket.return_value
+        sock.family = socket.AF_INET6
+
+        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
+
+        coro = self.loop.create_connection(asyncio.Protocol, 'fe80::1%1', 80)
+        t, p = self.loop.run_until_complete(coro)
+        try:
+            sock.connect.assert_called_with(('fe80::1', 80, 0, 1))
+            _, kwargs = m_socket.socket.call_args
+            self.assertEqual(kwargs['family'], m_socket.AF_INET6)
+            self.assertEqual(kwargs['type'], m_socket.SOCK_STREAM)
+        finally:
+            t.close()
+            test_utils.run_briefly(self.loop)  # allow transport to close
+
     @patch_socket
     def test_create_connection_ip_addr(self, m_socket):
         self._test_create_connection_ip_addr(m_socket, True)
diff --git a/Misc/NEWS.d/next/Library/2019-05-11-16-21-29.bpo-35545.FcvJvP.rst b/Misc/NEWS.d/next/Library/2019-05-11-16-21-29.bpo-35545.FcvJvP.rst
new file mode 100644 (file)
index 0000000..f4349af
--- /dev/null
@@ -0,0 +1,2 @@
+Fix asyncio discarding IPv6 scopes when ensuring hostname resolutions
+internally