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
This commit is contained in:
Lucas Clemente Vella 2021-04-24 20:04:53 +01:00 committed by Daniel Stenberg
parent ac54b10933
commit 84d2839740
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
11 changed files with 69 additions and 100 deletions

View 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;
#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);
}
/* 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);
}

View 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));

View 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",

View file

@ -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],

View 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;

View file

@ -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);