Skip to content

Commit 8f96c9f

Browse files
ZackerySpytzzooba
authored andcommitted
bpo-37007: Implement socket.if_nametoindex(), if_indextoname() and if_nameindex() on Windows (GH-13522)
1 parent fecb75c commit 8f96c9f

File tree

6 files changed

+79
-28
lines changed

6 files changed

+79
-28
lines changed

Doc/library/socket.rst

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,32 +1034,41 @@ The :mod:`socket` module also offers various network-related services:
10341034
(index int, name string) tuples.
10351035
:exc:`OSError` if the system call fails.
10361036

1037-
.. availability:: Unix.
1037+
.. availability:: Unix, Windows.
10381038

10391039
.. versionadded:: 3.3
10401040

1041+
.. versionchanged:: 3.8
1042+
Windows support was added.
1043+
10411044

10421045
.. function:: if_nametoindex(if_name)
10431046

10441047
Return a network interface index number corresponding to an
10451048
interface name.
10461049
:exc:`OSError` if no interface with the given name exists.
10471050

1048-
.. availability:: Unix.
1051+
.. availability:: Unix, Windows.
10491052

10501053
.. versionadded:: 3.3
10511054

1055+
.. versionchanged:: 3.8
1056+
Windows support was added.
1057+
10521058

10531059
.. function:: if_indextoname(if_index)
10541060

10551061
Return a network interface name corresponding to an
10561062
interface index number.
10571063
:exc:`OSError` if no interface with the given index exists.
10581064

1059-
.. availability:: Unix.
1065+
.. availability:: Unix, Windows.
10601066

10611067
.. versionadded:: 3.3
10621068

1069+
.. versionchanged:: 3.8
1070+
Windows support was added.
1071+
10631072

10641073
.. _socket-objects:
10651074

Doc/whatsnew/3.8.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,10 @@ convenience functions to automate the necessary tasks usually involved when
556556
creating a server socket, including accepting both IPv4 and IPv6 connections
557557
on the same socket. (Contributed by Giampaolo Rodola in :issue:`17561`.)
558558

559+
The :func:`socket.if_nameindex()`, :func:`socket.if_nametoindex()`, and
560+
:func:`socket.if_indextoname()` functions have been implemented on Windows.
561+
(Contributed by Zackery Spytz in :issue:`37007`.)
562+
559563
shlex
560564
----------
561565

Lib/test/test_socket.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -973,16 +973,18 @@ def testInterfaceNameIndex(self):
973973
self.assertIsInstance(_name, str)
974974
self.assertEqual(name, _name)
975975

976-
@unittest.skipUnless(hasattr(socket, 'if_nameindex'),
977-
'socket.if_nameindex() not available.')
978-
def testInvalidInterfaceNameIndex(self):
979-
# test nonexistent interface index/name
976+
@unittest.skipUnless(hasattr(socket, 'if_indextoname'),
977+
'socket.if_indextoname() not available.')
978+
def testInvalidInterfaceIndexToName(self):
980979
self.assertRaises(OSError, socket.if_indextoname, 0)
981-
self.assertRaises(OSError, socket.if_nametoindex, '_DEADBEEF')
982-
# test with invalid values
983-
self.assertRaises(TypeError, socket.if_nametoindex, 0)
984980
self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF')
985981

982+
@unittest.skipUnless(hasattr(socket, 'if_nametoindex'),
983+
'socket.if_nametoindex() not available.')
984+
def testInvalidInterfaceNameToIndex(self):
985+
self.assertRaises(TypeError, socket.if_nametoindex, 0)
986+
self.assertRaises(OSError, socket.if_nametoindex, '_DEADBEEF')
987+
986988
@unittest.skipUnless(hasattr(sys, 'getrefcount'),
987989
'test needs sys.getrefcount()')
988990
def testRefCountGetNameInfo(self):
@@ -1638,9 +1640,7 @@ def test_getaddrinfo_ipv6_basic(self):
16381640
self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, 0))
16391641

16401642
@unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
1641-
@unittest.skipUnless(
1642-
hasattr(socket, 'if_nameindex'),
1643-
'if_nameindex is not supported')
1643+
@unittest.skipIf(sys.platform == 'win32', 'does not work on Windows')
16441644
@unittest.skipIf(AIX, 'Symbolic scope id does not work')
16451645
def test_getaddrinfo_ipv6_scopeid_symbolic(self):
16461646
# Just pick up any network interface (Linux, Mac OS X)
@@ -1672,9 +1672,7 @@ def test_getaddrinfo_ipv6_scopeid_numeric(self):
16721672
self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, ifindex))
16731673

16741674
@unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
1675-
@unittest.skipUnless(
1676-
hasattr(socket, 'if_nameindex'),
1677-
'if_nameindex is not supported')
1675+
@unittest.skipIf(sys.platform == 'win32', 'does not work on Windows')
16781676
@unittest.skipIf(AIX, 'Symbolic scope id does not work')
16791677
def test_getnameinfo_ipv6_scopeid_symbolic(self):
16801678
# Just pick up any network interface.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Implement :func:`socket.if_nameindex()`, :func:`socket.if_nametoindex()`, and
2+
:func:`socket.if_indextoname()` on Windows.

Modules/socketmodule.c

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ if_indextoname(index) -- return the corresponding interface name\n\
345345

346346
/* Provides the IsWindows7SP1OrGreater() function */
347347
#include <versionhelpers.h>
348+
// For if_nametoindex() and if_indextoname()
349+
#include <iphlpapi.h>
348350

