Bug 344809 - numerous platform inconsistencies of PR_StringToNetAddr for IPv4 addresses. r=nelson NSPR_HEAD_20091218
authordwitte%mozilla.com
Fri, 18 Dec 2009 22:11:48 +0000
changeset 4192 150e90a801ceb619965526ed00ba4d443629007f
parent 4190 d530a967581c260a8eb053199db36740b9e27f2b (current diff)
parent 4191 60feb86ade8e7b06fcee2d17ab8096ad02ef6d5a (diff)
child 4193 fcb4104d243a4a8efed7934203b1f84f42a8b5bf
push idunknown
push userunknown
push dateunknown
reviewersnelson
bugs344809
Bug 344809 - numerous platform inconsistencies of PR_StringToNetAddr for IPv4 addresses. r=nelson
pr/src/Makefile.in
pr/src/misc/Makefile.in
pr/src/misc/aton.c
pr/src/misc/prnetdb.c
--- a/pr/src/Makefile.in
+++ b/pr/src/Makefile.in
@@ -252,16 +252,17 @@ OBJS = \
     misc/$(OBJDIR)/prerror.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prerrortable.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prinit.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prinrval.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/pripc.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prlog2.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prlong.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prnetdb.$(OBJ_SUFFIX) \
+    misc/$(OBJDIR)/aton.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prolock.$(OBJ_SUFFIX)	 \
     misc/$(OBJDIR)/prrng.$(OBJ_SUFFIX)	 \
     misc/$(OBJDIR)/prsystem.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prthinfo.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prtpool.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prtrace.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prtime.$(OBJ_SUFFIX)
 
--- a/pr/src/misc/Makefile.in
+++ b/pr/src/misc/Makefile.in
@@ -56,16 +56,17 @@ CSRCS = \
 	prerror.c  \
 	prerrortable.c  \
 	prinit.c   \
 	prinrval.c \
 	pripc.c \
 	prlog2.c   \
 	prlong.c   \
 	prnetdb.c  \
+	aton.c  \
 	prolock.c  \
 	prrng.c    \
 	prsystem.c \
 	prtime.c   \
 	prthinfo.c \
 	prtpool.c \
 	prtrace.c  \
 	$(NULL)
new file mode 100644
--- /dev/null
+++ b/pr/src/misc/aton.c
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*******************************************************************************
+ * The following function pr_inet_aton is based on the BSD function inet_aton
+ * with some modifications. The license and copyright notices applying to this
+ * function appear below. Modifications are also according to the license below.
+ ******************************************************************************/
+
+#include "prnetdb.h"
+
+/*
+ * Copyright (c) 1983, 1990, 1993
+ *    The Regents of the University of California.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ * 
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define XX 127
+static const unsigned char index_hex[256] = {
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+     0, 1, 2, 3,  4, 5, 6, 7,  8, 9,XX,XX, XX,XX,XX,XX,
+    XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+
+static PRBool _isdigit(char c) { return c >= '0' && c <= '9'; }
+static PRBool _isxdigit(char c) { return index_hex[(unsigned char) c] != XX; }
+static PRBool _isspace(char c) { return c == ' ' || (c >= '\t' && c <= '\r'); }
+#undef XX
+
+int
+pr_inet_aton(const char *cp, PRUint32 *addr)
+{
+    PRUint32 val;
+    int base, n;
+    char c;
+    PRUint8 parts[4];
+    PRUint8 *pp = parts;
+    int digit;
+
+    c = *cp;
+    for (;;) {
+        /*
+         * Collect number up to ``.''.
+         * Values are specified as for C:
+         * 0x=hex, 0=octal, isdigit=decimal.
+         */
+        if (!_isdigit(c))
+            return (0);
+        val = 0; base = 10; digit = 0;
+        if (c == '0') {
+            c = *++cp;
+            if (c == 'x' || c == 'X')
+                base = 16, c = *++cp;
+            else {
+                base = 8;
+                digit = 1;
+            }
+        }
+        for (;;) {
+            if (_isdigit(c)) {
+                if (base == 8 && (c == '8' || c == '9'))
+                    return (0);
+                val = (val * base) + (c - '0');
+                c = *++cp;
+                digit = 1;
+            } else if (base == 16 && _isxdigit(c)) {
+                val = (val << 4) + index_hex[(unsigned char) c];
+                c = *++cp;
+                digit = 1;
+            } else
+                break;
+        }
+        if (c == '.') {
+            /*
+             * Internet format:
+             *    a.b.c.d
+             *    a.b.c    (with c treated as 16 bits)
+             *    a.b    (with b treated as 24 bits)
+             */
+            if (pp >= parts + 3 || val > 0xffU)
+                return (0);
+            *pp++ = val;
+            c = *++cp;
+        } else
+            break;
+    }
+    /*
+     * Check for trailing characters.
+     */
+    if (c != '\0' && !_isspace(c))
+        return (0);
+    /*
+     * Did we get a valid digit?
+     */
+    if (!digit)
+        return (0);
+    /*
+     * Concoct the address according to
+     * the number of parts specified.
+     */
+    n = pp - parts + 1;
+    switch (n) {
+    case 1:                /*%< a -- 32 bits */
+        break;
+
+    case 2:                /*%< a.b -- 8.24 bits */
+        if (val > 0xffffffU)
+            return (0);
+        val |= parts[0] << 24;
+        break;
+
+    case 3:                /*%< a.b.c -- 8.8.16 bits */
+        if (val > 0xffffU)
+            return (0);
+        val |= (parts[0] << 24) | (parts[1] << 16);
+        break;
+
+    case 4:                /*%< a.b.c.d -- 8.8.8.8 bits */
+        if (val > 0xffU)
+            return (0);
+        val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+        break;
+    }
+    *addr = PR_htonl(val);
+    return (1);
+}
+
--- a/pr/src/misc/prnetdb.c
+++ b/pr/src/misc/prnetdb.c
@@ -1530,17 +1530,18 @@ PR_IsNetAddrType(const PRNetAddr *addr, 
                     && addr->inet.ip == htonl(INADDR_LOOPBACK)) {
                 return PR_TRUE;
             }
         }
     }
     return PR_FALSE;
 }
 
