]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
CURLOPT_IPRESOLVE: preventing wrong IP version from being used
authorLucas Clemente Vella <lvella@gmail.com>
Sat, 24 Apr 2021 19:04:53 +0000 (20:04 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 20 May 2021 14:58:31 +0000 (16:58 +0200)
In some situations, it was possible that a transfer was setup to
use an specific IP version, but due do DNS caching or connection
reuse, it ended up using a different IP version from requested.

This commit changes the effect of CURLOPT_IPRESOLVE from simply
restricting address resolution to preventing the wrong connection
type being used, when choosing a connection from the pool, and
to restricting what addresses could be used when establishing
a new connection.

It is important that all addresses versions are resolved, even if
not used in that transfer in particular, because the result is
cached, and could be useful for a different transfer with a
different CURLOPT_IPRESOLVE setting.

Closes #6853

docs/libcurl/curl_easy_setopt.3
docs/libcurl/opts/CURLOPT_IPRESOLVE.3
docs/libcurl/opts/CURLOPT_RESOLVE.3
include/curl/curl.h
lib/asyn-ares.c
lib/asyn-thread.c
lib/connect.c
lib/doh.c
lib/hostip6.c
lib/url.c
tests/data/test1082

index 0c4f9b7ca6b5c3ca97edf7c3e3d66c2653a2f3e6..ecba0d5d46dc3850597831d6d17e79ab46d89178 100644 (file)
@@ -494,7 +494,7 @@ Timeout for the connection phase. See \fICURLOPT_CONNECTTIMEOUT(3)\fP
 .IP CURLOPT_CONNECTTIMEOUT_MS
 Millisecond timeout for the connection phase. See \fICURLOPT_CONNECTTIMEOUT_MS(3)\fP
 .IP CURLOPT_IPRESOLVE
-IP version to resolve to. See \fICURLOPT_IPRESOLVE(3)\fP
+IP version to use. See \fICURLOPT_IPRESOLVE(3)\fP
 .IP CURLOPT_CONNECT_ONLY
 Only connect, nothing else. See \fICURLOPT_CONNECT_ONLY(3)\fP
 .IP CURLOPT_USE_SSL
index 4bdd8b2fee0450abf7f137a7a3aebaec7774c6c2..6d534da9e669f0f7f8d89e29dcb821376ded4bc1 100644 (file)
@@ -29,14 +29,15 @@ CURLOPT_IPRESOLVE \- specify which IP protocol version to use
 CURLcode curl_easy_setopt(CURL *handle, CURLOPT_IPRESOLVE, long resolve);
 .SH DESCRIPTION
 Allows an application to select what kind of IP addresses to use when
-resolving host names. This is only interesting when using host names that
-resolve addresses using more than one version of IP. The allowed values are:
+establishing a connection or choosing one from the connection pool. This is
+interesting when using host names that resolve addresses using more than
+one version of IP. The allowed values are:
 .IP CURL_IPRESOLVE_WHATEVER
-Default, resolves addresses to all IP versions that your system allows.
+Default, can use addresses of all IP versions that your system allows.
 .IP CURL_IPRESOLVE_V4
-Resolve to IPv4 addresses.
+Uses only IPv4 addresses.
 .IP CURL_IPRESOLVE_V6
-Resolve to IPv6 addresses.
+Uses only IPv6 addresses.
 .SH DEFAULT
 CURL_IPRESOLVE_WHATEVER
 .SH PROTOCOLS
@@ -47,7 +48,7 @@ CURL *curl = curl_easy_init();
 if(curl) {
   curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin");
 
-  /* resolve host name using IPv6-names only */
+  /* of all addresses example.com resolves to, only IPv6 ones are used */
   curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
 
   ret = curl_easy_perform(curl);
index 20ee69b0c3ab4a2dabacdd0b804f585ca4ffcfaf..57a5401e4061658def4a26621297d9a48d3a8c0b 100644 (file)
@@ -57,8 +57,8 @@ this entry will be removed and a new entry will be created. This is because
 the old entry may have have different addresses or a different time-out
 setting.
 
-The provided ADDRESS set by this option will be used even if
-\fICURLOPT_IPRESOLVE(3)\fP is set to make libcurl use another IP version.
+An ADDRESS provided by this option will only be use if not restricted by
+the setting of \fICURLOPT_IPRESOLVE(3)\fP to a different IP version.
 
 Remove names from the DNS cache again, to stop providing these fake resolves,
 by including a string in the linked list that uses the format
index e8f9db52fd800ed50ba20b7aa4d37f0ca1029e23..97de8c88ae00d5fd9b985842ff3f5240ae14de40 100644 (file)
@@ -1466,8 +1466,8 @@ typedef enum {
 #define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT
 
   /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to
-     tell libcurl to resolve names to those IP versions only. This only has
-     affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */
+     tell libcurl to use those IP versions only. This only has effect on
+     systems with support for more than one, i.e IPv4 _and_ IPv6. */
   CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_VALUES, 113),
 
   /* Set this option to limit the size of a file that will be downloaded from
@@ -2135,10 +2135,10 @@ typedef enum {
   /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host
      name resolves addresses using more than one IP protocol version, this
      option might be handy to force libcurl to use a specific IP version. */
-#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP
+#define CURL_IPRESOLVE_WHATEVER 0 /* default, uses addresses to all IP
                                      versions that your system allows */
-#define CURL_IPRESOLVE_V4       1 /* resolve to IPv4 addresses */
-#define CURL_IPRESOLVE_V6       2 /* resolve to IPv6 addresses */
+#define CURL_IPRESOLVE_V4       1 /* uses only IPv4 addresses/connections */
+#define CURL_IPRESOLVE_V6       2 /* uses only IPv6 addresses/connections */
 
   /* three convenient "aliases" that follow the name scheme better */
 #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
index b5ec12a7151bf1c0807516084a57e8c61fa2db37..7827847350cd25c3ada5b35b90dcd021a6e90b90 100644 (file)
@@ -620,28 +620,9 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
                                                 int *waitp)
 {
   char *bufp;
-  int family = PF_INET;
 
   *waitp = 0; /* default to synchronous response */
 
-#ifdef ENABLE_IPV6
-  switch(data->set.ipver) {
-  default:
-#if ARES_VERSION >= 0x010601
-    family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
-                           c-ares versions this just falls through and defaults
-                           to PF_INET */
-    break;
-#endif
-  case CURL_IPRESOLVE_V4:
-    family = PF_INET;
-    break;
-  case CURL_IPRESOLVE_V6:
-    family = PF_INET6;
-    break;
-  }
-#endif /* ENABLE_IPV6 */
-
   bufp = strdup(hostname);
   if(bufp) {
     struct thread_data *res = NULL;
@@ -661,33 +642,27 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
 
     /* initial status - failed */
     res->last_status = ARES_ENOTFOUND;
-#ifdef ENABLE_IPV6
-    if(family == PF_UNSPEC) {
-      if(Curl_ipv6works(data)) {
-        res->num_pending = 2;
-
-        /* areschannel is already setup in the Curl_open() function */
-        ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
-                            PF_INET, query_completed_cb, data);
-        ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
-                            PF_INET6, query_completed_cb, data);
-      }
-      else {
-        res->num_pending = 1;
 
-        /* areschannel is already setup in the Curl_open() function */
-        ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
-                            PF_INET, query_completed_cb, data);
-      }
+#if ARES_VERSION >= 0x010601
+    /* IPv6 supported by c-ares since 1.6.1 */
+    if(Curl_ipv6works(data)) {
+      /* The stack seems to be IPv6-enabled */
+      res->num_pending = 2;
+
+      /* areschannel is already setup in the Curl_open() function */
+      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
+                          PF_INET, query_completed_cb, data);
+      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
+                          PF_INET6, query_completed_cb, data);
     }
     else
