mirror of
https://github.com/moparisthebest/curl
synced 2024-12-22 08:08:50 -05:00
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:
parent
ac54b10933
commit
84d2839740
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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",
|
||||
|
18
lib/doh.c
18
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],
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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' );"
|
||||
|
Loading…
Reference in New Issue
Block a user