hostip: make more functions return CURLcode

- Curl_async_getaddrinfo() always returned NULL so it was pointless.
  Return proper curlcode instead to distinguish between errors. Same for
  Curl_doh().
- simplify the IP address handling
- make Curl_str2addr() function return CURLcode

Closes #19669
This commit is contained in:
Daniel Stenberg 2025-11-24 14:00:09 +01:00
parent a075d1c0d8
commit ce06fe7771
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
9 changed files with 117 additions and 145 deletions

View File

@ -718,25 +718,18 @@ static void async_ares_rr_done(void *user_data, ares_status_t status,
/*
* Curl_async_getaddrinfo() - when using ares
*
* Returns name information about the given hostname and port number. If
* successful, the 'hostent' is returned and the fourth argument will point to
* memory we need to free after use. That memory *MUST* be freed with
* Curl_freeaddrinfo(), nothing else.
* Starts a name resolve for the given hostname and port number.
*/
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp)
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
int port, int ip_version)
{
struct async_ares_ctx *ares = &data->state.async.ares;
#ifdef USE_HTTPSRR
char *rrname = NULL;
#endif
*waitp = 0; /* default to synchronous response */
if(async_ares_init_lazy(data))
return NULL;
return CURLE_FAILED_INIT;
data->state.async.done = FALSE; /* not done */
data->state.async.dns = NULL; /* clear */
@ -744,12 +737,12 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
data->state.async.ip_version = ip_version;
data->state.async.hostname = strdup(hostname);
if(!data->state.async.hostname)
return NULL;
return CURLE_OUT_OF_MEMORY;
#ifdef USE_HTTPSRR
if(port != 443) {
rrname = curl_maprintf("_%d_.https.%s", port, hostname);
if(!rrname)
return NULL;
return CURLE_OUT_OF_MEMORY;
}
#endif
@ -836,9 +829,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
async_ares_rr_done, data, NULL);
}
#endif
*waitp = 1; /* expect asynchronous response */
return NULL; /* no struct yet */
return CURLE_OK;
}
/* Set what DNS server are is to use. This is called in 2 situations:

View File

@ -755,15 +755,11 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
/*
* Curl_async_getaddrinfo() - for getaddrinfo
*/
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp)
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
int port, int ip_version)
{
struct addrinfo hints;
int pf = PF_INET;
*waitp = 0; /* default to synchronous response */
CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
#ifdef CURLRES_IPV6
@ -785,14 +781,11 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
SOCK_STREAM : SOCK_DGRAM;
/* fire up a new resolver thread! */
if(async_thrdd_init(data, hostname, port, ip_version, &hints)) {
*waitp = 1; /* expect asynchronous response */
return NULL;
}
if(async_thrdd_init(data, hostname, port, ip_version, &hints))
return CURLE_OK;
failf(data, "getaddrinfo() thread failed to start");
return NULL;
return CURLE_FAILED_INIT;
}
#endif /* !HAVE_GETADDRINFO */

View File

@ -118,11 +118,8 @@ CURLcode Curl_async_await(struct Curl_easy *data,
* Each resolver backend must of course make sure to return data in the
* correct format to comply with this.
*/
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp);
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
int port, int ip_version);
#ifdef USE_ARES
/* common functions for c-ares and threaded resolver with HTTPSRR */

View File