-#endif /* ENABLE_IPV6 */
+#endif /* ARES_VERSION >= 0x010601 */
     {
       res->num_pending = 1;
 
       /* areschannel is already setup in the Curl_open() function */
       ares_gethostbyname((ares_channel)data->state.async.resolver,
-                         hostname, family,
+                         hostname, PF_INET,
                          query_completed_cb, data);
     }
 
index cd93492f26ea478dfa209abd1eb1bf975317f872..36f68cb493fd6af6e253a8d5b4ad010a99825b78 100644 (file)
@@ -701,24 +701,9 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
   *waitp = 0; /* default to synchronous response */
 
 #ifdef CURLRES_IPV6
-  /*
-   * Check if a limited name resolve has been requested.
-   */
-  switch(data->set.ipver) {
-  case CURL_IPRESOLVE_V4:
-    pf = PF_INET;
-    break;
-  case CURL_IPRESOLVE_V6:
-    pf = PF_INET6;
-    break;
-  default:
+  if(Curl_ipv6works(data))
+    /* The stack seems to be IPv6-enabled */
     pf = PF_UNSPEC;
-    break;
-  }
-
-  if((pf != PF_INET) && !Curl_ipv6works(data))
-    /* The stack seems to be a non-IPv6 one */
-    pf = PF_INET;
 #endif /* CURLRES_IPV6 */
 
   memset(&hints, 0, sizeof(hints));
