[Python-checkins] bpo-37007: Implement socket.if_nametoindex(), if_indextoname() and if_nameindex() on Windows (GH-13522)

Steve Dower webhook-mailer at python.org
Wed May 29 17:02:45 EDT 2019


https://github.com/python/cpython/commit/8f96c9f8ed2a4795e34b333411451e24f28f74d2
commit: 8f96c9f8ed2a4795e34b333411451e24f28f74d2
branch: master
author: Zackery Spytz <zspytz at gmail.com>
committer: Steve Dower <steve.dower at python.org>
date: 2019-05-29T14:02:37-07:00
summary:

bpo-37007: Implement socket.if_nametoindex(), if_indextoname() and if_nameindex() on Windows (GH-13522)

files:
A Misc/NEWS.d/next/Core and Builtins/2019-05-23-04-19-13.bpo-37007.d1SOtF.rst
M Doc/library/socket.rst
M Doc/whatsnew/3.8.rst
M Lib/test/test_socket.py
M Modules/socketmodule.c
M PCbuild/_socket.vcxproj

diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst
index 5be2b76113eb..e0dbbb4c0d32 100644
--- a/Doc/library/socket.rst
+++ b/Doc/library/socket.rst
@@ -1034,10 +1034,13 @@ The :mod:`socket` module also offers various network-related services:
    (index int, name string) tuples.
    :exc:`OSError` if the system call fails.
 
-   .. availability:: Unix.
+   .. availability:: Unix, Windows.
 
    .. versionadded:: 3.3
 
+   .. versionchanged:: 3.8
+      Windows support was added.
+
 
 .. function:: if_nametoindex(if_name)
 
@@ -1045,10 +1048,13 @@ The :mod:`socket` module also offers various network-related services:
    interface name.
    :exc:`OSError` if no interface with the given name exists.
 
-   .. availability:: Unix.
+   .. availability:: Unix, Windows.
 
    .. versionadded:: 3.3
 
+   .. versionchanged:: 3.8
+      Windows support was added.
+
 
 .. function:: if_indextoname(if_index)
 
@@ -1056,10 +1062,13 @@ The :mod:`socket` module also offers various network-related services:
    interface index number.
    :exc:`OSError` if no interface with the given index exists.
 
-   .. availability:: Unix.
+   .. availability:: Unix, Windows.
 
    .. versionadded:: 3.3
 
+   .. versionchanged:: 3.8
+      Windows support was added.
+
 
 .. _socket-objects:
 
diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst
index 5cd9a8edc793..5ee9cf07ebaf 100644
--- a/Doc/whatsnew/3.8.rst
+++ b/Doc/whatsnew/3.8.rst
@@ -556,6 +556,10 @@ convenience functions to automate the necessary tasks usually involved when
 creating a server socket, including accepting both IPv4 and IPv6 connections
 on the same socket.  (Contributed by Giampaolo Rodola in :issue:`17561`.)
 
+The :func:`socket.if_nameindex()`, :func:`socket.if_nametoindex()`, and
+:func:`socket.if_indextoname()` functions have been implemented on Windows.
+(Contributed by Zackery Spytz in :issue:`37007`.)
+
 shlex
 ----------
 
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 0094cecb79cc..74662cfeb327 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -973,16 +973,18 @@ def testInterfaceNameIndex(self):
             self.assertIsInstance(_name, str)
             self.assertEqual(name, _name)
 
-    @unittest.skipUnless(hasattr(socket, 'if_nameindex'),
-                         'socket.if_nameindex() not available.')
-    def testInvalidInterfaceNameIndex(self):
-        # test nonexistent interface index/name
+    @unittest.skipUnless(hasattr(socket, 'if_indextoname'),
+                         'socket.if_indextoname() not available.')
+    def testInvalidInterfaceIndexToName(self):
         self.assertRaises(OSError, socket.if_indextoname, 0)
-        self.assertRaises(OSError, socket.if_nametoindex, '_DEADBEEF')
-        # test with invalid values
-        self.assertRaises(TypeError, socket.if_nametoindex, 0)
         self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF')
 
+    @unittest.skipUnless(hasattr(socket, 'if_nametoindex'),
+                         'socket.if_nametoindex() not available.')
+    def testInvalidInterfaceNameToIndex(self):
+        self.assertRaises(TypeError, socket.if_nametoindex, 0)
+        self.assertRaises(OSError, socket.if_nametoindex, '_DEADBEEF')
+
     @unittest.skipUnless(hasattr(sys, 'getrefcount'),
                          'test needs sys.getrefcount()')
     def testRefCountGetNameInfo(self):
@@ -1638,9 +1640,7 @@ def test_getaddrinfo_ipv6_basic(self):
         self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, 0))
 
     @unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
-    @unittest.skipUnless(
-        hasattr(socket, 'if_nameindex'),
-        'if_nameindex is not supported')
+    @unittest.skipIf(sys.platform == 'win32', 'does not work on Windows')
     @unittest.skipIf(AIX, 'Symbolic scope id does not work')
     def test_getaddrinfo_ipv6_scopeid_symbolic(self):
         # Just pick up any network interface (Linux, Mac OS X)
@@ -1672,9 +1672,7 @@ def test_getaddrinfo_ipv6_scopeid_numeric(self):
         self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, ifindex))
 
     @unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