@ -347,7 +347,7 @@ Curl_he2ai(const struct hostent *he, int port)
#endif
/*
* Curl_ip2addr()
* ip2addr()
*
* This function takes an Internet address, in binary form, as input parameter
* along with its address family and the string version of the address, and it
@ -355,8 +355,9 @@ Curl_he2ai(const struct hostent *he, int port)
* given address/host
*/
struct Curl_addrinfo *
Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
static CURLcode
ip2addr(struct Curl_addrinfo **addrp,
int af, const void *inaddr, const char *hostname, int port)
{
struct Curl_addrinfo *ai;
size_t addrsize;
@ -369,6 +370,7 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
DEBUGASSERT(inaddr && hostname);
namelen = strlen(hostname) + 1;
*addrp = NULL;
if(af == AF_INET)
addrsize = sizeof(struct sockaddr_in);
@ -377,12 +379,12 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
addrsize = sizeof(struct sockaddr_in6);
#endif
else
return NULL;
return CURLE_BAD_FUNCTION_ARGUMENT;
/* allocate memory to hold the struct, the address and the name */
ai = calloc(1, sizeof(struct Curl_addrinfo) + addrsize + namelen);
if(!ai)
return NULL;
return CURLE_OUT_OF_MEMORY;
/* put the address after the struct */
ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
/* then put the name after the address */
@ -412,29 +414,46 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
break;
#endif
}
return ai;
*addrp = ai;
return CURLE_OK;
}
/*
* Given an IPv4 or IPv6 dotted string address, this converts it to a proper
* allocated Curl_addrinfo struct and returns it.
*/
struct Curl_addrinfo *Curl_str2addr(char *address, int port)
CURLcode Curl_str2addr(const char *address, int port,
struct Curl_addrinfo **addrp)
{
struct in_addr in;
if(curlx_inet_pton(AF_INET, address, &in) > 0)
/* This is a dotted IP address 123.123.123.123-style */
return Curl_ip2addr(AF_INET, &in, address, port);
return ip2addr(addrp, AF_INET, &in, address, port);
#ifdef USE_IPV6
{
struct in6_addr in6;
if(curlx_inet_pton(AF_INET6, address, &in6) > 0)
/* This is a dotted IPv6 address ::1-style */
return Curl_ip2addr(AF_INET6, &in6, address, port);
return ip2addr(addrp, AF_INET6, &in6, address, port);
}
#endif
return NULL; /* bad input format */
return CURLE_BAD_FUNCTION_ARGUMENT; /* bad input format */
}
bool Curl_is_ipaddr(const char *address)
{
struct in_addr in;
if(curlx_inet_pton(AF_INET, address, &in) > 0)
return TRUE;
#ifdef USE_IPV6
{
struct in6_addr in6;
if(curlx_inet_pton(AF_INET6, address, &in6) > 0)
/* This is a dotted IPv6 address ::1-style */
return TRUE;
}
#endif
return FALSE;
}
#ifdef USE_UNIX_SOCKETS

View File

@ -76,10 +76,8 @@ struct Curl_addrinfo *
Curl_he2ai(const struct hostent *he, int port);
#endif
struct Curl_addrinfo *
Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port);
struct Curl_addrinfo *Curl_str2addr(char *dotted, int port);
bool Curl_is_ipaddr(const char *address);
CURLcode Curl_str2addr(const char *dotted, int port, struct Curl_addrinfo **);
#ifdef USE_UNIX_SOCKETS
struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,

View File

@ -441,15 +441,12 @@ error:
}
/*
* Curl_doh() resolves a name using DoH. It resolves a name and returns a
* 'Curl_addrinfo *' with the address information.
* Curl_doh() starts a name resolve using DoH. It resolves a name and returns
* a 'Curl_addrinfo *' with the address information.
*/
struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp)
CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
int port, int ip_version)
{
CURLcode result = CURLE_OK;
struct doh_probes *dohp = NULL;
@ -467,12 +464,12 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
data->state.async.ip_version = ip_version;
data->state.async.hostname = strdup(hostname);
if(!data->state.async.hostname)
return NULL;
return CURLE_OUT_OF_MEMORY;
/* start clean, consider allocating this struct on demand */
data->state.async.doh = dohp = calloc(1, sizeof(struct doh_probes));
if(!dohp)
return NULL;
return CURLE_OUT_OF_MEMORY;
for(i = 0; i < DOH_SLOT_COUNT; ++i) {
dohp->probe_resp[i].probe_mid = UINT_MAX;
@ -527,12 +524,11 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
dohp->pending++;
}
#endif
*waitp = TRUE; /* this never returns synchronously */
return NULL;
return CURLE_OK;
error:
Curl_doh_cleanup(data);
return NULL;
return result;
}
static DOHcode doh_skipqname(const unsigned char *doh, size_t dohlen,
@ -1300,6 +1296,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
result = Curl_dnscache_add(data, dns);
*dnsp = data->state.async.dns;
}
else
Curl_freeaddrinfo(ai);
} /* address processing done */
/* All done */

View File

@ -112,15 +112,12 @@ struct doh_probes {
};
/*
* Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name
* and returns a 'Curl_addrinfo *' with the address information.
* Curl_doh() starts a name resolve using DoH (DNS-over-HTTPS). It resolves a
* name and returns a 'Curl_addrinfo *' with the address information.
*/
struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp);
CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
int port, int ip_version);
CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dns);

View File

