hostip: make CURLOPT_RESOLVE support replacing IPv6 addresses

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
This commit is contained in:
Daniel Stenberg 2025-02-17 08:33:52 +01:00
parent 61f85bf967
commit 2f4dc6525c
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
3 changed files with 84 additions and 74 deletions

View 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)
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;
hlen = host_end - host_begin;
port_ptr = host_end + 1;
if(Curl_str_number(&port_ptr, &port, 0xffff) ||
(*port_ptr != ':'))
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;