-#ifndef _PR_HAVE_INET_NTOP
+extern int pr_inet_aton(const char *cp, PRUint32 *addr);
+
 #define XX 127
 static const unsigned char index_hex[256] = {
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
      0, 1, 2, 3,  4, 5, 6, 7,  8, 9,XX,XX, XX,XX,XX,XX,
     XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
@@ -1663,17 +1664,18 @@ static int StringToV6Addr(const char *st
             addr->pr_s6_addr16[tosection--] = 0;
         }
     } else if (section != 8) {
         return 0; /* too short */
     }
     return 1;
 }
 #undef XX
-            
+
+#ifndef _PR_HAVE_INET_NTOP
 static const char *basis_hex = "0123456789abcdef";
 
 /*
  * V6AddrToString() returns a pointer to the buffer containing
  * the text string if the conversion succeeds, and NULL otherwise.
  * (Same as inet_ntop(AF_INET6, addr, buf, size), except that errno
  * is not set on failure.)
  */
@@ -1769,17 +1771,16 @@ static const char *V6AddrToString(
         STUFF(basis_hex[val & 0xf]);
         section++;
         if (section < 8 && section != double_colon) STUFF(':');
     }
     STUFF('\0');
     return bufcopy;
 #undef STUFF    
 }
-
 #endif /* !_PR_HAVE_INET_NTOP */
 
 /*
  * Convert an IPv4 addr to an (IPv4-mapped) IPv6 addr
  */
 PR_IMPLEMENT(void) PR_ConvertIPv4AddrToIPv6(PRUint32 v4addr, PRIPv6Addr *v6addr)
 {
     PRUint8 *dstp;
@@ -2136,22 +2137,16 @@ PR_IMPLEMENT(const char *) PR_GetCanonNa
 #if defined(_PR_HAVE_GETADDRINFO)
 static PRStatus pr_StringToNetAddrGAI(const char *string, PRNetAddr *addr)
 {
     PRADDRINFO *res, hints;
     int rv;  /* 0 for success, or the error code EAI_xxx */
     PRNetAddr laddr;
     PRStatus status = PR_SUCCESS;
 
-    if (NULL == addr)
-    {
-        PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
-        return PR_FAILURE;
-    }
-
     memset(&hints, 0, sizeof(hints));
     hints.ai_flags = AI_NUMERICHOST;
     hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
 
     rv = GETADDRINFO(string, NULL, &hints, &res);
     if (rv != 0)
     {
@@ -2178,92 +2173,69 @@ static PRStatus pr_StringToNetAddrGAI(co
         status = PR_FAILURE;
     }
 
     FREEADDRINFO(res);
     return status;
 }
 #endif  /* _PR_HAVE_GETADDRINFO */
 
-#if !defined(_PR_HAVE_GETADDRINFO) || defined(_PR_INET6_PROBE) || defined(DARWIN)
 static PRStatus pr_StringToNetAddrFB(const char *string, PRNetAddr *addr)
 {
-    PRStatus status = PR_SUCCESS;
     PRIntn rv;
 
-#if defined(_PR_HAVE_INET_NTOP)
-    rv = inet_pton(AF_INET6, string, &addr->ipv6.ip);
+    rv = pr_inet_aton(string, &addr->inet.ip);
+    if (1 == rv)
+    {
+        addr->raw.family = AF_INET;
+        return PR_SUCCESS;
+    }
+
+    PR_ASSERT(0 == rv);
+    /* clean up after the failed call */
+    memset(&addr->inet.ip, 0, sizeof(addr->inet.ip));
+
+    rv = StringToV6Addr(string, &addr->ipv6.ip);
     if (1 == rv)
     {
         addr->raw.family = PR_AF_INET6;
-    }
-    else
-    {
-        PR_ASSERT(0 == rv);
-        /* clean up after the failed inet_pton() call */
-        memset(&addr->ipv6.ip, 0, sizeof(addr->ipv6.ip));
-        rv = inet_pton(AF_INET, string, &addr->inet.ip);
-        if (1 == rv)
-        {
-            addr->raw.family = AF_INET;
-        }
-        else
-        {
-            PR_ASSERT(0 == rv);
-            PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
-            status = PR_FAILURE;
-        }
-    }
-#else /* _PR_HAVE_INET_NTOP */
-    rv = StringToV6Addr(string, &addr->ipv6.ip);
-    if (1 == rv) {
-        addr->raw.family = PR_AF_INET6;
         return PR_SUCCESS;
     }
+
     PR_ASSERT(0 == rv);
-    /* clean up after the failed StringToV6Addr() call */
-    memset(&addr->ipv6.ip, 0, sizeof(addr->ipv6.ip));
-
-    addr->inet.family = AF_INET;
-    addr->inet.ip = inet_addr(string);
-    if ((PRUint32) -1 == addr->inet.ip)
-    {
-        /*
-         * The string argument is a malformed address string.
-         */
-        PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
-        status = PR_FAILURE;
-    }
-#endif /* _PR_HAVE_INET_NTOP */
-
-    return status;
+    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+    return PR_FAILURE;
 }
-#endif /* !_PR_HAVE_GETADDRINFO || _PR_INET6_PROBE || DARWIN */
 
 PR_IMPLEMENT(PRStatus) PR_StringToNetAddr(const char *string, PRNetAddr *addr)
 {
     if (!_pr_initialized) _PR_ImplicitInitialization();
 
+    if (!addr || !string || !*string)
+    {
+        PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+        return PR_FAILURE;
+    }
+
 #if !defined(_PR_HAVE_GETADDRINFO)
     return pr_StringToNetAddrFB(string, addr);
 #else
 #if defined(_PR_INET6_PROBE)
     if (!_pr_ipv6_is_present())
         return pr_StringToNetAddrFB(string, addr);
 #endif
-#if defined(DARWIN)
     /*
-     * On Mac OS X, getaddrinfo with AI_NUMERICHOST is slow.
-     * So we only use it to convert literal IP addresses that
-     * contain IPv6 scope IDs, which pr_StringToNetAddrFB
-     * cannot convert.  (See bug 404399.)
+     * getaddrinfo with AI_NUMERICHOST is much slower than pr_inet_aton on some
+     * platforms, such as Mac OS X (bug 404399), Linux glibc 2.10 (bug 344809),
+     * and most likely others. So we only use it to convert literal IP addresses
+     * that contain IPv6 scope IDs, which pr_inet_aton cannot convert.
      */
     if (!strchr(string, '%'))
         return pr_StringToNetAddrFB(string, addr);
-#endif
+
     return pr_StringToNetAddrGAI(string, addr);
 #endif
 }
 
 #if defined(_PR_HAVE_GETADDRINFO)
 static PRStatus pr_NetAddrToStringGNI(
     const PRNetAddr *addr, char *string, PRUint32 size)
 {