quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

asyn-ares.c (30051B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #ifdef CURLRES_ARES
     28 
     29 /***********************************************************************
     30  * Only for ares-enabled builds
     31  * And only for functions that fulfill the asynch resolver backend API
     32  * as defined in asyn.h, nothing else belongs in this file!
     33  **********************************************************************/
     34 
     35 #include <limits.h>
     36 #ifdef HAVE_NETINET_IN_H
     37 #include <netinet/in.h>
     38 #endif
     39 #ifdef HAVE_NETDB_H
     40 #include <netdb.h>
     41 #endif
     42 #ifdef HAVE_ARPA_INET_H
     43 #include <arpa/inet.h>
     44 #endif
     45 #ifdef __VMS
     46 #include <in.h>
     47 #include <inet.h>
     48 #endif
     49 
     50 #include "urldata.h"
     51 #include "cfilters.h"
     52 #include "sendf.h"
     53 #include "hostip.h"
     54 #include "hash.h"
     55 #include "share.h"
     56 #include "url.h"
     57 #include "multiif.h"
     58 #include "curlx/inet_pton.h"
     59 #include "connect.h"
     60 #include "select.h"
     61 #include "progress.h"
     62 #include "curlx/timediff.h"
     63 #include "httpsrr.h"
     64 #include "strdup.h"
     65 
     66 #include <ares.h>
     67 #include <ares_version.h> /* really old c-ares did not include this by
     68                              itself */
     69 
     70 #if ARES_VERSION >= 0x010601
     71 /* IPv6 supported since 1.6.1 */
     72 #define HAVE_CARES_IPV6 1
     73 #endif
     74 
     75 #if ARES_VERSION >= 0x010704
     76 #define HAVE_CARES_SERVERS_CSV 1
     77 #define HAVE_CARES_LOCAL_DEV 1
     78 #define HAVE_CARES_SET_LOCAL 1
     79 #endif
     80 
     81 #if ARES_VERSION >= 0x010b00
     82 #define HAVE_CARES_PORTS_CSV 1
     83 #endif
     84 
     85 #if ARES_VERSION >= 0x011000
     86 /* 1.16.0 or later has ares_getaddrinfo */
     87 #define HAVE_CARES_GETADDRINFO 1
     88 #endif
     89 
     90 #ifdef USE_HTTPSRR
     91 #if ARES_VERSION < 0x011c00
     92 #error "requires c-ares 1.28.0 or newer for HTTPSRR"
     93 #endif
     94 #define HTTPSRR_WORKS
     95 #endif
     96 
     97 /* The last 3 #include files should be in this order */
     98 #include "curl_printf.h"
     99 #include "curl_memory.h"
    100 #include "memdebug.h"
    101 
    102 /* How long we are willing to wait for additional parallel responses after
    103    obtaining a "definitive" one. For old c-ares without getaddrinfo.
    104 
    105    This is intended to equal the c-ares default timeout. cURL always uses that
    106    default value. Unfortunately, c-ares does not expose its default timeout in
    107    its API, but it is officially documented as 5 seconds.
    108 
    109    See query_completed_cb() for an explanation of how this is used.
    110  */
    111 #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
    112 
    113 #define CARES_TIMEOUT_PER_ATTEMPT 2000
    114 
    115 static int ares_ver = 0;
    116 
    117 static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
    118                                            bool reset_on_null);
    119 
    120 /*
    121  * Curl_async_global_init() - the generic low-level asynchronous name
    122  * resolve API. Called from curl_global_init() to initialize global resolver
    123  * environment. Initializes ares library.
    124  */
    125 int Curl_async_global_init(void)
    126 {
    127 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
    128   if(ares_library_init(ARES_LIB_INIT_ALL)) {
    129     return CURLE_FAILED_INIT;
    130   }
    131 #endif
    132   ares_version(&ares_ver);
    133   return CURLE_OK;
    134 }
    135 
    136 /*
    137  * Curl_async_global_cleanup()
    138  *
    139  * Called from curl_global_cleanup() to destroy global resolver environment.
    140  * Deinitializes ares library.
    141  */
    142 void Curl_async_global_cleanup(void)
    143 {
    144 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
    145   ares_library_cleanup();
    146 #endif
    147 }
    148 
    149 
    150 static void sock_state_cb(void *data, ares_socket_t socket_fd,
    151                           int readable, int writable)
    152 {
    153   struct Curl_easy *easy = data;
    154   if(!readable && !writable) {
    155     DEBUGASSERT(easy);
    156     Curl_multi_will_close(easy, socket_fd);
    157   }
    158 }
    159 
    160 static CURLcode async_ares_init(struct Curl_easy *data)
    161 {
    162   struct async_ares_ctx *ares = &data->state.async.ares;
    163   int status;
    164   struct ares_options options;
    165   int optmask = ARES_OPT_SOCK_STATE_CB;
    166   CURLcode rc = CURLE_OK;
    167 
    168   options.sock_state_cb = sock_state_cb;
    169   options.sock_state_cb_data = data;
    170 
    171   DEBUGASSERT(!ares->channel);
    172   /*
    173      if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
    174 
    175      if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
    176      to set the timeout value;
    177 
    178      if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
    179      overwrite c-ares' timeout.
    180   */
    181   DEBUGASSERT(ares_ver);
    182   if(ares_ver < 0x011400) {
    183     options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
    184     optmask |= ARES_OPT_TIMEOUTMS;
    185   }
    186 
    187   status = ares_init_options(&ares->channel, &options, optmask);
    188   if(status != ARES_SUCCESS) {
    189     ares->channel = NULL;
    190     rc = (status == ARES_ENOMEM) ?
    191          CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
    192     goto out;
    193   }
    194 
    195   rc = async_ares_set_dns_servers(data, FALSE);
    196   if(rc && rc != CURLE_NOT_BUILT_IN)
    197     goto out;
    198 
    199   rc = Curl_async_ares_set_dns_interface(data);
    200   if(rc && rc != CURLE_NOT_BUILT_IN)
    201     goto out;
    202 
    203   rc = Curl_async_ares_set_dns_local_ip4(data);
    204   if(rc && rc != CURLE_NOT_BUILT_IN)
    205     goto out;
    206 
    207   rc = Curl_async_ares_set_dns_local_ip6(data);
    208   if(rc && rc != CURLE_NOT_BUILT_IN)
    209     goto out;
    210 
    211   rc = CURLE_OK;
    212 
    213 out:
    214   if(rc && ares->channel) {
    215     ares_destroy(ares->channel);
    216     ares->channel = NULL;
    217   }
    218   return rc;
    219 }
    220 
    221 static CURLcode async_ares_init_lazy(struct Curl_easy *data)
    222 {
    223   struct async_ares_ctx *ares = &data->state.async.ares;
    224   if(!ares->channel)
    225     return async_ares_init(data);
    226   return CURLE_OK;
    227 }
    228 
    229 CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
    230 {
    231   struct async_ares_ctx *ares = &data->state.async.ares;
    232   CURLcode result = CURLE_OK;
    233   if(!ares->channel) {
    234     result = async_ares_init(data);
    235   }
    236   *impl = ares->channel;
    237   return result;
    238 }
    239 
    240 static void async_ares_cleanup(struct Curl_easy *data);
    241 
    242 void Curl_async_ares_shutdown(struct Curl_easy *data)
    243 {
    244   struct async_ares_ctx *ares = &data->state.async.ares;
    245   if(ares->channel)
    246     ares_cancel(ares->channel);
    247   async_ares_cleanup(data);
    248 }
    249 
    250 void Curl_async_ares_destroy(struct Curl_easy *data)
    251 {
    252   struct async_ares_ctx *ares = &data->state.async.ares;
    253   Curl_async_ares_shutdown(data);
    254   if(ares->channel) {
    255     ares_destroy(ares->channel);
    256     ares->channel = NULL;
    257   }
    258 }
    259 
    260 /*
    261  * async_ares_cleanup() cleans up async resolver data.
    262  */
    263 static void async_ares_cleanup(struct Curl_easy *data)
    264 {
    265   struct async_ares_ctx *ares = &data->state.async.ares;
    266   if(ares->temp_ai) {
    267     Curl_freeaddrinfo(ares->temp_ai);
    268     ares->temp_ai = NULL;
    269   }
    270 #ifdef USE_HTTPSRR
    271   Curl_httpsrr_cleanup(&ares->hinfo);
    272 #endif
    273 }
    274 
    275 /*
    276  * Curl_async_getsock() is called when someone from the outside world
    277  * (using curl_multi_fdset()) wants to get our fd_set setup.
    278  */
    279 
    280 int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
    281 {
    282   struct async_ares_ctx *ares = &data->state.async.ares;
    283   DEBUGASSERT(ares->channel);
    284   return Curl_ares_getsock(data, ares->channel, socks);
    285 }
    286 
    287 /*
    288  * Curl_async_is_resolved() is called repeatedly to check if a previous
    289  * name resolve request has completed. It should also make sure to time-out if
    290  * the operation seems to take too long.
    291  *
    292  * Returns normal CURLcode errors.
    293  */
    294 CURLcode Curl_async_is_resolved(struct Curl_easy *data,
    295                                 struct Curl_dns_entry **dns)
    296 {
    297   struct async_ares_ctx *ares = &data->state.async.ares;
    298   CURLcode result = CURLE_OK;
    299 
    300   DEBUGASSERT(dns);
    301   *dns = NULL;
    302 
    303   if(data->state.async.done) {
    304     *dns = data->state.async.dns;
    305     return CURLE_OK;
    306   }
    307 
    308   if(Curl_ares_perform(ares->channel, 0) < 0)
    309     return CURLE_UNRECOVERABLE_POLL;
    310 
    311 #ifndef HAVE_CARES_GETADDRINFO
    312   /* Now that we have checked for any last minute results above, see if there
    313      are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
    314      expires. */
    315   if(ares->num_pending
    316      /* This is only set to non-zero if the timer was started. */
    317      && (ares->happy_eyeballs_dns_time.tv_sec
    318          || ares->happy_eyeballs_dns_time.tv_usec)
    319      && (curlx_timediff(curlx_now(), ares->happy_eyeballs_dns_time)
    320          >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
    321     /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
    322        running. */
    323     memset(&ares->happy_eyeballs_dns_time, 0,
    324            sizeof(ares->happy_eyeballs_dns_time));
    325 
    326     /* Cancel the raw c-ares request, which will fire query_completed_cb() with
    327        ARES_ECANCELLED synchronously for all pending responses. This will
    328        leave us with res->num_pending == 0, which is perfect for the next
    329        block. */
    330     ares_cancel(ares->channel);
    331     DEBUGASSERT(ares->num_pending == 0);
    332   }
    333 #endif
    334 
    335   if(!ares->num_pending) {
    336     /* all c-ares operations done, what is the result to report? */
    337     Curl_resolv_unlink(data, &data->state.async.dns);
    338     data->state.async.done = TRUE;
    339     result = ares->result;
    340     if(ares->last_status == CURL_ASYNC_SUCCESS && !result) {
    341       data->state.async.dns =
    342         Curl_dnscache_mk_entry(data, ares->temp_ai,
    343                                data->state.async.hostname, 0,
    344                                data->state.async.port, FALSE);
    345       ares->temp_ai = NULL; /* temp_ai now owned by entry */
    346 #ifdef HTTPSRR_WORKS
    347         if(data->state.async.dns) {
    348           struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
    349           if(!lhrr)
    350             result = CURLE_OUT_OF_MEMORY;
    351           else
    352             data->state.async.dns->hinfo = lhrr;
    353         }
    354 #endif
    355       if(!result && data->state.async.dns)
    356         result = Curl_dnscache_add(data, data->state.async.dns);
    357     }
    358     /* if we have not found anything, report the proper
    359      * CURLE_COULDNT_RESOLVE_* code */
    360     if(!result && !data->state.async.dns)
    361       result = Curl_resolver_error(data);
    362     if(result)
    363       Curl_resolv_unlink(data, &data->state.async.dns);
    364     *dns = data->state.async.dns;
    365     CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
    366                  result, *dns ? "" : "not ");
    367     async_ares_cleanup(data);
    368   }
    369   return result;
    370 }
    371 
    372 /*
    373  * Curl_async_await()
    374  *
    375  * Waits for a resolve to finish. This function should be avoided since using
    376  * this risk getting the multi interface to "hang".
    377  *
    378  * 'entry' MUST be non-NULL.
    379  *
    380  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
    381  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
    382  */
    383 CURLcode Curl_async_await(struct Curl_easy *data,
    384                           struct Curl_dns_entry **entry)
    385 {
    386   struct async_ares_ctx *ares = &data->state.async.ares;
    387   CURLcode result = CURLE_OK;
    388   timediff_t timeout;
    389   struct curltime now = curlx_now();
    390 
    391   DEBUGASSERT(entry);
    392   *entry = NULL; /* clear on entry */
    393 
    394   timeout = Curl_timeleft(data, &now, TRUE);
    395   if(timeout < 0) {
    396     /* already expired! */
    397     connclose(data->conn, "Timed out before name resolve started");
    398     return CURLE_OPERATION_TIMEDOUT;
    399   }
    400   if(!timeout)
    401     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
    402 
    403   /* Wait for the name resolve query to complete. */
    404   while(!result) {
    405     struct timeval *tvp, tv, store;
    406     int itimeout;
    407     timediff_t timeout_ms;
    408 
    409 #if TIMEDIFF_T_MAX > INT_MAX
    410     itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
    411 #else
    412     itimeout = (int)timeout;
    413 #endif
    414 
    415     store.tv_sec = itimeout/1000;
    416     store.tv_usec = (itimeout%1000)*1000;
    417 
    418     tvp = ares_timeout(ares->channel, &store, &tv);
    419 
    420     /* use the timeout period ares returned to us above if less than one
    421        second is left, otherwise just use 1000ms to make sure the progress
    422        callback gets called frequent enough */
    423     if(!tvp->tv_sec)
    424       timeout_ms = (timediff_t)(tvp->tv_usec/1000);
    425     else
    426       timeout_ms = 1000;
    427 
    428     if(Curl_ares_perform(ares->channel, timeout_ms) < 0)
    429       return CURLE_UNRECOVERABLE_POLL;
    430 
    431     result = Curl_async_is_resolved(data, entry);
    432     if(result || data->state.async.done)
    433       break;
    434 
    435     if(Curl_pgrsUpdate(data))
    436       result = CURLE_ABORTED_BY_CALLBACK;
    437     else {
    438       struct curltime now2 = curlx_now();
    439       timediff_t timediff = curlx_timediff(now2, now); /* spent time */
    440       if(timediff <= 0)
    441         timeout -= 1; /* always deduct at least 1 */
    442       else if(timediff > timeout)
    443         timeout = -1;
    444       else
    445         timeout -= timediff;
    446       now = now2; /* for next loop */
    447     }
    448     if(timeout < 0)
    449       result = CURLE_OPERATION_TIMEDOUT;
    450   }
    451 
    452   /* Operation complete, if the lookup was successful we now have the entry
    453      in the cache. */
    454   data->state.async.done = TRUE;
    455   *entry = data->state.async.dns;
    456 
    457   if(result)
    458     ares_cancel(ares->channel);
    459   return result;
    460 }
    461 
    462 #ifndef HAVE_CARES_GETADDRINFO
    463 
    464 /* Connects results to the list */
    465 static void async_addr_concat(struct Curl_addrinfo **pbase,
    466                               struct Curl_addrinfo *ai)
    467 {
    468   if(!ai)
    469     return;
    470 
    471   /* When adding `ai` to an existing address list, we prefer ipv6
    472    * to be in front. */
    473 #ifdef USE_IPV6 /* CURLRES_IPV6 */
    474   if(*pbase && (*pbase)->ai_family == PF_INET6) {
    475     /* ipv6 already in front, append `ai` */
    476     struct Curl_addrinfo *tail = *pbase;
    477     while(tail->ai_next)
    478       tail = tail->ai_next;
    479     tail->ai_next = ai;
    480   }
    481   else
    482 #endif /* CURLRES_IPV6 */
    483   {
    484     /* prepend to the (possibly) existing list. */
    485     struct Curl_addrinfo *tail = ai;
    486     while(tail->ai_next)
    487       tail = tail->ai_next;
    488     tail->ai_next = *pbase;
    489     *pbase = ai;
    490   }
    491 }
    492 
    493 /*
    494  * ares_query_completed_cb() is the callback that ares will call when
    495  * the host query initiated by ares_gethostbyname() from
    496  * Curl_async_getaddrinfo(), when using ares, is completed either
    497  * successfully or with failure.
    498  */
    499 static void async_ares_hostbyname_cb(void *user_data,
    500                                      int status,
    501                                      int timeouts,
    502                                      struct hostent *hostent)
    503 {
    504   struct Curl_easy *data = (struct Curl_easy *)user_data;
    505   struct async_ares_ctx *ares = &data->state.async.ares;
    506 
    507   (void)timeouts; /* ignored */
    508 
    509   if(ARES_EDESTRUCTION == status)
    510     /* when this ares handle is getting destroyed, the 'arg' pointer may not
    511        be valid so only defer it when we know the 'status' says its fine! */
    512     return;
    513 
    514   if(CURL_ASYNC_SUCCESS == status) {
    515     ares->last_status = status; /* one success overrules any error */
    516     async_addr_concat(&ares->temp_ai,
    517       Curl_he2ai(hostent, data->state.async.port));
    518   }
    519   else if(ares->last_status != ARES_SUCCESS) {
    520     /* no success so far, remember error */
    521     ares->last_status = status;
    522   }
    523 
    524   ares->num_pending--;
    525 
    526   CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, "
    527                "addr=%sfound",
    528                status, ares->num_pending, ares->temp_ai ? "" : "not ");
    529   /* If there are responses still pending, we presume they must be the
    530      complementary IPv4 or IPv6 lookups that we started in parallel in
    531      Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a
    532      "definitive" response from one of a set of parallel queries, we need to
    533      think about how long we are willing to wait for more responses. */
    534   if(ares->num_pending
    535      /* Only these c-ares status values count as "definitive" for these
    536         purposes. For example, ARES_ENODATA is what we expect when there is
    537         no IPv6 entry for a domain name, and that is not a reason to get more
    538         aggressive in our timeouts for the other response. Other errors are
    539         either a result of bad input (which should affect all parallel
    540         requests), local or network conditions, non-definitive server
    541         responses, or us cancelling the request. */
    542      && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
    543     /* Right now, there can only be up to two parallel queries, so do not
    544        bother handling any other cases. */
    545     DEBUGASSERT(ares->num_pending == 1);
    546 
    547     /* it is possible that one of these parallel queries could succeed
    548        quickly, but the other could always fail or timeout (when we are
    549        talking to a pool of DNS servers that can only successfully resolve
    550        IPv4 address, for example).
    551 
    552        it is also possible that the other request could always just take
    553        longer because it needs more time or only the second DNS server can
    554        fulfill it successfully. But, to align with the philosophy of Happy
    555        Eyeballs, we do not want to wait _too_ long or users will think
    556        requests are slow when IPv6 lookups do not actually work (but IPv4
    557        ones do).
    558 
    559        So, now that we have a usable answer (some IPv4 addresses, some IPv6
    560        addresses, or "no such domain"), we start a timeout for the remaining
    561        pending responses. Even though it is typical that this resolved
    562        request came back quickly, that needn't be the case. It might be that
    563        this completing request did not get a result from the first DNS
    564        server or even the first round of the whole DNS server pool. So it
    565        could already be quite some time after we issued the DNS queries in
    566        the first place. Without modifying c-ares, we cannot know exactly
    567        where in its retry cycle we are. We could guess based on how much
    568        time has gone by, but it does not really matter. Happy Eyeballs tells
    569        us that, given usable information in hand, we simply do not want to
    570        wait "too much longer" after we get a result.
    571 
    572        We simply wait an additional amount of time equal to the default
    573        c-ares query timeout. That is enough time for a typical parallel
    574        response to arrive without being "too long". Even on a network
    575        where one of the two types of queries is failing or timing out
    576        constantly, this will usually mean we wait a total of the default
    577        c-ares timeout (5 seconds) plus the round trip time for the successful
    578        request, which seems bearable. The downside is that c-ares might race
    579        with us to issue one more retry just before we give up, but it seems
    580        better to "waste" that request instead of trying to guess the perfect
    581        timeout to prevent it. After all, we do not even know where in the
    582        c-ares retry cycle each request is.
    583     */
    584     ares->happy_eyeballs_dns_time = curlx_now();
    585     Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
    586                 EXPIRE_HAPPY_EYEBALLS_DNS);
    587   }
    588 }
    589 
    590 #else
    591 /* c-ares 1.16.0 or later */
    592 
    593 /*
    594  * async_ares_node2addr() converts an address list provided by c-ares
    595  * to an internal libcurl compatible list.
    596  */
    597 static struct Curl_addrinfo *
    598 async_ares_node2addr(struct ares_addrinfo_node *node)
    599 {
    600   /* traverse the ares_addrinfo_node list */
    601   struct ares_addrinfo_node *ai;
    602   struct Curl_addrinfo *cafirst = NULL;
    603   struct Curl_addrinfo *calast = NULL;
    604   int error = 0;
    605 
    606   for(ai = node; ai != NULL; ai = ai->ai_next) {
    607     size_t ss_size;
    608     struct Curl_addrinfo *ca;
    609     /* ignore elements with unsupported address family, */
    610     /* settle family-specific sockaddr structure size.  */
    611     if(ai->ai_family == AF_INET)
    612       ss_size = sizeof(struct sockaddr_in);
    613 #ifdef USE_IPV6
    614     else if(ai->ai_family == AF_INET6)
    615       ss_size = sizeof(struct sockaddr_in6);
    616 #endif
    617     else
    618       continue;
    619 
    620     /* ignore elements without required address info */
    621     if(!ai->ai_addr || !(ai->ai_addrlen > 0))
    622       continue;
    623 
    624     /* ignore elements with bogus address size */
    625     if((size_t)ai->ai_addrlen < ss_size)
    626       continue;
    627 
    628     ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
    629     if(!ca) {
    630       error = EAI_MEMORY;
    631       break;
    632     }
    633 
    634     /* copy each structure member individually, member ordering, */
    635     /* size, or padding might be different for each platform.    */
    636 
    637     ca->ai_flags     = ai->ai_flags;
    638     ca->ai_family    = ai->ai_family;
    639     ca->ai_socktype  = ai->ai_socktype;
    640     ca->ai_protocol  = ai->ai_protocol;
    641     ca->ai_addrlen   = (curl_socklen_t)ss_size;
    642     ca->ai_addr      = NULL;
    643     ca->ai_canonname = NULL;
    644     ca->ai_next      = NULL;
    645 
    646     ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
    647     memcpy(ca->ai_addr, ai->ai_addr, ss_size);
    648 
    649     /* if the return list is empty, this becomes the first element */
    650     if(!cafirst)
    651       cafirst = ca;
    652 
    653     /* add this element last in the return list */
    654     if(calast)
    655       calast->ai_next = ca;
    656     calast = ca;
    657   }
    658 
    659   /* if we failed, destroy the Curl_addrinfo list */
    660   if(error) {
    661     Curl_freeaddrinfo(cafirst);
    662     cafirst = NULL;
    663   }
    664 
    665   return cafirst;
    666 }
    667 
    668 static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
    669                                    struct ares_addrinfo *result)
    670 {
    671   struct Curl_easy *data = (struct Curl_easy *)user_data;
    672   struct async_ares_ctx *ares = &data->state.async.ares;
    673   (void)timeouts;
    674   CURL_TRC_DNS(data, "asyn-ares: addrinfo callback, status=%d", status);
    675   if(ARES_SUCCESS == status) {
    676     ares->temp_ai = async_ares_node2addr(result->nodes);
    677     ares->last_status = CURL_ASYNC_SUCCESS;
    678     ares_freeaddrinfo(result);
    679   }
    680   ares->num_pending--;
    681   CURL_TRC_DNS(data, "ares: addrinfo done, status=%d, pending=%d, "
    682                "addr=%sfound",
    683                status, ares->num_pending, ares->temp_ai ? "" : "not ");
    684 }
    685 
    686 #endif
    687 
    688 #ifdef USE_HTTPSRR
    689 static void async_ares_rr_done(void *user_data, ares_status_t status,
    690                                size_t timeouts,
    691                                const ares_dns_record_t *dnsrec)
    692 {
    693   struct Curl_easy *data = user_data;
    694   struct async_ares_ctx *ares = &data->state.async.ares;
    695 
    696   (void)timeouts;
    697   --ares->num_pending;
    698   CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, "
    699                "dnsres=%sfound",
    700                status, ares->num_pending,
    701                (dnsrec &&
    702                 ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ?
    703                 "" : "not ");
    704   if((ARES_SUCCESS != status) || !dnsrec)
    705     return;
    706   ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo);
    707 }
    708 #endif /* USE_HTTPSRR */
    709 
    710 /*
    711  * Curl_async_getaddrinfo() - when using ares
    712  *
    713  * Returns name information about the given hostname and port number. If
    714  * successful, the 'hostent' is returned and the fourth argument will point to
    715  * memory we need to free after use. That memory *MUST* be freed with
    716  * Curl_freeaddrinfo(), nothing else.
    717  */
    718 struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
    719                                              const char *hostname,
    720                                              int port,
    721                                              int ip_version,
    722                                              int *waitp)
    723 {
    724   struct async_ares_ctx *ares = &data->state.async.ares;
    725   *waitp = 0; /* default to synchronous response */
    726 
    727   if(async_ares_init_lazy(data))
    728     return NULL;
    729 
    730   data->state.async.done = FALSE;   /* not done */
    731   data->state.async.dns = NULL;     /* clear */
    732   data->state.async.port = port;
    733   data->state.async.ip_version = ip_version;
    734   data->state.async.hostname = strdup(hostname);
    735   if(!data->state.async.hostname)
    736     return NULL;
    737 
    738   /* initial status - failed */
    739   ares->last_status = ARES_ENOTFOUND;
    740 
    741 #ifdef HAVE_CARES_GETADDRINFO
    742   {
    743     struct ares_addrinfo_hints hints;
    744     char service[12];
    745     int pf = PF_INET;
    746     memset(&hints, 0, sizeof(hints));
    747 #ifdef CURLRES_IPV6
    748     if((ip_version != CURL_IPRESOLVE_V4) &&
    749        Curl_ipv6works(data)) {
    750       /* The stack seems to be IPv6-enabled */
    751       if(ip_version == CURL_IPRESOLVE_V6)
    752         pf = PF_INET6;
    753       else
    754         pf = PF_UNSPEC;
    755     }
    756 #endif /* CURLRES_IPV6 */
    757     CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s",
    758                  (pf == PF_UNSPEC) ? "A+AAAA" :
    759                  ((pf == PF_INET) ? "A" : "AAAA"));
    760     hints.ai_family = pf;
    761     hints.ai_socktype =
    762       (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
    763       SOCK_STREAM : SOCK_DGRAM;
    764     /* Since the service is a numerical one, set the hint flags
    765      * accordingly to save a call to getservbyname in inside C-Ares
    766      */
    767     hints.ai_flags = ARES_AI_NUMERICSERV;
    768     msnprintf(service, sizeof(service), "%d", port);
    769     ares->num_pending = 1;
    770     ares_getaddrinfo(ares->channel, data->state.async.hostname,
    771                      service, &hints, async_ares_addrinfo_cb, data);
    772   }
    773 #else
    774 
    775 #ifdef HAVE_CARES_IPV6
    776   if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
    777     /* The stack seems to be IPv6-enabled */
    778     /* areschannel is already setup in the Curl_open() function */
    779     CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
    780     ares_gethostbyname(ares->channel, hostname, PF_INET,
    781                        async_ares_hostbyname_cb, data);
    782     CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA");
    783     ares->num_pending = 2;
    784     ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6,
    785                        async_ares_hostbyname_cb, data);
    786   }
    787   else
    788 #endif
    789   {
    790     /* areschannel is already setup in the Curl_open() function */
    791     CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
    792     ares->num_pending = 1;
    793     ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
    794                        async_ares_hostbyname_cb, data);
    795   }
    796 #endif
    797 #ifdef USE_HTTPSRR
    798   {
    799     CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR");
    800     memset(&ares->hinfo, 0, sizeof(ares->hinfo));
    801     ares->hinfo.port = -1;
    802     ares->num_pending++; /* one more */
    803     ares_query_dnsrec(ares->channel, data->state.async.hostname,
    804                       ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
    805                       async_ares_rr_done, data, NULL);
    806   }
    807 #endif
    808   *waitp = 1; /* expect asynchronous response */
    809 
    810   return NULL; /* no struct yet */
    811 }
    812 
    813 /* Set what DNS server are is to use. This is called in 2 situations:
    814  * 1. when the application does 'CURLOPT_DNS_SERVERS' and passing NULL
    815  *    means any previous set value should be unset. Which means
    816  *    we need to destroy and create the are channel anew, if there is one.
    817  * 2. When we lazy init the ares channel and NULL means that there
    818  *    are no preferences and we do not reset any existing channel. */
    819 static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
    820                                            bool reset_on_null)
    821 {
    822   struct async_ares_ctx *ares = &data->state.async.ares;
    823   CURLcode result = CURLE_NOT_BUILT_IN;
    824   const char *servers = data->set.str[STRING_DNS_SERVERS];
    825   int ares_result = ARES_SUCCESS;
    826 
    827 #if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV)
    828   if(getenv("CURL_DNS_SERVER"))
    829     servers = getenv("CURL_DNS_SERVER");
    830 #endif
    831 
    832   if(!servers) {
    833     if(reset_on_null) {
    834       Curl_async_destroy(data);
    835     }
    836     return CURLE_OK;
    837   }
    838 
    839 #ifdef HAVE_CARES_SERVERS_CSV
    840   /* if channel is not there, this is just a parameter check */
    841   if(ares->channel)
    842 #ifdef HAVE_CARES_PORTS_CSV
    843     ares_result = ares_set_servers_ports_csv(ares->channel, servers);
    844 #else
    845     ares_result = ares_set_servers_csv(ares->channel, servers);
    846 #endif
    847   switch(ares_result) {
    848   case ARES_SUCCESS:
    849     result = CURLE_OK;
    850     break;
    851   case ARES_ENOMEM:
    852     result = CURLE_OUT_OF_MEMORY;
    853     break;
    854   case ARES_ENOTINITIALIZED:
    855   case ARES_ENODATA:
    856   case ARES_EBADSTR:
    857   default:
    858     DEBUGF(infof(data, "bad servers set"));
    859     result = CURLE_BAD_FUNCTION_ARGUMENT;
    860     break;
    861   }
    862 #else /* too old c-ares version! */
    863   (void)data;
    864   (void)(ares_result);
    865 #endif
    866   return result;
    867 }
    868 
    869 CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
    870 {
    871   return async_ares_set_dns_servers(data, TRUE);
    872 }
    873 
    874 CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
    875 {
    876 #ifdef HAVE_CARES_LOCAL_DEV
    877   struct async_ares_ctx *ares = &data->state.async.ares;
    878   const char *interf = data->set.str[STRING_DNS_INTERFACE];
    879 
    880   if(!interf)
    881     interf = "";
    882 
    883   /* if channel is not there, this is just a parameter check */
    884   if(ares->channel)
    885     ares_set_local_dev(ares->channel, interf);
    886 
    887   return CURLE_OK;
    888 #else /* c-ares version too old! */
    889   (void)data;
    890   (void)interf;
    891   return CURLE_NOT_BUILT_IN;
    892 #endif
    893 }
    894 
    895 CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
    896 {
    897 #ifdef HAVE_CARES_SET_LOCAL
    898   struct async_ares_ctx *ares = &data->state.async.ares;
    899   struct in_addr a4;
    900   const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4];
    901 
    902   if((!local_ip4) || (local_ip4[0] == 0)) {
    903     a4.s_addr = 0; /* disabled: do not bind to a specific address */
    904   }
    905   else {
    906     if(curlx_inet_pton(AF_INET, local_ip4, &a4) != 1) {
    907       DEBUGF(infof(data, "bad DNS IPv4 address"));
    908       return CURLE_BAD_FUNCTION_ARGUMENT;
    909     }
    910   }
    911 
    912   /* if channel is not there yet, this is just a parameter check */
    913   if(ares->channel)
    914     ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
    915 
    916   return CURLE_OK;
    917 #else /* c-ares version too old! */
    918   (void)data;
    919   (void)local_ip4;
    920   return CURLE_NOT_BUILT_IN;
    921 #endif
    922 }
    923 
    924 CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
    925 {
    926 #if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
    927   struct async_ares_ctx *ares = &data->state.async.ares;
    928   unsigned char a6[INET6_ADDRSTRLEN];
    929   const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6];
    930 
    931   if((!local_ip6) || (local_ip6[0] == 0)) {
    932     /* disabled: do not bind to a specific address */
    933     memset(a6, 0, sizeof(a6));
    934   }
    935   else {
    936     if(curlx_inet_pton(AF_INET6, local_ip6, a6) != 1) {
    937       DEBUGF(infof(data, "bad DNS IPv6 address"));
    938       return CURLE_BAD_FUNCTION_ARGUMENT;
    939     }
    940   }
    941 
    942   /* if channel is not there, this is just a parameter check */
    943   if(ares->channel)
    944     ares_set_local_ip6(ares->channel, a6);
    945 
    946   return CURLE_OK;
    947 #else /* c-ares version too old! */
    948   (void)data;
    949   return CURLE_NOT_BUILT_IN;
    950 #endif
    951 }
    952 
    953 #endif /* CURLRES_ARES */