]> granicus.if.org Git - python/commitdiff
Patch by Jp Calderone:
authorGuido van Rossum <guido@python.org>
Fri, 25 Apr 2003 05:48:32 +0000 (05:48 +0000)
committerGuido van Rossum <guido@python.org>
Fri, 25 Apr 2003 05:48:32 +0000 (05:48 +0000)
- 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.

This still needs a bit of work in the doc area, because it is not
available on all platforms (especially not on Windows).

Doc/lib/libsocket.tex
Lib/socket.py
Lib/test/test_socket.py
Misc/ACKS
Misc/NEWS
Modules/socketmodule.c

index 4febf220c61839c46676120f77a46d48ed110beb..73c0ff311f0c7c0e8ed281c9fcdcb1cd5c4b4d76 100644 (file)
@@ -150,6 +150,11 @@ those symbols that are defined in the \UNIX{} header files are defined;
 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
@@ -349,6 +354,43 @@ length, \exception{socket.error} will be raised.
 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}{}
 Return the default timeout in floating seconds for new socket objects.
 A value of \code{None} indicates that new socket objects have no timeout.
index cb0181b2a6c48db62f55924db00935b780c488bd..12f3c052d2955245b4e00ea9c7958a944649934f 100644 (file)
@@ -30,6 +30,7 @@ Special objects:
 
 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 bca1c6cabd12da9ab71f8ed33ba5c3115a5317e0..16a39896b0c41d5736c23522823458d864fbb451 100644 (file)
@@ -318,6 +318,65 @@ class GeneralModuleTests(unittest.TestCase):
         # 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 f99f6584a919a0168a7e1640457c7fad1008ef01..2fbe9af5c0b4a9e964c6126b3e10ada02ac78439 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -83,6 +83,7 @@ Alastair Burt
 Tarn Weisner Burton
 Lee Busby
 Ralph Butler
+Jp Calderone
 Daniel Calvelo
 Brett Cannon
 Mike Carlton
index d666762ea0efb69fe97683805cb9a15207fd263f..8c1d7c531d56b0b66e0e7f8a114cde46fe738005 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -383,6 +383,10 @@ Extension modules
   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 ba64cb86864fb41fb575f92999fef5ad46823a7d..fd6ede0ea7826b5d4189f9d5b139f3b43aeb88ba 100644 (file)
@@ -35,6 +35,7 @@ Module interface:
        --> List of (family, socktype, proto, canonname, sockaddr)
 - socket.getnameinfo(sockaddr, flags) --> (host, port)
 - socket.AF_INET, socket.SOCK_STREAM, etc.: constants from <socket.h>
+- 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 @@ Local naming conventions:
 
 #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 @@ socket_inet_ntoa(PyObject *self, PyObject *args)
        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 @@ static PyMethodDef socket_methods[] = {
         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 @@ See the socket module for documentation.");
 PyMODINIT_FUNC
 init_socket(void)
 {
-       PyObject *m;
+       PyObject *m, *has_ipv6;
 
        if (!os_init())
                return;
@@ -3214,6 +3318,14 @@ init_socket(void)
                               (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 @@ init_socket(void)
 #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)