Skip to content

bpo-37007: Implement socket.if_nametoindex() and socket.if_indextoname() on Windows #13522

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

Merged
merged 5 commits into from
May 29, 2019
Merged
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
15 changes: 12 additions & 3 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1034,32 +1034,41 @@ 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)

Return a network interface index number corresponding to an
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)

Return a network interface name corresponding to an
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:

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,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
----------

Expand Down
24 changes: 11 additions & 13 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Implement :func:`socket.if_nameindex()`, :func:`socket.if_nametoindex()`, and
:func:`socket.if_indextoname()` on Windows.
60 changes: 49 additions & 11 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -6668,28 +6670,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]));
Expand Down Expand Up @@ -6721,6 +6751,7 @@ socket_if_nameindex(PyObject *self, PyObject *arg)

if_freenameindex(ni);
return list;
#endif
}

PyDoc_STRVAR(if_nameindex_doc,
Expand All @@ -6732,8 +6763,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;
Expand All @@ -6757,7 +6791,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);
Expand All @@ -6777,7 +6815,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
Expand Down Expand Up @@ -6899,7 +6937,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,
Expand Down
2 changes: 1 addition & 1 deletion PCbuild/_socket.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
</PropertyGroup>
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
Expand Down