index 824ced31e916e0373a22c5b60e368e84ce56f42e..d9317f3783c8f2298d1985f66baf3fd00a6e2319 100644 (file)
@@ -1367,14 +1367,31 @@ CURLcode Curl_connecthost(struct Curl_easy *data,
   conn->timeoutms_per_addr[1] =
     conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
 
-  conn->tempfamily[0] = conn->tempaddr[0]?
-    conn->tempaddr[0]->ai_family:0;
+  if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
+    /* any IP version is allowed */
+    conn->tempfamily[0] = conn->tempaddr[0]?
+      conn->tempaddr[0]->ai_family:0;
 #ifdef ENABLE_IPV6
-  conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
-    AF_INET : AF_INET6;
+    conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
+      AF_INET : AF_INET6;
 #else
-  conn->tempfamily[1] = AF_UNSPEC;
+    conn->tempfamily[1] = AF_UNSPEC;
 #endif
+  }
+  else {
+    /* only one IP version is allowed */
+    conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ?
+      AF_INET :
+#ifdef ENABLE_IPV6
+      AF_INET6;
+#else
+      AF_UNSPEC;
+#endif
+    conn->tempfamily[1] = AF_UNSPEC;
+
+    ainext(conn, 0, FALSE); /* find first address of the right type */
+  }
+
   ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
 
   DEBUGF(infof(data, "family0 == %s, family1 == %s\n",
index 18d7221c75cdcd1eaffa7d34827fa92e6b46058a..36f8cd58dc57d949d10c4aa1c69dbe5de333d496 100644 (file)
--- a/lib/doh.c
+++ b/lib/doh.c
@@ -420,17 +420,15 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
   if(!dohp->headers)
     goto error;
 
-  if(conn->ip_version != CURL_IPRESOLVE_V6) {
-    /* create IPv4 DOH request */
-    result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
-                      DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
-                      data->multi, dohp->headers);
-    if(result)
-      goto error;
-    dohp->pending++;
-  }
+  /* create IPv4 DOH request */
+  result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
+                    DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
+                    data->multi, dohp->headers);
+  if(result)
+    goto error;
+  dohp->pending++;
 
-  if(conn->ip_version != CURL_IPRESOLVE_V4) {
+  if(Curl_ipv6works(data)) {
     /* create IPv6 DOH request */
     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
index 53b3c672239279a15939c2bd4d872880a661b8c8..9791d86468d9061bad80069d65a1e23264401541 100644 (file)
@@ -140,26 +140,13 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
 #ifndef USE_RESOLVE_ON_IPS
   char addrbuf[128];
 #endif
-  int pf;
+  int pf = PF_INET;
 
   *waitp = 0; /* synchronous response only */
 
-  /* Check if a limited name resolve has been requested */
-  switch(data->set.ipver) {
-  case CURL_IPRESOLVE_V4:
-    pf = PF_INET;
-    break;
-  case CURL_IPRESOLVE_V6:
-    pf = PF_INET6;
-    break;
-  default:
+  if(Curl_ipv6works(data))
+    /* The stack seems to be IPv6-enabled */
     pf = PF_UNSPEC;
-    break;
-  }
-
-  if((pf != PF_INET) && !Curl_ipv6works(data))
-    /* The stack seems to be a non-IPv6 one */
-    pf = PF_INET;
 
   memset(&hints, 0, sizeof(hints));
   hints.ai_family = pf;
index 5f64626176796e77120737c44e91f8d987de859b..1ee38af0d567ff84d86c8a75132aa865d5a74a73 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -1172,6 +1172,12 @@ ConnectionExists(struct Curl_easy *data,
         continue;
       }
 
+      if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
+          && data->set.ipver != check->ip_version) {
+        /* skip because the connection is not via the requested IP version */
+        continue;
+      }
+
       if(bundle->multiuse == BUNDLE_MULTIPLEX)
         multiplexed = CONN_INUSE(check);
 
index 3ee436bf0eb1911c3c3fadf8b3a7a2178efed27a..d4dd0e9b5f2aa6b13bef73118a0accb2e6d38a0e 100644 (file)
@@ -32,7 +32,7 @@ http
 HTTP GET with localhost --interface
  </name>
  <command>
-http://%HOSTIP:%HTTPPORT/%TESTNUMBER -4 --interface localhost
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER -4 --interface 127.0.0.1
 </command>
 <precheck>
 perl -e "print 'Test requires default test client host address' if ( '%CLIENTIP' ne '127.0.0.1' );"