]> sjero.net Git - wget/commitdiff
[svn] Add proper detection of numeric IPv6 addresses.
authorhniksic <devnull@localhost>
Fri, 5 Sep 2003 20:36:17 +0000 (13:36 -0700)
committerhniksic <devnull@localhost>
Fri, 5 Sep 2003 20:36:17 +0000 (13:36 -0700)
By Mauro Tortonesi.

src/ChangeLog
src/url.c

index c5faaf22fc133b454d38727d8a5e5fc9457fa053..06ee80e4a0d81e3ed930536aecaf5892d5057011 100644 (file)
@@ -1,3 +1,15 @@
+2003-09-05  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * url.c (is_valid_ipv6_address): Modified to not require
+       zero-terminated strings.
+       (is_valid_ipv4_address): Ditto.
+
+2003-09-05  Mauro Tortonesi <mauro@deepspace6.net>
+
+       src/url.c: added RFC 2732 compliance for URL parsing. The
+       functions is_*_address valid are a modified version of
+       glibc 2.3.2 inet_pton's code.
+
 2003-09-03  Ahmon Dancy  <dancy@dancysoft.com>
 
        * main.c init.c options.h: Added --retry-connrefused option so
index 7636ddcd3a85b6a2aadc86a2eaf7cdf5b248233a..b5cb388b41b3325450b4333c2f1b9f49ec29d2bf 100644 (file)
--- a/src/url.c
+++ b/src/url.c
@@ -58,6 +58,11 @@ extern int errno;
 /* Is X ".."?  */
 #define DDOTP(x) ((*(x) == '.') && (*(x + 1) == '.') && (!*(x + 2)))
 
+static const int NS_INADDRSZ  = 4;
+static const int NS_IN6ADDRSZ = 16;
+static const int NS_INT16SZ = 2;
+
+
 struct scheme_data
 {
   char *leading_string;
@@ -645,7 +650,7 @@ static char *parse_errors[] = {
 #define PE_UNTERMINATED_IPV6_ADDRESS   5
   "Unterminated IPv6 numeric address",
 #define PE_INVALID_IPV6_ADDRESS                6
-  "Invalid char in IPv6 numeric address"
+  "Invalid IPv6 numeric address"
 };
 
 #define SETERR(p, v) do {                      \
@@ -653,6 +658,137 @@ static char *parse_errors[] = {
     *(p) = (v);                                        \
 } while (0)
 
+/* The following two functions were adapted from glibc. */
+
+static int
+is_valid_ipv4_address (const char *str, const char *end)
+{
+  int saw_digit, octets;
+  int val;
+
+  saw_digit = 0;
+  octets = 0;
+  val = 0;
+
+  while (str < end) {
+    int ch = *str++;
+
+    if (ch >= '0' && ch <= '9') {
+      val = val * 10 + (ch - '0');
+
+      if (val > 255)
+        return 0;
+      if (saw_digit == 0) {
+        if (++octets > 4)
+          return 0;
+        saw_digit = 1;
+      }
+    } else if (ch == '.' && saw_digit == 1) {
+      if (octets == 4)
+        return 0;
+      val = 0;
+      saw_digit = 0;
+    } else
+      return 0;
+  }
+  if (octets < 4)
+    return 0;
+  
+  return 1;
+}
+
+static int
+is_valid_ipv6_address (const char *str, const char *end)
+{
+  static const char xdigits[] = "0123456789abcdef";
+  const char *curtok;
+  int tp;
+  const char *colonp;
+  int saw_xdigit;
+  unsigned int val;
+
+  tp = 0;
+  colonp = NULL;
+
+  if (str == end)
+    return 0;
+  
+  /* Leading :: requires some special handling. */
+  if (*str == ':')
+    {
+      ++str;
+      if (str == end || *str != ':')
+       return 0;
+    }
+
+  curtok = str;
+  saw_xdigit = 0;
+  val = 0;
+
+  while (str < end) {
+    int ch = *str++;
+    const char *pch;
+
+    /* if ch is a number, add it to val. */
+    pch = strchr(xdigits, ch);
+    if (pch != NULL) {
+      val <<= 4;
+      val |= (pch - xdigits);
+      if (val > 0xffff)
+       return 0;
+      saw_xdigit = 1;
+      continue;
+    }
+
+    /* if ch is a colon ... */
+    if (ch == ':') {
+      curtok = str;
+      if (saw_xdigit == 0) {
+       if (colonp != NULL)
+         return 0;
+       colonp = str + tp;
+       continue;
+      } else if (str == end) {
+       return 0;
+      }
+      if (tp > NS_IN6ADDRSZ - NS_INT16SZ)
+       return 0;
+      tp += NS_INT16SZ;
+      saw_xdigit = 0;
+      val = 0;
+      continue;
+    }
+
+    /* if ch is a dot ... */
+    if (ch == '.' && (tp <= NS_IN6ADDRSZ - NS_INADDRSZ) &&
+       is_valid_ipv4_address(curtok, end) == 1) {
+      tp += NS_INADDRSZ;
+      saw_xdigit = 0;
+      break;
+    }
+    
+    return 0;
+  }
+
+  if (saw_xdigit == 1) {
+    if (tp > NS_IN6ADDRSZ - NS_INT16SZ) 
+      return 0;
+    tp += NS_INT16SZ;
+  }
+
+  if (colonp != NULL) {
+    if (tp == NS_IN6ADDRSZ) 
+      return 0;
+    tp = NS_IN6ADDRSZ;
+  }
+
+  if (tp != NS_IN6ADDRSZ)
+    return 0;
+
+  return 1;
+}
+
+
 /* Parse a URL.
 
    Return a new struct url if successful, NULL on error.  In case of
@@ -710,36 +846,29 @@ url_parse (const char *url, int *error)
 
   if (*p == '[')
     {
-      /* Support http://[::1]/ used by IPv6. */
-      int invalid = 0;
-      ++p;
-      while (1)
+      /* Handle IPv6 address inside square brackets.  Ideally we'd
+        just look for the terminating ']', but rfc2732 mandates
+        rejecting invalid IPv6 addresses.  */
+
+      /* The address begins after '['. */
+      host_b = p + 1;
+      host_e = strchr (host_b, ']');
+
+      if (!host_e)
        {
-         char c = *p++;
-         switch (c)
-           {
-           case ']':
-             goto out;
-           case '\0':
-             SETERR (error, PE_UNTERMINATED_IPV6_ADDRESS);
-             return NULL;
-           case ':': case '.':
-             break;
-           default:
-             if (ISXDIGIT (c))
-               break;
-             invalid = 1;
-           }
+         SETERR (error, PE_UNTERMINATED_IPV6_ADDRESS);
+         return NULL;
        }
-    out:
-      if (invalid)
+
+      /* Check if the IPv6 address is valid. */
+      if (!is_valid_ipv6_address(host_b, host_e))
        {
          SETERR (error, PE_INVALID_IPV6_ADDRESS);
          return NULL;
        }
-      /* Don't include brackets in [host_b, host_p). */
-      ++host_b;
-      host_e = p - 1;
+
+      /* Continue parsing after the closing ']'. */
+      p = host_e + 1;
     }
   else
     {
@@ -782,6 +911,7 @@ url_parse (const char *url, int *error)
              SETERR (error, PE_BAD_PORT_NUMBER);
              return NULL;
            }
+         
          port = 10 * port + (*pp - '0');
        }
     }