From da0fc14d46a174b921ea0f68e7996bf1cda9b95d Mon Sep 17 00:00:00 2001 From: Atsuo Ishimoto Date: Mon, 16 Jul 2012 15:16:54 +0900 Subject: [PATCH 1/3] Issue #7171: Add Windows implementation of ``inet_ntop`` and ``inet_pton`` to socket module. --- Doc/library/socket.rst | 4 +- Lib/test/test_socket.py | 16 ++++++ Modules/socketmodule.c | 108 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 658b8c9aaae..b01238c29dd 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -594,7 +594,7 @@ The module :mod:`socket` exports the following constants and functions: both the value of *address_family* and the underlying implementation of :c:func:`inet_pton`. - Availability: Unix (maybe not all platforms). + Availability: Unix (maybe not all platforms), Windows. .. function:: inet_ntop(address_family, packed_ip) @@ -610,7 +610,7 @@ The module :mod:`socket` exports the following constants and functions: specified address family, :exc:`ValueError` will be raised. A :exc:`OSError` is raised for errors from the call to :func:`inet_ntop`. - Availability: Unix (maybe not all platforms). + Availability: Unix (maybe not all platforms), Windows. .. diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index beff31a7f59..eb8619f75c7 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -980,6 +980,14 @@ class GeneralModuleTests(unittest.TestCase): return except ImportError: return + + if sys.platform == "win32": + try: + inet_pton(AF_INET6, '::') + except OSError as e: + if e.winerror == 10022: + return # IPv6 might not be installed on this PC + f = lambda a: inet_pton(AF_INET6, a) assertInvalid = lambda a: self.assertRaises( (OSError, ValueError), f, a @@ -1058,6 +1066,14 @@ class GeneralModuleTests(unittest.TestCase): return except ImportError: return + + if sys.platform == "win32": + try: + inet_ntop(AF_INET6, b'\x00' * 16) + except OSError as e: + if e.winerror == 10022: + return # IPv6 might not be installed on this PC + f = lambda a: inet_ntop(AF_INET6, a) assertInvalid = lambda a: self.assertRaises( (OSError, ValueError), f, a diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 0396bf4ead5..6c4b050371a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5023,7 +5023,7 @@ socket_inet_ntoa(PyObject *self, PyObject *args) return PyUnicode_FromString(inet_ntoa(packed_addr)); } -#ifdef HAVE_INET_PTON +#if defined(HAVE_INET_PTON) || defined(MS_WINDOWS) PyDoc_STRVAR(inet_pton_doc, "inet_pton(af, ip) -> packed IP address string\n\ @@ -5031,6 +5031,10 @@ PyDoc_STRVAR(inet_pton_doc, Convert an IP address from string format to a packed string suitable\n\ for use with low-level network functions."); +#endif + +#ifdef HAVE_INET_PTON + static PyObject * socket_inet_pton(PyObject *self, PyObject *args) { @@ -5075,12 +5079,52 @@ socket_inet_pton(PyObject *self, PyObject *args) return NULL; } } +#elif defined(MS_WINDOWS) + +static PyObject * +socket_inet_pton(PyObject *self, PyObject *args) +{ + int af; + char* ip; + struct sockaddr_in6 addr; + INT ret, size; + + if (!PyArg_ParseTuple(args, "is:inet_pton", &af, &ip)) { + return NULL; + } + + size = sizeof(addr); + ret = WSAStringToAddressA(ip, af, NULL, (LPSOCKADDR)&addr, &size); + + if (ret) { + PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError()); + return NULL; + } else if(af == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in*)&addr; + return PyBytes_FromStringAndSize((const char *)&(addr4->sin_addr), + sizeof(addr4->sin_addr)); + } else if (af == AF_INET6) { + return PyBytes_FromStringAndSize((const char *)&(addr.sin6_addr), + sizeof(addr.sin6_addr)); + } else { + PyErr_SetString(PyExc_OSError, "unknown address family"); + return NULL; + } +} + +#endif + +#if defined(HAVE_INET_PTON) || defined(MS_WINDOWS) PyDoc_STRVAR(inet_ntop_doc, "inet_ntop(af, packed_ip) -> string formatted IP address\n\ \n\ Convert a packed IP address of the given family to string format."); +#endif + + +#ifdef HAVE_INET_PTON static PyObject * socket_inet_ntop(PyObject *self, PyObject *args) { @@ -5134,6 +5178,66 @@ socket_inet_ntop(PyObject *self, PyObject *args) return NULL; } +#elif defined(MS_WINDOWS) + +static PyObject * +socket_inet_ntop(PyObject *self, PyObject *args) +{ + int af; + char* packed; + int len; + struct sockaddr_in6 addr; + DWORD addrlen, ret, retlen; + char ip[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; + + /* Guarantee NUL-termination for PyUnicode_FromString() below */ + memset((void *) &ip[0], '\0', sizeof(ip)); + + if (!PyArg_ParseTuple(args, "iy#:inet_ntop", &af, &packed, &len)) { + return NULL; + } + + if (af == AF_INET) { + struct sockaddr_in * addr4 = (struct sockaddr_in *)&addr; + + if (len != sizeof(struct in_addr)) { + PyErr_SetString(PyExc_ValueError, + "invalid length of packed IP address string"); + return NULL; + } + memset(addr4, 0, sizeof(struct sockaddr_in)); + addr4->sin_family = AF_INET; + memcpy(&(addr4->sin_addr), packed, sizeof(addr4->sin_addr)); + addrlen = sizeof(struct sockaddr_in); + } else if (af == AF_INET6) { + if (len != sizeof(struct in6_addr)) { + PyErr_SetString(PyExc_ValueError, + "invalid length of packed IP address string"); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + memcpy(&(addr.sin6_addr), packed, sizeof(addr.sin6_addr)); + addrlen = sizeof(addr); + } else { + PyErr_Format(PyExc_ValueError, + "unknown address family %d", af); + return NULL; + } + + retlen = sizeof(ip); + ret = WSAAddressToStringA((struct sockaddr*)&addr, addrlen, NULL, + ip, &retlen); + + if (ret) { + PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError()); + return NULL; + } else { + return PyUnicode_FromString(ip); + } +} + #endif /* HAVE_INET_PTON */ /* Python interface to getaddrinfo(host, port). */ @@ -5600,7 +5704,7 @@ static PyMethodDef socket_methods[] = { METH_VARARGS, inet_aton_doc}, {"inet_ntoa", socket_inet_ntoa, METH_VARARGS, inet_ntoa_doc}, -#ifdef HAVE_INET_PTON +#if defined(HAVE_INET_PTON) || defined(MS_WINDOWS) {"inet_pton", socket_inet_pton, METH_VARARGS, inet_pton_doc}, {"inet_ntop", socket_inet_ntop, From 8ec784c2df2cfe9783d6434a46815c8d0c4f29f0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Nov 2013 13:43:22 -0500 Subject: [PATCH 2/3] Issue #7171: Update syntax to replace MAX in favor of Py_MAX (matching implementation for Unix). --- Modules/socketmodule.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 6c4b050371a..fa61cfd02f8 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5188,7 +5188,11 @@ socket_inet_ntop(PyObject *self, PyObject *args) int len; struct sockaddr_in6 addr; DWORD addrlen, ret, retlen; - char ip[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; +#ifdef ENABLE_IPV6 + char ip[Py_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; +#else + char ip[INET_ADDRSTRLEN + 1]; +#endif /* Guarantee NUL-termination for PyUnicode_FromString() below */ memset((void *) &ip[0], '\0', sizeof(ip)); From 6bdc498734e83afbfd8d80c2f70b5d3800d65223 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Nov 2013 14:02:04 -0500 Subject: [PATCH 3/3] Normalize whitespace --- Lib/test/test_socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index eb8619f75c7..a7c25160750 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -980,14 +980,14 @@ class GeneralModuleTests(unittest.TestCase): return except ImportError: return - + if sys.platform == "win32": try: inet_pton(AF_INET6, '::') except OSError as e: if e.winerror == 10022: return # IPv6 might not be installed on this PC - + f = lambda a: inet_pton(AF_INET6, a) assertInvalid = lambda a: self.assertRaises( (OSError, ValueError), f, a