Index: Doc/lib/libsocket.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libsocket.tex,v retrieving revision 1.69 diff -u -u -r1.69 libsocket.tex --- Doc/lib/libsocket.tex 20 Mar 2003 17:58:12 -0000 1.69 +++ Doc/lib/libsocket.tex 19 Apr 2003 18:08:49 -0000 @@ -150,6 +150,11 @@ for a few symbols, default values are provided. \end{datadesc} +\begin{datadesc}{has_ipv6} +This constant contains a boolean value which indicates if IPv6 is +supported on this platform. +\end{datadesc} + \begin{funcdesc}{getaddrinfo}{host, port\optional{, family, socktype, proto, flags}} Resolves the \var{host}/\var{port} argument, into a sequence of @@ -347,6 +352,43 @@ \function{inet_ntoa()} does not support IPv6, and \function{getnameinfo()} should be used instead for IPv4/v6 dual stack support. +\end{funcdesc} + +\begin{funcdesc}{inet_pton}{address_family, ip_string} +Convert an IP address from its family-specific string format to a packed, +binary format. + +Supported values for address_family are currently \constant{AF_INET} +and \constant{AF_INET6}. + +\function{inet_pton()} is useful when a library or network protocol calls for +an object of type \ctype{struct in_addr} (similar to \function{inet_aton()}) +or \ctype{struct in6_addr}. + +If the IP address string passed to this function is invalid, +\exception{socket.error} will be raised. Note that exactly what is valid +depends on both the value of \var{address_family} and the underlying +implementation of \cfunction{inet_pton()}. +\versionadded{2.3} +\end{funcdesc} + +\begin{funcdesc}{inet_ntop}{address_family, packed_ip} +Convert a packed IP address (a string of some number of characters) to its +standard, family-specific string representation (for example, '7.10.0.5' or +'5aef:2b::8') + +Supported values for address_family are currently \constant{AF_INET} +and \constant{AF_INET6}. + +\function{inet_pton()} is useful when a library or network protocol calls for +an object of type \ctype{struct in_addr} (similar to \function{inet_aton()}) +or \ctype{struct in6_addr}. + +If the string passed to this function is not the correct length for the +specified address family, \exception{ValueError} will be raised. +A \exception{socket.error} is raised for errors from the call to +\function{inet_ntop()}. +\versionadded{2.3} \end{funcdesc} \begin{funcdesc}{getdefaulttimeout}{} Index: Lib/socket.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/socket.py,v retrieving revision 1.36 diff -u -u -r1.36 socket.py --- Lib/socket.py 30 Mar 2003 04:54:23 -0000 1.36 +++ Lib/socket.py 19 Apr 2003 18:08:51 -0000 @@ -30,6 +30,7 @@ SocketType -- type object for socket objects error -- exception raised for I/O errors +has_ipv6 -- boolean value indicating if IPv6 is supported Integer constants: Index: Lib/test/test_socket.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_socket.py,v retrieving revision 1.61 diff -u -u -r1.61 test_socket.py --- Lib/test/test_socket.py 26 Dec 2002 17:04:45 -0000 1.61 +++ Lib/test/test_socket.py 19 Apr 2003 18:08:52 -0000 @@ -318,6 +318,65 @@ # Check that setting it to an invalid type raises TypeError self.assertRaises(TypeError, socket.setdefaulttimeout, "spam") + def testIPv4toString(self): + from socket import inet_aton as f, inet_pton, AF_INET + g = lambda a: inet_pton(AF_INET, a) + + self.assertEquals('\x00\x00\x00\x00', f('0.0.0.0')) + self.assertEquals('\xff\x00\xff\x00', f('255.0.255.0')) + self.assertEquals('\xaa\xaa\xaa\xaa', f('170.170.170.170')) + self.assertEquals('\x01\x02\x03\x04', f('1.2.3.4')) + + self.assertEquals('\x00\x00\x00\x00', g('0.0.0.0')) + self.assertEquals('\xff\x00\xff\x00', g('255.0.255.0')) + self.assertEquals('\xaa\xaa\xaa\xaa', g('170.170.170.170')) + + def testIPv6toString(self): + try: + from socket import inet_pton, AF_INET6, has_ipv6 + if not has_ipv6: + return + except ImportError: + return + f = lambda a: inet_pton(AF_INET6, a) + + self.assertEquals('\x00' * 16, f('::')) + self.assertEquals('\x00' * 16, f('0::0')) + self.assertEquals('\x00\x01' + '\x00' * 14, f('1::')) + self.assertEquals( + '\x45\xef\x76\xcb\x00\x1a\x56\xef\xaf\xeb\x0b\xac\x19\x24\xae\xae', + f('45ef:76cb:1a:56ef:afeb:bac:1924:aeae') + ) + + def testStringToIPv4(self): + from socket import inet_ntoa as f, inet_ntop, AF_INET + g = lambda a: inet_ntop(AF_INET, a) + + self.assertEquals('1.0.1.0', f('\x01\x00\x01\x00')) + self.assertEquals('170.85.170.85', f('\xaa\x55\xaa\x55')) + self.assertEquals('255.255.255.255', f('\xff\xff\xff\xff')) + self.assertEquals('1.2.3.4', f('\x01\x02\x03\x04')) + + self.assertEquals('1.0.1.0', g('\x01\x00\x01\x00')) + self.assertEquals('170.85.170.85', g('\xaa\x55\xaa\x55')) + self.assertEquals('255.255.255.255', g('\xff\xff\xff\xff')) + + def testStringToIPv6(self): + try: + from socket import inet_ntop, AF_INET6, has_ipv6 + if not has_ipv6: + return + except ImportError: + return + f = lambda a: inet_ntop(AF_INET6, a) + + self.assertEquals('::', f('\x00' * 16)) + self.assertEquals('::1', f('\x00' * 15 + '\x01')) + self.assertEquals( + 'aef:b01:506:1001:ffff:9997:55:170', + f('\x0a\xef\x0b\x01\x05\x06\x10\x01\xff\xff\x99\x97\x00\x55\x01\x70') + ) + # XXX The following don't test module-level functionality... def testSockName(self): Index: Misc/ACKS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/ACKS,v retrieving revision 1.232 diff -u -u -r1.232 ACKS --- Misc/ACKS 25 Mar 2003 10:20:54 -0000 1.232 +++ Misc/ACKS 19 Apr 2003 18:08:53 -0000 @@ -83,6 +83,7 @@ Tarn Weisner Burton Lee Busby Ralph Butler +Jp Calderone Daniel Calvelo Brett Cannon Mike Carlton Index: Misc/NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.734 diff -u -u -r1.734 NEWS --- Misc/NEWS 18 Apr 2003 21:04:39 -0000 1.734 +++ Misc/NEWS 19 Apr 2003 18:08:58 -0000 @@ -352,6 +352,10 @@ zlib test suite using the unittest module. (SF bug #640230 and patch #678531.) +- The socket module now provides the functions inet_pton and inet_ntop + for converting between string and packed representation of IP addresses. + See SF patch #658327. + - Added an itertools module containing high speed, memory efficient looping constructs inspired by tools from Haskell and SML. Index: Modules/socketmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/socketmodule.c,v retrieving revision 1.259 diff -u -u -r1.259 socketmodule.c --- Modules/socketmodule.c 18 Apr 2003 10:39:53 -0000 1.259 +++ Modules/socketmodule.c 19 Apr 2003 18:09:00 -0000 @@ -35,6 +35,7 @@ --> List of (family, socktype, proto, canonname, sockaddr) - socket.getnameinfo(sockaddr, flags) --> (host, port) - socket.AF_INET, socket.SOCK_STREAM, etc.: constants from +- socket.has_ipv6: boolean value indicating if IPv6 is supported - socket.inet_aton(IP address) -> 32-bit packed IP representation - socket.inet_ntoa(packed IP) -> IP address string - socket.getdefaulttimeout() -> None | float @@ -62,6 +63,9 @@ #include "Python.h" +#undef MAX +#define MAX(x, y) ((x) < (y) ? (y) : (x)) + /* Socket object documentation */ PyDoc_STRVAR(sock_doc, "socket([family[, type[, proto]]]) -> socket object\n\ @@ -2776,6 +2780,100 @@ return PyString_FromString(inet_ntoa(packed_addr)); } +#ifdef HAVE_INET_PTON + +PyDoc_STRVAR(inet_pton_doc, +"inet_pton(af, ip) -> packed IP address string\n\ +\n\ +Convert an IP address from string format to a packed string suitable\n\ +for use with low-level network functions."); + +static PyObject * +socket_inet_pton(PyObject *self, PyObject *args) +{ + int af; + char* ip; + int retval; + char packed[MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; + + if (!PyArg_ParseTuple(args, "is:inet_pton", &af, &ip)) { + return NULL; + } + + retval = inet_pton(af, ip, packed); + if (retval < 0) { + PyErr_SetFromErrno(socket_error); + return NULL; + } else if (retval == 0) { + PyErr_SetString(socket_error, + "illegal IP address string passed to inet_pton"); + return NULL; + } else if (af == AF_INET) { + return PyString_FromStringAndSize(packed, + sizeof(struct in_addr)); + } else if (af == AF_INET6) { + return PyString_FromStringAndSize(packed, + sizeof(struct in6_addr)); + } else { + PyErr_SetString(socket_error, "unknown address family"); + return NULL; + } +} + +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."); + +static PyObject * +socket_inet_ntop(PyObject *self, PyObject *args) +{ + int af; + char* packed; + int len; + const char* retval; + char ip[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; + + /* Guarantee NUL-termination for PyString_FromString() below */ + memset((void *) &ip[0], '\0', sizeof(ip) + 1); + + if (!PyArg_ParseTuple(args, "is#:inet_ntop", &af, &packed, &len)) { + return NULL; + } + + if (af == AF_INET) { + if (len != sizeof(struct in_addr)) { + PyErr_SetString(PyExc_ValueError, + "invalid length of packed IP address string"); + return NULL; + } + } else if (af == AF_INET6) { + if (len != sizeof(struct in6_addr)) { + PyErr_SetString(PyExc_ValueError, + "invalid length of packed IP address string"); + return NULL; + } + } else { + PyErr_Format(PyExc_ValueError, + "unknown address family %d", af); + return NULL; + } + + retval = inet_ntop(af, packed, ip, sizeof(ip)); + if (!retval) { + PyErr_SetFromErrno(socket_error); + return NULL; + } else { + return PyString_FromString(retval); + } + + /* NOTREACHED */ + PyErr_SetString(PyExc_RuntimeError, "invalid handling of inet_ntop"); + return NULL; +} + +#endif /* HAVE_INET_PTON */ + /* Python interface to getaddrinfo(host, port). */ /*ARGSUSED*/ @@ -3035,6 +3133,12 @@ METH_VARARGS, inet_aton_doc}, {"inet_ntoa", socket_inet_ntoa, METH_VARARGS, inet_ntoa_doc}, +#ifdef HAVE_INET_PTON + {"inet_pton", socket_inet_pton, + METH_VARARGS, inet_pton_doc}, + {"inet_ntop", socket_inet_ntop, + METH_VARARGS, inet_ntop_doc}, +#endif {"getaddrinfo", socket_getaddrinfo, METH_VARARGS, getaddrinfo_doc}, {"getnameinfo", socket_getnameinfo, @@ -3178,7 +3282,7 @@ PyMODINIT_FUNC init_socket(void) { - PyObject *m; + PyObject *m, *has_ipv6; if (!os_init()) return; @@ -3214,6 +3318,14 @@ (PyObject *)&sock_type) != 0) return; +#ifdef ENABLE_IPV6 + has_ipv6 = Py_True; +#else + has_ipv6 = Py_False; +#endif + Py_INCREF(has_ipv6); + PyModule_AddObject(m, "has_ipv6", has_ipv6); + /* Export C API */ if (PyModule_AddObject(m, PySocket_CAPI_NAME, PyCObject_FromVoidPtr((void *)&PySocketModuleAPI, NULL) @@ -3800,6 +3912,7 @@ #ifndef HAVE_INET_PTON /* Simplistic emulation code for inet_pton that only works for IPv4 */ +/* These are not exposed because they do not set errno properly */ int inet_pton(int af, const char *src, void *dst)