From ce06fe7771052549ff430c86173b2eaca91f8a9c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 24 Nov 2025 14:00:09 +0100 Subject: [PATCH] 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 --- lib/asyn-ares.c | 22 +++---- lib/asyn-thrdd.c | 17 ++---- lib/asyn.h | 7 +-- lib/curl_addrinfo.c | 41 +++++++++---- lib/curl_addrinfo.h | 6 +- lib/doh.c | 22 ++++--- lib/doh.h | 11 ++-- lib/hostip.c | 132 +++++++++++++++++------------------------ tests/libtest/lib655.c | 4 +- 9 files changed, 117 insertions(+), 145 deletions(-) diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index ab57d5e00b..d459fd4a75 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -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: diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index f4f57b547a..71e31e6d0d 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -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 */ diff --git a/lib/asyn.h b/lib/asyn.h index 7863042bbe..8da7dd9720 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -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 */ diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index fc26bef833..cd9b52feba 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -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 ip2addr(addrp, AF_INET6, &in6, address, port); + } +#endif + 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 Curl_ip2addr(AF_INET6, &in6, address, port); + return TRUE; } #endif - return NULL; /* bad input format */ + return FALSE; } #ifdef USE_UNIX_SOCKETS diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h index 2303e95e31..9a26c67849 100644 --- a/lib/curl_addrinfo.h +++ b/lib/curl_addrinfo.h @@ -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, diff --git a/lib/doh.c b/lib/doh.c index 45524edf4c..83a83f5346 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -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 */ diff --git a/lib/doh.h b/lib/doh.h index 3fd7de2c1c..726fb9f735 100644 --- a/lib/doh.h +++ b/lib/doh.h @@ -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); diff --git a/lib/hostip.c b/lib/hostip.c index d5f753f4b4..4c1bb92367 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -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); diff --git a/tests/libtest/lib655.c b/tests/libtest/lib655.c index 07392cbd86..a213a1c493 100644 --- a/tests/libtest/lib655.c +++ b/tests/libtest/lib655.c @@ -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; -- 2.47.3