349351
/* remove some flags on older version Windows during run-time.
350352
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\
66676669
A value of None indicates that new socket objects have no timeout.\n\
66686670
When the socket module is first imported, the default is None.");
66696671

6670-
#ifdef HAVE_IF_NAMEINDEX
6672+
#if defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
66716673
/* Python API for getting interface indices and names */
66726674

66736675
static PyObject *
66746676
socket_if_nameindex(PyObject *self, PyObject *arg)
66756677
{
6676-
PyObject *list;
6678+
PyObject *list = PyList_New(0);
6679+
if (list == NULL) {
6680+
return NULL;
6681+
}
6682+
#ifdef MS_WINDOWS
6683+
PMIB_IF_TABLE2 tbl;
6684+
int ret;
6685+
if ((ret = GetIfTable2Ex(MibIfTableRaw, &tbl)) != NO_ERROR) {
6686+
Py_DECREF(list);
6687+
// ret is used instead of GetLastError()
6688+
return PyErr_SetFromWindowsErr(ret);
6689+
}
6690+
for (ULONG i = 0; i < tbl->NumEntries; ++i) {
6691+
MIB_IF_ROW2 r = tbl->Table[i];
6692+
WCHAR buf[NDIS_IF_MAX_STRING_SIZE + 1];
6693+
if ((ret = ConvertInterfaceLuidToNameW(&r.InterfaceLuid, buf,
6694+
Py_ARRAY_LENGTH(buf)))) {
6695+
Py_DECREF(list);
6696+
FreeMibTable(tbl);
6697+
// ret is used instead of GetLastError()
6698+
return PyErr_SetFromWindowsErr(ret);
6699+
}
6700+
PyObject *tuple = Py_BuildValue("Iu", r.InterfaceIndex, buf);
6701+
if (tuple == NULL || PyList_Append(list, tuple) == -1) {
6702+
Py_XDECREF(tuple);
6703+
Py_DECREF(list);
6704+
FreeMibTable(tbl);
6705+
return NULL;
6706+
}
6707+
Py_DECREF(tuple);
6708+
}
6709+
FreeMibTable(tbl);
6710+
return list;
6711+
#else
66776712
int i;
66786713
struct if_nameindex *ni;
66796714

66806715
ni = if_nameindex();
66816716
if (ni == NULL) {
6717+
Py_DECREF(list);
66826718
PyErr_SetFromErrno(PyExc_OSError);
66836719
return NULL;
66846720
}
66856721

6686-
list = PyList_New(0);
6687-
if (list == NULL) {
6688-
if_freenameindex(ni);
6689-
return NULL;
6690-
}
6691-
66926722
#ifdef _Py_MEMORY_SANITIZER
66936723
__msan_unpoison(ni, sizeof(ni));
66946724
__msan_unpoison(&ni[0], sizeof(ni[0]));
@@ -6720,6 +6750,7 @@ socket_if_nameindex(PyObject *self, PyObject *arg)
67206750

67216751
if_freenameindex(ni);
67226752
return list;
6753+
#endif
67236754
}
67246755

67256756
PyDoc_STRVAR(if_nameindex_doc,
@@ -6731,8 +6762,11 @@ static PyObject *
67316762
socket_if_nametoindex(PyObject *self, PyObject *args)
67326763
{
67336764
PyObject *oname;
6765+
#ifdef MS_WINDOWS
6766+
NET_IFINDEX index;
6767+
#else
67346768
unsigned long index;
6735-
6769+
#endif
67366770
if (!PyArg_ParseTuple(args, "O&:if_nametoindex",
67376771
PyUnicode_FSConverter, &oname))
67386772
return NULL;
@@ -6756,7 +6790,11 @@ Returns the interface index corresponding to the interface name if_name.");
67566790
static PyObject *
67576791
socket_if_indextoname(PyObject *self, PyObject *arg)
67586792
{
6793+
#ifdef MS_WINDOWS
6794+
NET_IFINDEX index;
6795+
#else
67596796
unsigned long index;
6797+
#endif
67606798
char name[IF_NAMESIZE + 1];
67616799

67626800
index = PyLong_AsUnsignedLong(arg);
@@ -6776,7 +6814,7 @@ PyDoc_STRVAR(if_indextoname_doc,
67766814
\n\
67776815
Returns the interface name corresponding to the interface index if_index.");
67786816

6779-
#endif /* HAVE_IF_NAMEINDEX */
6817+
#endif // defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
67806818

67816819

67826820
#ifdef CMSG_LEN
@@ -6898,7 +6936,7 @@ static PyMethodDef socket_methods[] = {
68986936
METH_NOARGS, getdefaulttimeout_doc},
68996937
{"setdefaulttimeout", socket_setdefaulttimeout,
69006938
METH_O, setdefaulttimeout_doc},
6901-
#ifdef HAVE_IF_NAMEINDEX
6939+
#if defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
69026940
{"if_nameindex", socket_if_nameindex,
69036941
METH_NOARGS, if_nameindex_doc},
69046942
{"if_nametoindex", socket_if_nametoindex,

PCbuild/_socket.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
</PropertyGroup>
9494
<ItemDefinitionGroup>
9595
<Link>
96-
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
96+
<AdditionalDependencies>ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
9797
</Link>
9898
</ItemDefinitionGroup>
9999
<ItemGroup>

0 commit comments

Comments
 (0)