@ -509,10 +509,8 @@ Curl_dnscache_mk_entry(struct Curl_easy *data,
/* shuffle addresses if requested */
if(data->set.dns_shuffle_addresses) {
CURLcode result = Curl_shuffle_addr(data, &addr);
if(result) {
Curl_freeaddrinfo(addr);
if(result)
return NULL;
}
}
#else
(void)data;
@ -522,10 +520,8 @@ Curl_dnscache_mk_entry(struct Curl_easy *data,
/* Create a new cache entry */
dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
if(!dns) {
Curl_freeaddrinfo(addr);
if(!dns)
return NULL;
}
dns->refcount = 1; /* the cache has the first reference */
dns->addr = addr; /* this is the address(es) */
@ -569,6 +565,7 @@ dnscache_add_addr(struct Curl_easy *data,
dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
(void *)dns);
if(!dns2) {
dns->addr = NULL;
dnscache_entry_free(dns);
return NULL;
}
@ -744,40 +741,6 @@ static bool tailmatch(const char *full, size_t flen,
return curl_strnequal(part, &full[flen - plen], plen);
}
static struct Curl_addrinfo *
convert_ipaddr_direct(const char *hostname, int port, bool *is_ipaddr)
{
struct in_addr in;
*is_ipaddr = FALSE;
/* First check if this is an IPv4 address string */
if(curlx_inet_pton(AF_INET, hostname, &in) > 0) {
/* This is a dotted IP address 123.123.123.123-style */
*is_ipaddr = TRUE;
#ifdef USE_RESOLVE_ON_IPS
(void)port;
return NULL;
#else
return Curl_ip2addr(AF_INET, &in, hostname, port);
#endif
}
#ifdef USE_IPV6
else {
struct in6_addr in6;
/* check if this is an IPv6 address string */
if(curlx_inet_pton(AF_INET6, hostname, &in6) > 0) {
/* This is an IPv6 address literal */
*is_ipaddr = TRUE;
#ifdef USE_RESOLVE_ON_IPS
return NULL;
#else
return Curl_ip2addr(AF_INET6, &in6, hostname, port);
#endif
}
}
#endif /* USE_IPV6 */
return NULL;
}
static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version)
{
#ifdef CURLRES_IPV6
@ -841,10 +804,10 @@ CURLcode Curl_resolv(struct Curl_easy *data,
struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns = NULL;
struct Curl_addrinfo *addr = NULL;
int respwait = 0;
bool is_ipaddr;
bool respwait = FALSE;
size_t hostname_len;
bool keep_negative = TRUE; /* cache a negative result */
CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
*entry = NULL;
@ -853,8 +816,11 @@ CURLcode Curl_resolv(struct Curl_easy *data,
#else
(void)allowDOH;
#endif
if(!dnscache)
DEBUGASSERT(dnscache);
if(!dnscache) {
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto error;
}
/* We should intentionally error and not resolve .onion TLDs */
hostname_len = strlen(hostname);
@ -874,6 +840,7 @@ CURLcode Curl_resolv(struct Curl_easy *data,
dnscache_unlock(data, dnscache);
if(dns) {
infof(data, "Hostname %s was found in DNS cache", hostname);
result = CURLE_OK;
goto out;
}
@ -882,7 +849,8 @@ CURLcode Curl_resolv(struct Curl_easy *data,
void *resolver = NULL;
int st;
#ifdef CURLRES_ASYNCH
if(Curl_async_get_impl(data, &resolver))
result = Curl_async_get_impl(data, &resolver);
if(result)
goto error;
#endif
Curl_set_in_callback(data, TRUE);
@ -891,57 +859,58 @@ CURLcode Curl_resolv(struct Curl_easy *data,
Curl_set_in_callback(data, FALSE);
if(st) {
keep_negative = FALSE;
result = CURLE_ABORTED_BY_CALLBACK;
goto error;
}
}
/* shortcut literal IP addresses, if we are not told to resolve them. */
addr = convert_ipaddr_direct(hostname, port, &is_ipaddr);
if(addr)
goto out;
if(Curl_is_ipaddr(hostname)) {
#ifndef USE_RESOLVE_ON_IPS
/* allowed to convert, hostname is IP address, then NULL means error */
if(is_ipaddr) {
keep_negative = FALSE;
goto error;
}
/* shortcut literal IP addresses, if we are not told to resolve them. */
result = Curl_str2addr(hostname, port, &addr);
if(result)
goto error;
goto out;
#endif
}
/* Really need a resolver for hostname. */
if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
goto error;
if(!is_ipaddr &&
(curl_strequal(hostname, "localhost") ||
curl_strequal(hostname, "localhost.") ||
tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
tailmatch(hostname, hostname_len, STRCONST(".localhost.")))) {
if(curl_strequal(hostname, "localhost") ||
curl_strequal(hostname, "localhost.") ||
tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
tailmatch(hostname, hostname_len, STRCONST(".localhost."))) {
addr = get_localhost(port, hostname);
result = addr ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
#ifndef CURL_DISABLE_DOH
else if(!is_ipaddr && allowDOH && data->set.doh) {
addr = Curl_doh(data, hostname, port, ip_version, &respwait);
else if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) {
result = Curl_doh(data, hostname, port, ip_version);
respwait = TRUE;
}
#endif
else {
/* Can we provide the requested IP specifics in resolving? */
if(!can_resolve_ip_version(data, ip_version))
if(!can_resolve_ip_version(data, ip_version)) {
result = CURLE_COULDNT_RESOLVE_HOST;
goto error;
}
#ifdef CURLRES_ASYNCH
addr = Curl_async_getaddrinfo(data, hostname, port, ip_version, &respwait);
result = Curl_async_getaddrinfo(data, hostname, port, ip_version);
respwait = TRUE;
#else
respwait = 0; /* no async waiting here */
respwait = FALSE; /* no async waiting here */
addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version);
if(addr)
result = CURLE_OK;
#endif
}
out:
/* We either have found a `dns` or looked up the `addr`
* or `respwait` is set for an async operation.
* Everything else is a failure to resolve. */
if(dns) {
/* We either have found a `dns` or looked up the `addr` or `respwait` is set
* for an async operation. Everything else is a failure to resolve. */
if(result)
;
else if(dns) {
if(!dns->addr) {
infof(data, "Negative DNS entry");
dns->refcount--;
@ -955,7 +924,12 @@ out:
dns = Curl_dnscache_mk_entry(data, addr, hostname, 0, port, FALSE);
if(!dns || Curl_dnscache_add(data, dns)) {
/* this is OOM or similar, do not store such negative resolves */
Curl_freeaddrinfo(addr);
if(dns)
/* avoid a dangling pointer to addr in the dying dns entry */
dns->addr = NULL;
keep_negative = FALSE;
result = CURLE_OUT_OF_MEMORY;
goto error;
}
show_resolve_info(data, dns);
@ -967,6 +941,7 @@ out:
*entry = dns;
return dns ? CURLE_OK : CURLE_AGAIN;
}
result = CURLE_COULDNT_RESOLVE_HOST;
}
error:
if(dns)
@ -974,7 +949,8 @@ error:
Curl_async_shutdown(data);
if(keep_negative)
store_negative_resolve(data, hostname, port);
return CURLE_COULDNT_RESOLVE_HOST;
DEBUGASSERT(result);
return result;
}
CURLcode Curl_resolv_blocking(struct Curl_easy *data,
@ -1338,6 +1314,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
while(*host) {
struct Curl_str target;
struct Curl_addrinfo *ai;
CURLcode result;
if(!curlx_str_single(&host, '[')) {
if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
@ -1368,8 +1345,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
memcpy(address, curlx_str(&target), curlx_strlen(&target));
address[curlx_strlen(&target)] = '\0';
ai = Curl_str2addr(address, (int)port);
if(!ai) {
result = Curl_str2addr(address, (int)port, &ai);
if(result) {
infof(data, "Resolve address '%s' found illegal", address);
goto err;
}
@ -1428,11 +1405,12 @@ err:
/* put this new host in the cache */
dns = dnscache_add_addr(data, dnscache, head, curlx_str(&source),
curlx_strlen(&source), (int)port, permanent);
if(dns) {
if(dns)
/* release the returned reference; the cache itself will keep the
* entry alive: */
dns->refcount--;
}
else
Curl_freeaddrinfo(head);
dnscache_unlock(data, dnscache);

View File

@ -82,9 +82,9 @@ static CURLcode test_lib655(const char *URL)
/* this should fail */
res = curl_easy_perform(curl);
if(res != CURLE_COULDNT_RESOLVE_HOST) {
if(res != CURLE_ABORTED_BY_CALLBACK) {
curl_mfprintf(stderr, "curl_easy_perform should have returned "
"CURLE_COULDNT_RESOLVE_HOST but instead returned error %d\n",
"CURLE_ABORTED_BY_CALLBACK but instead returned error %d\n",
res);
if(res == CURLE_OK)
res = TEST_ERR_FAILURE;