summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas Clemente Vella <lvella@gmail.com>2021-04-24 20:04:53 +0100
committerDaniel Stenberg <daniel@haxx.se>2021-05-20 16:58:31 +0200
commit84d2839740ca78041ac7419d9aaeac55c1e1c729 (patch)
treeaee6202a7815b0cce0e1a2b9179c3b4421e7fb9a
parentac54b10933749d4c17d9568532ff2f1ed64c119a (diff)
downloadcurl-84d2839740ca78041ac7419d9aaeac55c1e1c729.tar.gz
CURLOPT_IPRESOLVE: preventing wrong IP version from being used
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
-rw-r--r--docs/libcurl/curl_easy_setopt.32
-rw-r--r--docs/libcurl/opts/CURLOPT_IPRESOLVE.313
-rw-r--r--docs/libcurl/opts/CURLOPT_RESOLVE.34
-rw-r--r--include/curl/curl.h10
-rw-r--r--lib/asyn-ares.c51
-rw-r--r--lib/asyn-thread.c19
-rw-r--r--lib/connect.c27
-rw-r--r--lib/doh.c18
-rw-r--r--lib/hostip6.c19
-rw-r--r--lib/url.c6
-rw-r--r--tests/data/test10822
11 files changed, 70 insertions, 101 deletions
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index 0c4f9b7ca..ecba0d5d4 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -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
diff --git a/docs/libcurl/opts/CURLOPT_IPRESOLVE.3 b/docs/libcurl/opts/CURLOPT_IPRESOLVE.3
index 4bdd8b2fe..6d534da9e 100644
--- a/docs/libcurl/opts/CURLOPT_IPRESOLVE.3
+++ b/docs/libcurl/opts/CURLOPT_IPRESOLVE.3
@@ -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);
diff --git a/docs/libcurl/opts/CURLOPT_RESOLVE.3 b/docs/libcurl/opts/CURLOPT_RESOLVE.3
index 20ee69b0c..57a5401e4 100644
--- a/docs/libcurl/opts/CURLOPT_RESOLVE.3
+++ b/docs/libcurl/opts/CURLOPT_RESOLVE.3
@@ -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
diff --git a/include/curl/curl.h b/include/curl/curl.h
index e8f9db52f..97de8c88a 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -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
diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c
index b5ec12a71..782784735 100644
--- a/lib/asyn-ares.c
+++ b/lib/asyn-ares.c
@@ -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);
}
diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c
index cd93492f2..36f68cb49 100644
--- a/lib/asyn-thread.c
+++ b/lib/asyn-thread.c
@@ -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));
diff --git a/lib/connect.c b/lib/connect.c
index 824ced31e..d9317f378 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -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",
diff --git a/lib/doh.c b/lib/doh.c
index 18d7221c7..36f8cd58d 100644
--- 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],
diff --git a/lib/hostip6.c b/lib/hostip6.c
index 53b3c6722..9791d8646 100644
--- a/lib/hostip6.c
+++ b/lib/hostip6.c
@@ -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;
diff --git a/lib/url.c b/lib/url.c
index 5f6462617..1ee38af0d 100644
--- 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);
diff --git a/tests/data/test1082 b/tests/data/test1082
index 3ee436bf0..d4dd0e9b5 100644
--- a/tests/data/test1082
+++ b/tests/data/test1082
@@ -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' );"