]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
hostip: make CURLOPT_RESOLVE support replacing IPv6 addresses 16358/head
authorDaniel Stenberg <daniel@haxx.se>
Mon, 17 Feb 2025 07:33:52 +0000 (08:33 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 18 Feb 2025 07:55:56 +0000 (08:55 +0100)
This also applies to --resolve of course.

Applied strparse functions on the function.

Fixes #16357
Reported-by: rmg-x on github
Closes #16358
Assisted-by: Jay Satiro
docs/cmdline-opts/resolve.md
docs/libcurl/opts/CURLOPT_RESOLVE.md
lib/hostip.c

index 2b71d9a590154e86d473c26ac3017efc465f35c9..18733df0c2bf4f8801ccf3fc6887bb3d1e87e383 100644 (file)
@@ -12,6 +12,7 @@ See-also:
   - alt-svc
 Example:
   - --resolve example.com:443:127.0.0.1 $URL
+  - --resolve example.com:443:[2001:db8::252f:efd6] $URL
 ---
 
 # `--resolve`
@@ -20,8 +21,8 @@ Provide a custom address for a specific host and port pair. Using this, you
 can make the curl requests(s) use a specified address and prevent the
 otherwise normally resolved address to be used. Consider it a sort of
 /etc/hosts alternative provided on the command line. The port number should be
-the number used for the specific protocol the host is used for. It means
-you need several entries if you want to provide address for the same host but
+the number used for the specific protocol the host is used for. It means you
+need several entries if you want to provide addresses for the same host but
 different ports.
 
 By specifying `*` as host you can tell curl to resolve any host and specific
@@ -37,9 +38,13 @@ parallel transfers with a lot of files. In such cases, if this option is used
 curl tries to resolve the host as it normally would once the timeout has
 expired.
 
+Provide IPv6 addresses within [brackets].
+
 To redirect connects from a specific hostname or any hostname, independently
 of port number, consider the --connect-to option.
 
 Support for resolving with wildcard was added in 7.64.0.
 
 Support for the '+' prefix was added in 7.75.0.
+
+Support for specifying the host component as an IPv6 address was added in 8.13.0.
index 4dc404bbf2c8465e6e5073f74515f18a9727ef1e..952ff824e78f3220e3e4f5e2e621b9a068fd17e4 100644 (file)
@@ -73,6 +73,8 @@ resolves, include a string in the linked list that uses the format
 The entry to remove must be prefixed with a dash, and the hostname and port
 number must exactly match what was added previously.
 
+Provide IPv6 addresses within [brackets].
+
 Using this option multiple times makes the last set list override the previous
 ones. Set it to NULL to disable its use again.
 
@@ -90,6 +92,7 @@ int main(void)
   CURL *curl;
   struct curl_slist *host = NULL;
   host = curl_slist_append(NULL, "example.com:443:127.0.0.1");
+  host = curl_slist_append(host, "example.com:443:[2001:db8::252f:efd6]");
 
   curl = curl_easy_init();
   if(curl) {
@@ -117,6 +120,8 @@ Support for providing multiple IP addresses per entry was added in 7.59.0.
 Support for adding non-permanent entries by using the "+" prefix was added in
 7.75.0.
 
+Support for specifying the host component as an IPv6 address was added in 8.13.0.
+
 # %AVAILABILITY%
 
 # RETURN VALUE
index 4dbedfb0120691b271524ace87fcd36f9400ad4d..17d335564f75febdcd8af69a0d87113d325ab215 100644 (file)
@@ -1124,43 +1124,46 @@ void Curl_hostcache_clean(struct Curl_easy *data,
 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
 {
   struct curl_slist *hostp;
-  const char *host_end;
 
   /* Default is no wildcard found */
   data->state.wildcard_resolve = FALSE;
 
   for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
     char entry_id[MAX_HOSTCACHE_LEN];
-    if(!hostp->data)
+    const char *host = hostp->data;
+    struct Curl_str source;
+    if(!host)
       continue;
-    if(hostp->data[0] == '-') {
+    if(*host == '-') {
       curl_off_t num = 0;
       size_t entry_len;
-      size_t hlen = 0;
-      host_end = strchr(&hostp->data[1], ':');
-
-      if(host_end) {
-        hlen = host_end - &hostp->data[1];
-        host_end++;
-        if(Curl_str_number(&host_end, &num, 0xffff))
-          host_end = NULL;
+      host++;
+      if(!Curl_str_single(&host, '[')) {
+        if(Curl_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
+           Curl_str_single(&host, ']') ||
+           Curl_str_single(&host, ':'))
+          continue;
       }
-      if(!host_end) {
-        infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'",
-              hostp->data);
-        continue;
+      else {
+        if(Curl_str_until(&host, &source, 4096, ':') ||
+           Curl_str_single(&host, ':')) {
+          continue;
+        }
       }
-      /* Create an entry id, based upon the hostname and port */
-      entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num,
-                                      entry_id, sizeof(entry_id));
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
 
-      /* delete entry, ignore if it did not exist */
-      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+      if(!Curl_str_number(&host, &num, 0xffff)) {
+        /* Create an entry id, based upon the hostname and port */
+        entry_len = create_hostcache_id(source.str, source.len, (int)num,
+                                        entry_id, sizeof(entry_id));
+        if(data->share)
+          Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
 
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+        /* delete entry, ignore if it did not exist */
+        Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+
+        if(data->share)
+          Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+      }
     }
     else {
       struct Curl_dns_entry *dns;
@@ -1170,71 +1173,66 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
       const char *addresses = NULL;
 #endif
-      const char *addr_begin;
-      const char *addr_end;
-      const char *port_ptr;
       curl_off_t port = 0;
-      const char *end_ptr;
       bool permanent = TRUE;
       bool error = TRUE;
-      char *host_begin = hostp->data;
-      size_t hlen = 0;
 
-      if(host_begin[0] == '+') {
-        host_begin++;
+      if(*host == '+') {
+        host++;
         permanent = FALSE;
       }
-      host_end = strchr(host_begin, ':');
-      if(!host_end)
-        goto err;
-      hlen = host_end - host_begin;
-
-      port_ptr = host_end + 1;
-      if(Curl_str_number(&port_ptr, &port, 0xffff) ||
-         (*port_ptr != ':'))
+      if(!Curl_str_single(&host, '[')) {
+        if(Curl_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
+           Curl_str_single(&host, ']'))
+          continue;
+      }
+      else {
+        if(Curl_str_until(&host, &source, 4096, ':'))
+          continue;
+      }
+      if(Curl_str_single(&host, ':') ||
+         Curl_str_number(&host, &port, 0xffff) ||
+         Curl_str_single(&host, ':'))
         goto err;
-      end_ptr = port_ptr;
 
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-      addresses = end_ptr + 1;
+      addresses = host;
 #endif
 
-      while(*end_ptr) {
-        size_t alen;
+      /* start the address section */
+      while(*host) {
+        struct Curl_str target;
         struct Curl_addrinfo *ai;
 
-        addr_begin = end_ptr + 1;
-        addr_end = strchr(addr_begin, ',');
-        if(!addr_end)
-          addr_end = addr_begin + strlen(addr_begin);
-        end_ptr = addr_end;
-
-        /* allow IP(v6) address within [brackets] */
-        if(*addr_begin == '[') {
-          if(addr_end == addr_begin || *(addr_end - 1) != ']')
+        if(!Curl_str_single(&host, '[')) {
+          if(Curl_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
+             Curl_str_single(&host, ']'))
             goto err;
-          ++addr_begin;
-          --addr_end;
         }
-
-        alen = addr_end - addr_begin;
-        if(!alen)
-          continue;
-
-        if(alen >= sizeof(address))
-          goto err;
-
-        memcpy(address, addr_begin, alen);
-        address[alen] = '\0';
-
+        else {
+          if(Curl_str_until(&host, &target, 4096, ',')) {
+            if(Curl_str_single(&host, ','))
+              goto err;
+            /* survive nothing but just a comma */
+            continue;
+          }
+        }
 #ifndef USE_IPV6
-        if(strchr(address, ':')) {
+        if(memchr(target.str, ':', target.len)) {
           infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
                 address);
+          if(Curl_str_single(&host, ','))
+            goto err;
           continue;
         }
 #endif
 
+        if(target.len >= sizeof(address))
+          goto err;
+
+        memcpy(address, target.str, target.len);
+        address[target.len] = '\0';
+
         ai = Curl_str2addr(address, (int)port);
         if(!ai) {
           infof(data, "Resolve address '%s' found illegal", address);
@@ -1248,6 +1246,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
         else {
           head = tail = ai;
         }
+        if(Curl_str_single(&host, ','))
+          break;
       }
 
       if(!head)
@@ -1263,7 +1263,7 @@ err:
       }
 
       /* Create an entry id, based upon the hostname and port */
-      entry_len = create_hostcache_id(host_begin, hlen, (int)port,
+      entry_len = create_hostcache_id(source.str, source.len, (int)port,
                                       entry_id, sizeof(entry_id));
 
       if(data->share)
@@ -1274,7 +1274,7 @@ err:
 
       if(dns) {
         infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T
-              " - old addresses discarded", (int)hlen, host_begin, port);
+              " - old addresses discarded", (int)source.len, source.str, port);
         /* delete old entry, there are two reasons for this
          1. old entry may have different addresses.
          2. even if entry with correct addresses is already in the cache,
@@ -1290,7 +1290,7 @@ err:
       }
 
       /* put this new host in the cache */
-      dns = Curl_cache_addr(data, head, host_begin, hlen, (int)port,
+      dns = Curl_cache_addr(data, head, source.str, source.len, (int)port,
                             permanent);
       if(dns) {
         /* release the returned reference; the cache itself will keep the
@@ -1307,12 +1307,12 @@ err:
       }
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s",
-            (int)hlen, host_begin, port, addresses,
+            (int)source.len, source.str, port, addresses,
             permanent ? "" : " (non-permanent)");
 #endif
 
       /* Wildcard hostname */
-      if((hlen == 1) && (host_begin[0] == '*')) {
+      if((source.len == 1) && (source.str[0] == '*')) {
         infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard",
               port);
         data->state.wildcard_resolve = TRUE;