diff options
author | Daniel Stenberg <daniel@haxx.se> | 2021-08-10 16:11:51 +0200 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2021-08-11 09:53:06 +0200 |
commit | ba904db0705c931ff52b4181b21ae95e6e0af3cd (patch) | |
tree | 1103ac3779b3c1453973d3e04f476161dec949ba /lib | |
parent | 2bfa57bff184437028025933d26fecb215355173 (diff) | |
download | curl-ba904db0705c931ff52b4181b21ae95e6e0af3cd.tar.gz |
ares: use ares_getaddrinfo()
ares_getaddrinfo() is the getaddrinfo() cloned provided by c-ares, introduced
in version 1.16.0.
With older c-ares versions, curl invokes ares_gethostbyname() twice - once for
IPv4 and once for IPv6 to resolve both addresses, and then combines the
returned results.
Reported-by: jjandesmet
Fixes #7364
Closes #7552
Diffstat (limited to 'lib')
-rw-r--r-- | lib/asyn-ares.c | 117 |
1 files changed, 115 insertions, 2 deletions
diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 839fabb86..0be661395 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -86,7 +86,7 @@ #include "memdebug.h" struct thread_data { - int num_pending; /* number of ares_gethostbyname() requests */ + int num_pending; /* number of outstanding c-ares requests */ struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */ int last_status; @@ -490,6 +490,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, return result; } +#if ARES_VERSION < 0x011000 /* before 1.16.0 */ + /* Connects results to the list */ static void compound_results(struct thread_data *res, struct Curl_addrinfo *ai) @@ -620,8 +622,98 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */ } } } +#else +/* c-ares 1.16.0 or later */ /* + * ares2addr() converts an address list provided by c-ares to an internal + * libcurl compatible list + */ +static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node) +{ + /* traverse the ares_addrinfo_node list */ + struct ares_addrinfo_node *ai; + struct Curl_addrinfo *cafirst = NULL; + struct Curl_addrinfo *calast = NULL; + int error = 0; + + for(ai = node; ai != NULL; ai = ai->ai_next) { + size_t ss_size; + struct Curl_addrinfo *ca; + /* ignore elements with unsupported address family, */ + /* settle family-specific sockaddr structure size. */ + if(ai->ai_family == AF_INET) + ss_size = sizeof(struct sockaddr_in); +#ifdef ENABLE_IPV6 + else if(ai->ai_family == AF_INET6) + ss_size = sizeof(struct sockaddr_in6); +#endif + else + continue; + + /* ignore elements without required address info */ + if(!ai->ai_addr || !(ai->ai_addrlen > 0)) + continue; + + /* ignore elements with bogus address size */ + if((size_t)ai->ai_addrlen < ss_size) + continue; + + ca = malloc(sizeof(struct Curl_addrinfo) + ss_size); + if(!ca) { + error = EAI_MEMORY; + break; + } + + /* copy each structure member individually, member ordering, */ + /* size, or padding might be different for each platform. */ + + ca->ai_flags = ai->ai_flags; + ca->ai_family = ai->ai_family; + ca->ai_socktype = ai->ai_socktype; + ca->ai_protocol = ai->ai_protocol; + ca->ai_addrlen = (curl_socklen_t)ss_size; + ca->ai_addr = NULL; + ca->ai_canonname = NULL; + ca->ai_next = NULL; + + ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); + memcpy(ca->ai_addr, ai->ai_addr, ss_size); + + /* if the return list is empty, this becomes the first element */ + if(!cafirst) + cafirst = ca; + + /* add this element last in the return list */ + if(calast) + calast->ai_next = ca; + calast = ca; + } + + /* if we failed, destroy the Curl_addrinfo list */ + if(error) { + Curl_freeaddrinfo(cafirst); + cafirst = NULL; + } + + return cafirst; +} + +static void addrinfo_cb(void *arg, int status, int timeouts, + struct ares_addrinfo *result) +{ + struct Curl_easy *data = (struct Curl_easy *)arg; + struct thread_data *res = data->state.async.tdata; + (void)timeouts; + if(ARES_SUCCESS == status) { + res->temp_ai = ares2addr(result->nodes); + res->last_status = CURL_ASYNC_SUCCESS; + } + res->num_pending--; +} + +#endif +/* * Curl_resolver_getaddrinfo() - when using ares * * Returns name information about the given hostname and port number. If @@ -659,6 +751,27 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, res->last_status = ARES_ENOTFOUND; #if ARES_VERSION >= 0x010601 + { + struct ares_addrinfo_hints hints; + char service[12]; + int pf = PF_INET; + memset(&hints, 0, sizeof(hints)); +#ifdef CURLRES_IPV6 + if(Curl_ipv6works(data)) + /* The stack seems to be IPv6-enabled */ + pf = PF_UNSPEC; +#endif /* CURLRES_IPV6 */ + hints.ai_family = pf; + hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)? + SOCK_STREAM : SOCK_DGRAM; + msnprintf(service, sizeof(service), "%d", port); + res->num_pending = 1; + ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname, + service, &hints, addrinfo_cb, data); + } +#else + +#if ARES_VERSION >= 0x010601 /* IPv6 supported by c-ares since 1.6.1 */ if(Curl_ipv6works(data)) { /* The stack seems to be IPv6-enabled */ @@ -680,7 +793,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, hostname, PF_INET, query_completed_cb, data); } - +#endif *waitp = 1; /* expect asynchronous response */ } return NULL; /* no struct yet */ |