[Python-checkins] cpython: Issue #16201: socket: Use inet_pton()/inet_addr() instead of ad-hoc parsing for

charles-francois.natali python-checkins at python.org
Fri Sep 13 19:54:21 CEST 2013


http://hg.python.org/cpython/rev/540a9c69c2ea
changeset:   85679:540a9c69c2ea
user:        Charles-François Natali <cf.natali at gmail.com>
date:        Fri Sep 13 19:53:08 2013 +0200
summary:
  Issue #16201: socket: Use inet_pton()/inet_addr() instead of ad-hoc parsing for
numeric IP addresses.

files:
  Lib/test/test_socket.py |  14 +++++
  Modules/socketmodule.c  |  73 +++++++++++++++++++++-------
  2 files changed, 68 insertions(+), 19 deletions(-)


diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -757,6 +757,20 @@
         if not fqhn in all_host_names:
             self.fail("Error testing host resolution mechanisms. (fqdn: %s, all: %s)" % (fqhn, repr(all_host_names)))
 
+    def test_host_resolution(self):
+        for addr in ['0.1.1.~1', '1+.1.1.1', '::1q', '::1::2',
+                     '1:1:1:1:1:1:1:1:1']:
+            self.assertRaises(OSError, socket.gethostbyname, addr)
+            self.assertRaises(OSError, socket.gethostbyaddr, addr)
+
+        for addr in [support.HOST, '10.0.0.1', '255.255.255.255']:
+            self.assertEqual(socket.gethostbyname(addr), addr)
+
+        # we don't test support.HOSTv6 because there's a chance it doesn't have
+        # a matching name entry (e.g. 'ip6-localhost')
+        for host in [support.HOST]:
+            self.assertIn(host, socket.gethostbyaddr(host)[2])
+
     @unittest.skipUnless(hasattr(socket, 'sethostname'), "test needs socket.sethostname()")
     @unittest.skipUnless(hasattr(socket, 'gethostname'), "test needs socket.gethostname()")
     def test_sethostname(self):
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -425,6 +425,10 @@
 #define INVALID_SOCKET (-1)
 #endif
 
+#ifndef INADDR_NONE
+#define INADDR_NONE (-1)
+#endif
+
 /* XXX There's a problem here: *static* functions are not supposed to have
    a Py prefix (or use CapitalizedWords).  Later... */
 
@@ -787,8 +791,6 @@
 {
     struct addrinfo hints, *res;
     int error;
-    int d1, d2, d3, d4;
-    char ch;
 
     memset((void *) addr_ret, '\0', sizeof(*addr_ret));
     if (name[0] == '\0') {
@@ -837,7 +839,10 @@
         freeaddrinfo(res);
         return siz;
     }
-    if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
+    /* special-case broadcast - inet_addr() below can return INADDR_NONE for
+     * this */ 
+    if (strcmp(name, "255.255.255.255") == 0 ||
+        strcmp(name, "<broadcast>") == 0) {
         struct sockaddr_in *sin;
         if (af != AF_INET && af != AF_UNSPEC) {
             PyErr_SetString(PyExc_OSError,
@@ -853,20 +858,53 @@
         sin->sin_addr.s_addr = INADDR_BROADCAST;
         return sizeof(sin->sin_addr);
     }
-    if (sscanf(name, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 &&
-        0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 &&
-        0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) {
-        struct sockaddr_in *sin;
-        sin = (struct sockaddr_in *)addr_ret;
-        sin->sin_addr.s_addr = htonl(
-            ((long) d1 << 24) | ((long) d2 << 16) |
-            ((long) d3 << 8) | ((long) d4 << 0));
-        sin->sin_family = AF_INET;
+
+    /* avoid a name resolution in case of numeric address */
+#ifdef HAVE_INET_PTON
+    /* check for an IPv4 address */
+    if (af == AF_UNSPEC || af == AF_INET) {
+        struct sockaddr_in *sin = (struct sockaddr_in *)addr_ret;
+        memset(sin, 0, sizeof(*sin));
+        if (inet_pton(AF_INET, name, &sin->sin_addr) > 0) {
+            sin->sin_family = AF_INET;
 #ifdef HAVE_SOCKADDR_SA_LEN
-        sin->sin_len = sizeof(*sin);
-#endif
-        return 4;
-    }
+            sin->sin_len = sizeof(*sin);
+#endif
+            return 4;
+        }
+    }
+#ifdef ENABLE_IPV6
+    /* check for an IPv6 address - if the address contains a scope ID, we
+     * fallback to getaddrinfo(), which can handle translation from interface
+     * name to interface index */
+    if ((af == AF_UNSPEC || af == AF_INET6) && !strchr(name, '%')) {
+        struct sockaddr_in6 *sin = (struct sockaddr_in6 *)addr_ret;
+        memset(sin, 0, sizeof(*sin));
+        if (inet_pton(AF_INET6, name, &sin->sin6_addr) > 0) {
+            sin->sin6_family = AF_INET6;
+#ifdef HAVE_SOCKADDR_SA_LEN
+            sin->sin6_len = sizeof(*sin);
+#endif
+            return 16;
+        }
+    }
+#endif /* ENABLE_IPV6 */
+#else /* HAVE_INET_PTON */
+    /* check for an IPv4 address */
+    if (af == AF_INET || af == AF_UNSPEC) {
+        struct sockaddr_in *sin = (struct sockaddr_in *)addr_ret;
+        memset(sin, 0, sizeof(*sin));
+        if ((sin->sin_addr.s_addr = inet_addr(name)) != INADDR_NONE) {
+            sin->sin_family = AF_INET;
+#ifdef HAVE_SOCKADDR_SA_LEN
+            sin->sin_len = sizeof(*sin);
+#endif
+            return 4;
+        }
+    }   
+#endif /* HAVE_INET_PTON */
+
+    /* perform a name resolution */
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = af;
     Py_BEGIN_ALLOW_THREADS
@@ -4896,9 +4934,6 @@
 static PyObject*
 socket_inet_aton(PyObject *self, PyObject *args)
 {
-#ifndef INADDR_NONE
-#define INADDR_NONE (-1)
-#endif
 #ifdef HAVE_INET_ATON
     struct in_addr buf;
 #endif

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list