]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
urlapi: consume trailing dots after IPv4 numerical addresses
authorDaniel Stenberg <daniel@haxx.se>
Fri, 15 May 2026 15:04:26 +0000 (17:04 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 15 May 2026 21:38:50 +0000 (23:38 +0200)
If the hostname is specified as an IPv4 numerical address and it is
followed by a single dot, acccept that as a valid IPv4 and remove the
dot when normalizing.

This prevents otherwise legitimate IPv4 hostnames to have trailing dots.
Seems to match what browsers do.

Extended test 1560 to verify.

Closes #21635

lib/urlapi.c
tests/data/test1560
tests/libtest/lib1560.c

index a3111f9db6e8a79dd562b16137118f5d0609fa12..a2d3c7e35ade54d6478057cdcc5853a4f41fb384 100644 (file)
@@ -496,6 +496,9 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
  * Output the "normalized" version of that input string in plain quad decimal
  * integers.
  *
+ * A single dot following the numerical address is accepted and "swallowed" as
+ * if it was never there.
+ *
  * Returns the host type.
  *
  * @unittest 1675
@@ -527,17 +530,26 @@ UNITTEST int ipv4_normalize(struct dynbuf *host)
     else
       rc = curlx_str_number(&c, &l, UINT_MAX);
 
-    if(rc)
-      return HOST_NAME;
-
-    parts[n] = (unsigned int)l;
+    if(rc) {
+      if(!n || (rc != STRE_NO_NUM) || *c)
+        return HOST_NAME;
+      n--;
+    }
+    else
+      parts[n] = (unsigned int)l;
 
     switch(*c) {
     case '.':
-      if(n == 3)
-        return HOST_NAME;
-      n++;
-      c++;
+      if(n == 3) {
+        if(c[1])
+          /* something follows this dot */
+          return HOST_NAME;
+        done = TRUE;
+      }
+      else {
+        n++;
+        c++;
+      }
       break;
 
     case '\0':
index e0d792800b2e34dd76963b698e3b79fb58934592..e27229739f8e56636d281eeb19d54a35155171a8 100644 (file)
@@ -37,7 +37,7 @@ lib%TESTNUMBER
 success
 </stdout>
 <limits>
-Allocations: 3100
+Allocations: 3200
 </limits>
 </verify>
 </testcase>
index e833c304e3719c05f669a7c9b25346aa20ed3eaf..533a44e98376c30cd84e659c5f002acd8aa54094 100644 (file)
@@ -625,6 +625,23 @@ static const struct testcase get_parts_list[] = {
 };
 
 static const struct urltestcase get_url_list[] = {
+  {"https://127.1.", "https://127.0.0.1/", 0, 0, CURLUE_OK},
+  {"https://127.1.:443", "https://127.0.0.1:443/", 0, 0, CURLUE_OK},
+  {"https://127.1.?moo", "https://127.0.0.1/?moo", 0, 0, CURLUE_OK},
+  {"https://127.1.#moo", "https://127.0.0.1/#moo", 0, 0, CURLUE_OK},
+  {"https://127.1.a", "https://127.1.a/", 0, 0, CURLUE_OK},
+  {"https://127.1..", "", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://127.1..:443", "", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://127.1..?moo", "", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://127.1..#moo", "", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://127.1.1.", "https://127.1.0.1/", 0, 0, CURLUE_OK},
+  {"https://127.1.1./foo", "https://127.1.0.1/foo", 0, 0, CURLUE_OK},
+  {"https://127.1.1.1.", "https://127.1.1.1/", 0, 0, CURLUE_OK},
+  {"https://127.1", "https://127.0.0.1/", 0, 0, CURLUE_OK},
+  {"https://127.0.0.1.", "https://127.0.0.1/", 0, 0, CURLUE_OK},
+  {"https://127.0.0.0xff.", "https://127.0.0.255/", 0, 0, CURLUE_OK},
+  {"https://127.0.0.1..", "", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://127.0.0.256..", "", 0, 0, CURLUE_BAD_HOSTNAME},
   {"http://hej./", "http://hej./", 0, 0, CURLUE_OK},
   {"http://hej../", "", 0, 0, CURLUE_BAD_HOSTNAME},
   {"http://hej.../", "", 0, 0, CURLUE_BAD_HOSTNAME},
@@ -743,9 +760,9 @@ static const struct urltestcase get_url_list[] = {
   {"https://16843009", "https://1.1.1.1/", 0, 0, CURLUE_OK},
   {"https://0177.1", "https://127.0.0.1/", 0, 0, CURLUE_OK},
   {"https://0111.02.0x3", "https://73.2.0.3/", 0, 0, CURLUE_OK},
-  {"https://0111.02.0x3.", "https://0111.02.0x3./", 0, 0, CURLUE_OK},
+  {"https://0111.02.0x3.", "https://73.2.0.3/", 0, 0, CURLUE_OK},
   {"https://0111.02.030", "https://73.2.0.24/", 0, 0, CURLUE_OK},
-  {"https://0111.02.030.", "https://0111.02.030./", 0, 0, CURLUE_OK},
+  {"https://0111.02.030.", "https://73.2.0.24/", 0, 0, CURLUE_OK},
   {"https://0xff.0xff.0377.255", "https://255.255.255.255/", 0, 0, CURLUE_OK},
   {"https://1.0xffffff", "https://1.255.255.255/", 0, 0, CURLUE_OK},
   /* IPv4 numerical overflows or syntax errors will not normalize */