-    @unittest.skipUnless(
-        hasattr(socket, 'if_nameindex'),
-        'if_nameindex is not supported')
+    @unittest.skipIf(sys.platform == 'win32', 'does not work on Windows')
     @unittest.skipIf(AIX, 'Symbolic scope id does not work')
     def test_getnameinfo_ipv6_scopeid_symbolic(self):
         # Just pick up any network interface.
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-23-04-19-13.bpo-37007.d1SOtF.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-23-04-19-13.bpo-37007.d1SOtF.rst
new file mode 100644
index 000000000000..ac344a57c83d
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-05-23-04-19-13.bpo-37007.d1SOtF.rst	
@@ -0,0 +1,2 @@
+Implement :func:`socket.if_nameindex()`, :func:`socket.if_nametoindex()`, and
+:func:`socket.if_indextoname()` on Windows.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index ca4d760f8cb4..ac1698c6c767 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -345,6 +345,8 @@ if_indextoname(index) -- return the corresponding interface name\n\
 
 /* Provides the IsWindows7SP1OrGreater() function */
 #include <versionhelpers.h>
+// For if_nametoindex() and if_indextoname()
+#include <iphlpapi.h>
 
 /* remove some flags on older version Windows during run-time.
    https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596.aspx */
@@ -6667,28 +6669,56 @@ Set the default timeout in seconds (float) for new socket objects.\n\
 A value of None indicates that new socket objects have no timeout.\n\
 When the socket module is first imported, the default is None.");
 
-#ifdef HAVE_IF_NAMEINDEX
+#if defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
 /* Python API for getting interface indices and names */
 
 static PyObject *
 socket_if_nameindex(PyObject *self, PyObject *arg)
 {
-    PyObject *list;
+    PyObject *list = PyList_New(0);
+    if (list == NULL) {
+        return NULL;
+    }
+#ifdef MS_WINDOWS
+    PMIB_IF_TABLE2 tbl;
+    int ret;
+    if ((ret = GetIfTable2Ex(MibIfTableRaw, &tbl)) != NO_ERROR) {
+        Py_DECREF(list);
+        // ret is used instead of GetLastError()
+        return PyErr_SetFromWindowsErr(ret);
+    }
+    for (ULONG i = 0; i < tbl->NumEntries; ++i) {
+        MIB_IF_ROW2 r = tbl->Table[i];
+        WCHAR buf[NDIS_IF_MAX_STRING_SIZE + 1];
+        if ((ret = ConvertInterfaceLuidToNameW(&r.InterfaceLuid, buf,
+                                               Py_ARRAY_LENGTH(buf)))) {
+            Py_DECREF(list);
+            FreeMibTable(tbl);
+            // ret is used instead of GetLastError()
+            return PyErr_SetFromWindowsErr(ret);
+        }
+        PyObject *tuple = Py_BuildValue("Iu", r.InterfaceIndex, buf);
+        if (tuple == NULL || PyList_Append(list, tuple) == -1) {
+            Py_XDECREF(tuple);
+            Py_DECREF(list);
+            FreeMibTable(tbl);
+            return NULL;
+        }
+        Py_DECREF(tuple);
+    }
+    FreeMibTable(tbl);
+    return list;
+#else
     int i;
     struct if_nameindex *ni;
 
     ni = if_nameindex();
     if (ni == NULL) {
+        Py_DECREF(list);
         PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
 
-    list = PyList_New(0);
-    if (list == NULL) {
-        if_freenameindex(ni);
-        return NULL;
-    }
-
 #ifdef _Py_MEMORY_SANITIZER
     __msan_unpoison(ni, sizeof(ni));
     __msan_unpoison(&ni[0], sizeof(ni[0]));
@@ -6720,6 +6750,7 @@ socket_if_nameindex(PyObject *self, PyObject *arg)
 
     if_freenameindex(ni);
     return list;
+#endif
 }
 
 PyDoc_STRVAR(if_nameindex_doc,
@@ -6731,8 +6762,11 @@ static PyObject *
 socket_if_nametoindex(PyObject *self, PyObject *args)
 {
     PyObject *oname;
+#ifdef MS_WINDOWS
+    NET_IFINDEX index;
+#else
     unsigned long index;
-
+#endif
     if (!PyArg_ParseTuple(args, "O&:if_nametoindex",
                           PyUnicode_FSConverter, &oname))
         return NULL;
@@ -6756,7 +6790,11 @@ Returns the interface index corresponding to the interface name if_name.");
 static PyObject *
 socket_if_indextoname(PyObject *self, PyObject *arg)
 {
+#ifdef MS_WINDOWS
+    NET_IFINDEX index;
+#else
     unsigned long index;
+#endif
     char name[IF_NAMESIZE + 1];
 
     index = PyLong_AsUnsignedLong(arg);
@@ -6776,7 +6814,7 @@ PyDoc_STRVAR(if_indextoname_doc,
 \n\
 Returns the interface name corresponding to the interface index if_index.");
 
-#endif  /* HAVE_IF_NAMEINDEX */
+#endif // defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
 
 
 #ifdef CMSG_LEN
@@ -6898,7 +6936,7 @@ static PyMethodDef socket_methods[] = {
      METH_NOARGS, getdefaulttimeout_doc},
     {"setdefaulttimeout",       socket_setdefaulttimeout,
      METH_O, setdefaulttimeout_doc},
-#ifdef HAVE_IF_NAMEINDEX
+#if defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
     {"if_nameindex", socket_if_nameindex,
      METH_NOARGS, if_nameindex_doc},
     {"if_nametoindex", socket_if_nametoindex,
diff --git a/PCbuild/_socket.vcxproj b/PCbuild/_socket.vcxproj
index 9498abf8fb5e..8fd75f90e7ee 100644
--- a/PCbuild/_socket.vcxproj
+++ b/PCbuild/_socket.vcxproj
@@ -93,7 +93,7 @@
   </PropertyGroup>
   <ItemDefinitionGroup>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>



More information about the Python-checkins mailing list