bpo-37007: Implement socket.if_nametoindex(), if_indextoname() and if_nameindex() on Windows (GH-13522)
This commit is contained in:
parent
fecb75c1bb
commit
8f96c9f8ed
|
@ -1034,10 +1034,13 @@ The :mod:`socket` module also offers various network-related services:
|
||||||
(index int, name string) tuples.
|
(index int, name string) tuples.
|
||||||
:exc:`OSError` if the system call fails.
|
:exc:`OSError` if the system call fails.
|
||||||
|
|
||||||
.. availability:: Unix.
|
.. availability:: Unix, Windows.
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. versionchanged:: 3.8
|
||||||
|
Windows support was added.
|
||||||
|
|
||||||
|
|
||||||
.. function:: if_nametoindex(if_name)
|
.. function:: if_nametoindex(if_name)
|
||||||
|
|
||||||
|
@ -1045,10 +1048,13 @@ The :mod:`socket` module also offers various network-related services:
|
||||||
interface name.
|
interface name.
|
||||||
:exc:`OSError` if no interface with the given name exists.
|
:exc:`OSError` if no interface with the given name exists.
|
||||||
|
|
||||||
.. availability:: Unix.
|
.. availability:: Unix, Windows.
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. versionchanged:: 3.8
|
||||||
|
Windows support was added.
|
||||||
|
|
||||||
|
|
||||||
.. function:: if_indextoname(if_index)
|
.. function:: if_indextoname(if_index)
|
||||||
|
|
||||||
|
@ -1056,10 +1062,13 @@ The :mod:`socket` module also offers various network-related services:
|
||||||
interface index number.
|
interface index number.
|
||||||
:exc:`OSError` if no interface with the given index exists.
|
:exc:`OSError` if no interface with the given index exists.
|
||||||
|
|
||||||
.. availability:: Unix.
|
.. availability:: Unix, Windows.
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. versionchanged:: 3.8
|
||||||
|
Windows support was added.
|
||||||
|
|
||||||
|
|
||||||
.. _socket-objects:
|
.. _socket-objects:
|
||||||
|
|
||||||
|
|
|
@ -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
|
creating a server socket, including accepting both IPv4 and IPv6 connections
|
||||||
on the same socket. (Contributed by Giampaolo Rodola in :issue:`17561`.)
|
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
|
shlex
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -973,16 +973,18 @@ class GeneralModuleTests(unittest.TestCase):
|
||||||
self.assertIsInstance(_name, str)
|
self.assertIsInstance(_name, str)
|
||||||
self.assertEqual(name, _name)
|
self.assertEqual(name, _name)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(socket, 'if_nameindex'),
|
@unittest.skipUnless(hasattr(socket, 'if_indextoname'),
|
||||||
'socket.if_nameindex() not available.')
|
'socket.if_indextoname() not available.')
|
||||||
def testInvalidInterfaceNameIndex(self):
|
def testInvalidInterfaceIndexToName(self):
|
||||||
# test nonexistent interface index/name
|
|
||||||
self.assertRaises(OSError, socket.if_indextoname, 0)
|
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')
|
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'),
|
@unittest.skipUnless(hasattr(sys, 'getrefcount'),
|
||||||
'test needs sys.getrefcount()')
|
'test needs sys.getrefcount()')
|
||||||
def testRefCountGetNameInfo(self):
|
def testRefCountGetNameInfo(self):
|
||||||
|
@ -1638,9 +1640,7 @@ class GeneralModuleTests(unittest.TestCase):
|
||||||
self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, 0))
|
self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, 0))
|
||||||
|
|
||||||
@unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
|
@unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
|
||||||
@unittest.skipUnless(
|
@unittest.skipIf(sys.platform == 'win32', 'does not work on Windows')
|
||||||
hasattr(socket, 'if_nameindex'),
|
|
||||||
'if_nameindex is not supported')
|
|
||||||
@unittest.skipIf(AIX, 'Symbolic scope id does not work')
|
@unittest.skipIf(AIX, 'Symbolic scope id does not work')
|
||||||
def test_getaddrinfo_ipv6_scopeid_symbolic(self):
|
def test_getaddrinfo_ipv6_scopeid_symbolic(self):
|
||||||
# Just pick up any network interface (Linux, Mac OS X)
|
# Just pick up any network interface (Linux, Mac OS X)
|
||||||
|
@ -1672,9 +1672,7 @@ class GeneralModuleTests(unittest.TestCase):
|
||||||
self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, ifindex))
|
self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, ifindex))
|
||||||
|
|
||||||
@unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
|
@unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
|
||||||
@unittest.skipUnless(
|
@unittest.skipIf(sys.platform == 'win32', 'does not work on Windows')
|
||||||
hasattr(socket, 'if_nameindex'),
|
|
||||||
'if_nameindex is not supported')
|
|
||||||
@unittest.skipIf(AIX, 'Symbolic scope id does not work')
|
@unittest.skipIf(AIX, 'Symbolic scope id does not work')
|
||||||
def test_getnameinfo_ipv6_scopeid_symbolic(self):
|
def test_getnameinfo_ipv6_scopeid_symbolic(self):
|
||||||
# Just pick up any network interface.
|
# Just pick up any network interface.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Implement :func:`socket.if_nameindex()`, :func:`socket.if_nametoindex()`, and
|
||||||
|
:func:`socket.if_indextoname()` on Windows.
|
|
@ -345,6 +345,8 @@ http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/net/getaddrinfo.c.diff?r1=1.82&
|
||||||
|
|
||||||
/* Provides the IsWindows7SP1OrGreater() function */
|
/* Provides the IsWindows7SP1OrGreater() function */
|
||||||
#include <versionhelpers.h>
|
#include <versionhelpers.h>
|
||||||
|
// For if_nametoindex() and if_indextoname()
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
|
||||||
/* remove some flags on older version Windows during run-time.
|
/* remove some flags on older version Windows during run-time.
|
||||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596.aspx */
|
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\
|
A value of None indicates that new socket objects have no timeout.\n\
|
||||||
When the socket module is first imported, the default is None.");
|
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 */
|
/* Python API for getting interface indices and names */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
socket_if_nameindex(PyObject *self, PyObject *arg)
|
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;
|
int i;
|
||||||
struct if_nameindex *ni;
|
struct if_nameindex *ni;
|
||||||
|
|
||||||
ni = if_nameindex();
|
ni = if_nameindex();
|
||||||
if (ni == NULL) {
|
if (ni == NULL) {
|
||||||
|
Py_DECREF(list);
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
list = PyList_New(0);
|
|
||||||
if (list == NULL) {
|
|
||||||
if_freenameindex(ni);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _Py_MEMORY_SANITIZER
|
#ifdef _Py_MEMORY_SANITIZER
|
||||||
__msan_unpoison(ni, sizeof(ni));
|
__msan_unpoison(ni, sizeof(ni));
|
||||||
__msan_unpoison(&ni[0], sizeof(ni[0]));
|
__msan_unpoison(&ni[0], sizeof(ni[0]));
|
||||||
|
@ -6720,6 +6750,7 @@ socket_if_nameindex(PyObject *self, PyObject *arg)
|
||||||
|
|
||||||
if_freenameindex(ni);
|
if_freenameindex(ni);
|
||||||
return list;
|
return list;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(if_nameindex_doc,
|
PyDoc_STRVAR(if_nameindex_doc,
|
||||||
|
@ -6731,8 +6762,11 @@ static PyObject *
|
||||||
socket_if_nametoindex(PyObject *self, PyObject *args)
|
socket_if_nametoindex(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *oname;
|
PyObject *oname;
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
NET_IFINDEX index;
|
||||||
|
#else
|
||||||
unsigned long index;
|
unsigned long index;
|
||||||
|
#endif
|
||||||
if (!PyArg_ParseTuple(args, "O&:if_nametoindex",
|
if (!PyArg_ParseTuple(args, "O&:if_nametoindex",
|
||||||
PyUnicode_FSConverter, &oname))
|
PyUnicode_FSConverter, &oname))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -6756,7 +6790,11 @@ Returns the interface index corresponding to the interface name if_name.");
|
||||||
static PyObject *
|
static PyObject *
|
||||||
socket_if_indextoname(PyObject *self, PyObject *arg)
|
socket_if_indextoname(PyObject *self, PyObject *arg)
|
||||||
{
|
{
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
NET_IFINDEX index;
|
||||||
|
#else
|
||||||
unsigned long index;
|
unsigned long index;
|
||||||
|
#endif
|
||||||
char name[IF_NAMESIZE + 1];
|
char name[IF_NAMESIZE + 1];
|
||||||
|
|
||||||
index = PyLong_AsUnsignedLong(arg);
|
index = PyLong_AsUnsignedLong(arg);
|
||||||
|
@ -6776,7 +6814,7 @@ PyDoc_STRVAR(if_indextoname_doc,
|
||||||
\n\
|
\n\
|
||||||
Returns the interface name corresponding to the interface index if_index.");
|
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
|
#ifdef CMSG_LEN
|
||||||
|
@ -6898,7 +6936,7 @@ static PyMethodDef socket_methods[] = {
|
||||||
METH_NOARGS, getdefaulttimeout_doc},
|
METH_NOARGS, getdefaulttimeout_doc},
|
||||||
{"setdefaulttimeout", socket_setdefaulttimeout,
|
{"setdefaulttimeout", socket_setdefaulttimeout,
|
||||||
METH_O, setdefaulttimeout_doc},
|
METH_O, setdefaulttimeout_doc},
|
||||||
#ifdef HAVE_IF_NAMEINDEX
|
#if defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
|
||||||
{"if_nameindex", socket_if_nameindex,
|
{"if_nameindex", socket_if_nameindex,
|
||||||
METH_NOARGS, if_nameindex_doc},
|
METH_NOARGS, if_nameindex_doc},
|
||||||
{"if_nametoindex", socket_if_nametoindex,
|
{"if_nametoindex", socket_if_nametoindex,
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Loading…
Reference in New Issue