xref: /third_party/curl/lib/asyn-ares.c (revision 13498266)
113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci/***********************************************************************
2813498266Sopenharmony_ci * Only for ares-enabled builds
2913498266Sopenharmony_ci * And only for functions that fulfill the asynch resolver backend API
3013498266Sopenharmony_ci * as defined in asyn.h, nothing else belongs in this file!
3113498266Sopenharmony_ci **********************************************************************/
3213498266Sopenharmony_ci
3313498266Sopenharmony_ci#ifdef CURLRES_ARES
3413498266Sopenharmony_ci
3513498266Sopenharmony_ci#include <limits.h>
3613498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
3713498266Sopenharmony_ci#include <netinet/in.h>
3813498266Sopenharmony_ci#endif
3913498266Sopenharmony_ci#ifdef HAVE_NETDB_H
4013498266Sopenharmony_ci#include <netdb.h>
4113498266Sopenharmony_ci#endif
4213498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
4313498266Sopenharmony_ci#include <arpa/inet.h>
4413498266Sopenharmony_ci#endif
4513498266Sopenharmony_ci#ifdef __VMS
4613498266Sopenharmony_ci#include <in.h>
4713498266Sopenharmony_ci#include <inet.h>
4813498266Sopenharmony_ci#endif
4913498266Sopenharmony_ci
5013498266Sopenharmony_ci#include "urldata.h"
5113498266Sopenharmony_ci#include "sendf.h"
5213498266Sopenharmony_ci#include "hostip.h"
5313498266Sopenharmony_ci#include "hash.h"
5413498266Sopenharmony_ci#include "share.h"
5513498266Sopenharmony_ci#include "url.h"
5613498266Sopenharmony_ci#include "multiif.h"
5713498266Sopenharmony_ci#include "inet_pton.h"
5813498266Sopenharmony_ci#include "connect.h"
5913498266Sopenharmony_ci#include "select.h"
6013498266Sopenharmony_ci#include "progress.h"
6113498266Sopenharmony_ci#include "timediff.h"
6213498266Sopenharmony_ci
6313498266Sopenharmony_ci#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
6413498266Sopenharmony_ci  defined(_WIN32)
6513498266Sopenharmony_ci#  define CARES_STATICLIB
6613498266Sopenharmony_ci#endif
6713498266Sopenharmony_ci#include <ares.h>
6813498266Sopenharmony_ci#include <ares_version.h> /* really old c-ares didn't include this by
6913498266Sopenharmony_ci                             itself */
7013498266Sopenharmony_ci
7113498266Sopenharmony_ci#if ARES_VERSION >= 0x010500
7213498266Sopenharmony_ci/* c-ares 1.5.0 or later, the callback proto is modified */
7313498266Sopenharmony_ci#define HAVE_CARES_CALLBACK_TIMEOUTS 1
7413498266Sopenharmony_ci#endif
7513498266Sopenharmony_ci
7613498266Sopenharmony_ci#if ARES_VERSION >= 0x010601
7713498266Sopenharmony_ci/* IPv6 supported since 1.6.1 */
7813498266Sopenharmony_ci#define HAVE_CARES_IPV6 1
7913498266Sopenharmony_ci#endif
8013498266Sopenharmony_ci
8113498266Sopenharmony_ci#if ARES_VERSION >= 0x010704
8213498266Sopenharmony_ci#define HAVE_CARES_SERVERS_CSV 1
8313498266Sopenharmony_ci#define HAVE_CARES_LOCAL_DEV 1
8413498266Sopenharmony_ci#define HAVE_CARES_SET_LOCAL 1
8513498266Sopenharmony_ci#endif
8613498266Sopenharmony_ci
8713498266Sopenharmony_ci#if ARES_VERSION >= 0x010b00
8813498266Sopenharmony_ci#define HAVE_CARES_PORTS_CSV 1
8913498266Sopenharmony_ci#endif
9013498266Sopenharmony_ci
9113498266Sopenharmony_ci#if ARES_VERSION >= 0x011000
9213498266Sopenharmony_ci/* 1.16.0 or later has ares_getaddrinfo */
9313498266Sopenharmony_ci#define HAVE_CARES_GETADDRINFO 1
9413498266Sopenharmony_ci#endif
9513498266Sopenharmony_ci
9613498266Sopenharmony_ci/* The last 3 #include files should be in this order */
9713498266Sopenharmony_ci#include "curl_printf.h"
9813498266Sopenharmony_ci#include "curl_memory.h"
9913498266Sopenharmony_ci#include "memdebug.h"
10013498266Sopenharmony_ci
10113498266Sopenharmony_cistruct thread_data {
10213498266Sopenharmony_ci  int num_pending; /* number of outstanding c-ares requests */
10313498266Sopenharmony_ci  struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
10413498266Sopenharmony_ci                                    parts */
10513498266Sopenharmony_ci  int last_status;
10613498266Sopenharmony_ci#ifndef HAVE_CARES_GETADDRINFO
10713498266Sopenharmony_ci  struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
10813498266Sopenharmony_ci#endif
10913498266Sopenharmony_ci  char hostname[1];
11013498266Sopenharmony_ci};
11113498266Sopenharmony_ci
11213498266Sopenharmony_ci/* How long we are willing to wait for additional parallel responses after
11313498266Sopenharmony_ci   obtaining a "definitive" one. For old c-ares without getaddrinfo.
11413498266Sopenharmony_ci
11513498266Sopenharmony_ci   This is intended to equal the c-ares default timeout.  cURL always uses that
11613498266Sopenharmony_ci   default value.  Unfortunately, c-ares doesn't expose its default timeout in
11713498266Sopenharmony_ci   its API, but it is officially documented as 5 seconds.
11813498266Sopenharmony_ci
11913498266Sopenharmony_ci   See query_completed_cb() for an explanation of how this is used.
12013498266Sopenharmony_ci */
12113498266Sopenharmony_ci#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
12213498266Sopenharmony_ci
12313498266Sopenharmony_ci#define CARES_TIMEOUT_PER_ATTEMPT 2000
12413498266Sopenharmony_ci
12513498266Sopenharmony_ci/*
12613498266Sopenharmony_ci * Curl_resolver_global_init() - the generic low-level asynchronous name
12713498266Sopenharmony_ci * resolve API.  Called from curl_global_init() to initialize global resolver
12813498266Sopenharmony_ci * environment.  Initializes ares library.
12913498266Sopenharmony_ci */
13013498266Sopenharmony_ciint Curl_resolver_global_init(void)
13113498266Sopenharmony_ci{
13213498266Sopenharmony_ci#ifdef CARES_HAVE_ARES_LIBRARY_INIT
13313498266Sopenharmony_ci  if(ares_library_init(ARES_LIB_INIT_ALL)) {
13413498266Sopenharmony_ci    return CURLE_FAILED_INIT;
13513498266Sopenharmony_ci  }
13613498266Sopenharmony_ci#endif
13713498266Sopenharmony_ci  return CURLE_OK;
13813498266Sopenharmony_ci}
13913498266Sopenharmony_ci
14013498266Sopenharmony_ci/*
14113498266Sopenharmony_ci * Curl_resolver_global_cleanup()
14213498266Sopenharmony_ci *
14313498266Sopenharmony_ci * Called from curl_global_cleanup() to destroy global resolver environment.
14413498266Sopenharmony_ci * Deinitializes ares library.
14513498266Sopenharmony_ci */
14613498266Sopenharmony_civoid Curl_resolver_global_cleanup(void)
14713498266Sopenharmony_ci{
14813498266Sopenharmony_ci#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
14913498266Sopenharmony_ci  ares_library_cleanup();
15013498266Sopenharmony_ci#endif
15113498266Sopenharmony_ci}
15213498266Sopenharmony_ci
15313498266Sopenharmony_ci
15413498266Sopenharmony_cistatic void sock_state_cb(void *data, ares_socket_t socket_fd,
15513498266Sopenharmony_ci                          int readable, int writable)
15613498266Sopenharmony_ci{
15713498266Sopenharmony_ci  struct Curl_easy *easy = data;
15813498266Sopenharmony_ci  if(!readable && !writable) {
15913498266Sopenharmony_ci    DEBUGASSERT(easy);
16013498266Sopenharmony_ci    Curl_multi_closed(easy, socket_fd);
16113498266Sopenharmony_ci  }
16213498266Sopenharmony_ci}
16313498266Sopenharmony_ci
16413498266Sopenharmony_ci/*
16513498266Sopenharmony_ci * Curl_resolver_init()
16613498266Sopenharmony_ci *
16713498266Sopenharmony_ci * Called from curl_easy_init() -> Curl_open() to initialize resolver
16813498266Sopenharmony_ci * URL-state specific environment ('resolver' member of the UrlState
16913498266Sopenharmony_ci * structure).  Fills the passed pointer by the initialized ares_channel.
17013498266Sopenharmony_ci */
17113498266Sopenharmony_ciCURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
17213498266Sopenharmony_ci{
17313498266Sopenharmony_ci  int status;
17413498266Sopenharmony_ci  struct ares_options options;
17513498266Sopenharmony_ci  int optmask = ARES_OPT_SOCK_STATE_CB;
17613498266Sopenharmony_ci  static int ares_ver = 0;
17713498266Sopenharmony_ci  options.sock_state_cb = sock_state_cb;
17813498266Sopenharmony_ci  options.sock_state_cb_data = easy;
17913498266Sopenharmony_ci  if(ares_ver == 0)
18013498266Sopenharmony_ci    ares_version(&ares_ver);
18113498266Sopenharmony_ci
18213498266Sopenharmony_ci  if(ares_ver < 0x011400) { /* c-ares included similar change since 1.20.0 */
18313498266Sopenharmony_ci    options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
18413498266Sopenharmony_ci    optmask |= ARES_OPT_TIMEOUTMS;
18513498266Sopenharmony_ci  }
18613498266Sopenharmony_ci
18713498266Sopenharmony_ci  /*
18813498266Sopenharmony_ci     if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
18913498266Sopenharmony_ci
19013498266Sopenharmony_ci     if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
19113498266Sopenharmony_ci     to set the timeout value;
19213498266Sopenharmony_ci
19313498266Sopenharmony_ci     if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
19413498266Sopenharmony_ci     overwrite c-ares' timeout.
19513498266Sopenharmony_ci  */
19613498266Sopenharmony_ci
19713498266Sopenharmony_ci  status = ares_init_options((ares_channel*)resolver, &options, optmask);
19813498266Sopenharmony_ci  if(status != ARES_SUCCESS) {
19913498266Sopenharmony_ci    if(status == ARES_ENOMEM)
20013498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
20113498266Sopenharmony_ci    else
20213498266Sopenharmony_ci      return CURLE_FAILED_INIT;
20313498266Sopenharmony_ci  }
20413498266Sopenharmony_ci  return CURLE_OK;
20513498266Sopenharmony_ci  /* make sure that all other returns from this function should destroy the
20613498266Sopenharmony_ci     ares channel before returning error! */
20713498266Sopenharmony_ci}
20813498266Sopenharmony_ci
20913498266Sopenharmony_ci/*
21013498266Sopenharmony_ci * Curl_resolver_cleanup()
21113498266Sopenharmony_ci *
21213498266Sopenharmony_ci * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
21313498266Sopenharmony_ci * URL-state specific environment ('resolver' member of the UrlState
21413498266Sopenharmony_ci * structure).  Destroys the ares channel.
21513498266Sopenharmony_ci */
21613498266Sopenharmony_civoid Curl_resolver_cleanup(void *resolver)
21713498266Sopenharmony_ci{
21813498266Sopenharmony_ci  ares_destroy((ares_channel)resolver);
21913498266Sopenharmony_ci}
22013498266Sopenharmony_ci
22113498266Sopenharmony_ci/*
22213498266Sopenharmony_ci * Curl_resolver_duphandle()
22313498266Sopenharmony_ci *
22413498266Sopenharmony_ci * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
22513498266Sopenharmony_ci * environment ('resolver' member of the UrlState structure).  Duplicates the
22613498266Sopenharmony_ci * 'from' ares channel and passes the resulting channel to the 'to' pointer.
22713498266Sopenharmony_ci */
22813498266Sopenharmony_ciCURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
22913498266Sopenharmony_ci{
23013498266Sopenharmony_ci  (void)from;
23113498266Sopenharmony_ci  /*
23213498266Sopenharmony_ci   * it would be better to call ares_dup instead, but right now
23313498266Sopenharmony_ci   * it is not possible to set 'sock_state_cb_data' outside of
23413498266Sopenharmony_ci   * ares_init_options
23513498266Sopenharmony_ci   */
23613498266Sopenharmony_ci  return Curl_resolver_init(easy, to);
23713498266Sopenharmony_ci}
23813498266Sopenharmony_ci
23913498266Sopenharmony_cistatic void destroy_async_data(struct Curl_async *async);
24013498266Sopenharmony_ci
24113498266Sopenharmony_ci/*
24213498266Sopenharmony_ci * Cancel all possibly still on-going resolves for this connection.
24313498266Sopenharmony_ci */
24413498266Sopenharmony_civoid Curl_resolver_cancel(struct Curl_easy *data)
24513498266Sopenharmony_ci{
24613498266Sopenharmony_ci  DEBUGASSERT(data);
24713498266Sopenharmony_ci  if(data->state.async.resolver)
24813498266Sopenharmony_ci    ares_cancel((ares_channel)data->state.async.resolver);
24913498266Sopenharmony_ci  destroy_async_data(&data->state.async);
25013498266Sopenharmony_ci}
25113498266Sopenharmony_ci
25213498266Sopenharmony_ci/*
25313498266Sopenharmony_ci * We're equivalent to Curl_resolver_cancel() for the c-ares resolver.  We
25413498266Sopenharmony_ci * never block.
25513498266Sopenharmony_ci */
25613498266Sopenharmony_civoid Curl_resolver_kill(struct Curl_easy *data)
25713498266Sopenharmony_ci{
25813498266Sopenharmony_ci  /* We don't need to check the resolver state because we can be called safely
25913498266Sopenharmony_ci     at any time and we always do the same thing. */
26013498266Sopenharmony_ci  Curl_resolver_cancel(data);
26113498266Sopenharmony_ci}
26213498266Sopenharmony_ci
26313498266Sopenharmony_ci/*
26413498266Sopenharmony_ci * destroy_async_data() cleans up async resolver data.
26513498266Sopenharmony_ci */
26613498266Sopenharmony_cistatic void destroy_async_data(struct Curl_async *async)
26713498266Sopenharmony_ci{
26813498266Sopenharmony_ci  if(async->tdata) {
26913498266Sopenharmony_ci    struct thread_data *res = async->tdata;
27013498266Sopenharmony_ci    if(res) {
27113498266Sopenharmony_ci      if(res->temp_ai) {
27213498266Sopenharmony_ci        Curl_freeaddrinfo(res->temp_ai);
27313498266Sopenharmony_ci        res->temp_ai = NULL;
27413498266Sopenharmony_ci      }
27513498266Sopenharmony_ci      free(res);
27613498266Sopenharmony_ci    }
27713498266Sopenharmony_ci    async->tdata = NULL;
27813498266Sopenharmony_ci  }
27913498266Sopenharmony_ci}
28013498266Sopenharmony_ci
28113498266Sopenharmony_ci/*
28213498266Sopenharmony_ci * Curl_resolver_getsock() is called when someone from the outside world
28313498266Sopenharmony_ci * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
28413498266Sopenharmony_ci * with ares. The caller must make sure that this function is only called when
28513498266Sopenharmony_ci * we have a working ares channel.
28613498266Sopenharmony_ci *
28713498266Sopenharmony_ci * Returns: sockets-in-use-bitmap
28813498266Sopenharmony_ci */
28913498266Sopenharmony_ci
29013498266Sopenharmony_ciint Curl_resolver_getsock(struct Curl_easy *data,
29113498266Sopenharmony_ci                          curl_socket_t *socks)
29213498266Sopenharmony_ci{
29313498266Sopenharmony_ci  struct timeval maxtime;
29413498266Sopenharmony_ci  struct timeval timebuf;
29513498266Sopenharmony_ci  struct timeval *timeout;
29613498266Sopenharmony_ci  long milli;
29713498266Sopenharmony_ci  int max = ares_getsock((ares_channel)data->state.async.resolver,
29813498266Sopenharmony_ci                         (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
29913498266Sopenharmony_ci
30013498266Sopenharmony_ci  maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
30113498266Sopenharmony_ci  maxtime.tv_usec = 0;
30213498266Sopenharmony_ci
30313498266Sopenharmony_ci  timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
30413498266Sopenharmony_ci                         &timebuf);
30513498266Sopenharmony_ci  milli = (long)curlx_tvtoms(timeout);
30613498266Sopenharmony_ci  if(milli == 0)
30713498266Sopenharmony_ci    milli += 10;
30813498266Sopenharmony_ci  Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
30913498266Sopenharmony_ci
31013498266Sopenharmony_ci  return max;
31113498266Sopenharmony_ci}
31213498266Sopenharmony_ci
31313498266Sopenharmony_ci/*
31413498266Sopenharmony_ci * waitperform()
31513498266Sopenharmony_ci *
31613498266Sopenharmony_ci * 1) Ask ares what sockets it currently plays with, then
31713498266Sopenharmony_ci * 2) wait for the timeout period to check for action on ares' sockets.
31813498266Sopenharmony_ci * 3) tell ares to act on all the sockets marked as "with action"
31913498266Sopenharmony_ci *
32013498266Sopenharmony_ci * return number of sockets it worked on, or -1 on error
32113498266Sopenharmony_ci */
32213498266Sopenharmony_ci
32313498266Sopenharmony_cistatic int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
32413498266Sopenharmony_ci{
32513498266Sopenharmony_ci  int nfds;
32613498266Sopenharmony_ci  int bitmask;
32713498266Sopenharmony_ci  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
32813498266Sopenharmony_ci  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
32913498266Sopenharmony_ci  int i;
33013498266Sopenharmony_ci  int num = 0;
33113498266Sopenharmony_ci
33213498266Sopenharmony_ci  bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
33313498266Sopenharmony_ci                         ARES_GETSOCK_MAXNUM);
33413498266Sopenharmony_ci
33513498266Sopenharmony_ci  for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
33613498266Sopenharmony_ci    pfd[i].events = 0;
33713498266Sopenharmony_ci    pfd[i].revents = 0;
33813498266Sopenharmony_ci    if(ARES_GETSOCK_READABLE(bitmask, i)) {
33913498266Sopenharmony_ci      pfd[i].fd = socks[i];
34013498266Sopenharmony_ci      pfd[i].events |= POLLRDNORM|POLLIN;
34113498266Sopenharmony_ci    }
34213498266Sopenharmony_ci    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
34313498266Sopenharmony_ci      pfd[i].fd = socks[i];
34413498266Sopenharmony_ci      pfd[i].events |= POLLWRNORM|POLLOUT;
34513498266Sopenharmony_ci    }
34613498266Sopenharmony_ci    if(pfd[i].events)
34713498266Sopenharmony_ci      num++;
34813498266Sopenharmony_ci    else
34913498266Sopenharmony_ci      break;
35013498266Sopenharmony_ci  }
35113498266Sopenharmony_ci
35213498266Sopenharmony_ci  if(num) {
35313498266Sopenharmony_ci    nfds = Curl_poll(pfd, num, timeout_ms);
35413498266Sopenharmony_ci    if(nfds < 0)
35513498266Sopenharmony_ci      return -1;
35613498266Sopenharmony_ci  }
35713498266Sopenharmony_ci  else
35813498266Sopenharmony_ci    nfds = 0;
35913498266Sopenharmony_ci
36013498266Sopenharmony_ci  if(!nfds)
36113498266Sopenharmony_ci    /* Call ares_process() unconditionally here, even if we simply timed out
36213498266Sopenharmony_ci       above, as otherwise the ares name resolve won't timeout! */
36313498266Sopenharmony_ci    ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
36413498266Sopenharmony_ci                    ARES_SOCKET_BAD);
36513498266Sopenharmony_ci  else {
36613498266Sopenharmony_ci    /* move through the descriptors and ask for processing on them */
36713498266Sopenharmony_ci    for(i = 0; i < num; i++)
36813498266Sopenharmony_ci      ares_process_fd((ares_channel)data->state.async.resolver,
36913498266Sopenharmony_ci                      (pfd[i].revents & (POLLRDNORM|POLLIN))?
37013498266Sopenharmony_ci                      pfd[i].fd:ARES_SOCKET_BAD,
37113498266Sopenharmony_ci                      (pfd[i].revents & (POLLWRNORM|POLLOUT))?
37213498266Sopenharmony_ci                      pfd[i].fd:ARES_SOCKET_BAD);
37313498266Sopenharmony_ci  }
37413498266Sopenharmony_ci  return nfds;
37513498266Sopenharmony_ci}
37613498266Sopenharmony_ci
37713498266Sopenharmony_ci/*
37813498266Sopenharmony_ci * Curl_resolver_is_resolved() is called repeatedly to check if a previous
37913498266Sopenharmony_ci * name resolve request has completed. It should also make sure to time-out if
38013498266Sopenharmony_ci * the operation seems to take too long.
38113498266Sopenharmony_ci *
38213498266Sopenharmony_ci * Returns normal CURLcode errors.
38313498266Sopenharmony_ci */
38413498266Sopenharmony_ciCURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
38513498266Sopenharmony_ci                                   struct Curl_dns_entry **dns)
38613498266Sopenharmony_ci{
38713498266Sopenharmony_ci  struct thread_data *res = data->state.async.tdata;
38813498266Sopenharmony_ci  CURLcode result = CURLE_OK;
38913498266Sopenharmony_ci
39013498266Sopenharmony_ci  DEBUGASSERT(dns);
39113498266Sopenharmony_ci  *dns = NULL;
39213498266Sopenharmony_ci
39313498266Sopenharmony_ci  if(waitperform(data, 0) < 0)
39413498266Sopenharmony_ci    return CURLE_UNRECOVERABLE_POLL;
39513498266Sopenharmony_ci
39613498266Sopenharmony_ci#ifndef HAVE_CARES_GETADDRINFO
39713498266Sopenharmony_ci  /* Now that we've checked for any last minute results above, see if there are
39813498266Sopenharmony_ci     any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
39913498266Sopenharmony_ci     expires. */
40013498266Sopenharmony_ci  if(res
40113498266Sopenharmony_ci     && res->num_pending
40213498266Sopenharmony_ci     /* This is only set to non-zero if the timer was started. */
40313498266Sopenharmony_ci     && (res->happy_eyeballs_dns_time.tv_sec
40413498266Sopenharmony_ci         || res->happy_eyeballs_dns_time.tv_usec)
40513498266Sopenharmony_ci     && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
40613498266Sopenharmony_ci         >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
40713498266Sopenharmony_ci    /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
40813498266Sopenharmony_ci       running. */
40913498266Sopenharmony_ci    memset(
41013498266Sopenharmony_ci      &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
41113498266Sopenharmony_ci
41213498266Sopenharmony_ci    /* Cancel the raw c-ares request, which will fire query_completed_cb() with
41313498266Sopenharmony_ci       ARES_ECANCELLED synchronously for all pending responses.  This will
41413498266Sopenharmony_ci       leave us with res->num_pending == 0, which is perfect for the next
41513498266Sopenharmony_ci       block. */
41613498266Sopenharmony_ci    ares_cancel((ares_channel)data->state.async.resolver);
41713498266Sopenharmony_ci    DEBUGASSERT(res->num_pending == 0);
41813498266Sopenharmony_ci  }
41913498266Sopenharmony_ci#endif
42013498266Sopenharmony_ci
42113498266Sopenharmony_ci  if(res && !res->num_pending) {
42213498266Sopenharmony_ci    (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
42313498266Sopenharmony_ci    /* temp_ai ownership is moved to the connection, so we need not free-up
42413498266Sopenharmony_ci       them */
42513498266Sopenharmony_ci    res->temp_ai = NULL;
42613498266Sopenharmony_ci
42713498266Sopenharmony_ci    if(!data->state.async.dns)
42813498266Sopenharmony_ci      result = Curl_resolver_error(data);
42913498266Sopenharmony_ci    else
43013498266Sopenharmony_ci      *dns = data->state.async.dns;
43113498266Sopenharmony_ci
43213498266Sopenharmony_ci    destroy_async_data(&data->state.async);
43313498266Sopenharmony_ci  }
43413498266Sopenharmony_ci
43513498266Sopenharmony_ci  return result;
43613498266Sopenharmony_ci}
43713498266Sopenharmony_ci
43813498266Sopenharmony_ci/*
43913498266Sopenharmony_ci * Curl_resolver_wait_resolv()
44013498266Sopenharmony_ci *
44113498266Sopenharmony_ci * Waits for a resolve to finish. This function should be avoided since using
44213498266Sopenharmony_ci * this risk getting the multi interface to "hang".
44313498266Sopenharmony_ci *
44413498266Sopenharmony_ci * 'entry' MUST be non-NULL.
44513498266Sopenharmony_ci *
44613498266Sopenharmony_ci * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
44713498266Sopenharmony_ci * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
44813498266Sopenharmony_ci */
44913498266Sopenharmony_ciCURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
45013498266Sopenharmony_ci                                   struct Curl_dns_entry **entry)
45113498266Sopenharmony_ci{
45213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
45313498266Sopenharmony_ci  timediff_t timeout;
45413498266Sopenharmony_ci  struct curltime now = Curl_now();
45513498266Sopenharmony_ci
45613498266Sopenharmony_ci  DEBUGASSERT(entry);
45713498266Sopenharmony_ci  *entry = NULL; /* clear on entry */
45813498266Sopenharmony_ci
45913498266Sopenharmony_ci  timeout = Curl_timeleft(data, &now, TRUE);
46013498266Sopenharmony_ci  if(timeout < 0) {
46113498266Sopenharmony_ci    /* already expired! */
46213498266Sopenharmony_ci    connclose(data->conn, "Timed out before name resolve started");
46313498266Sopenharmony_ci    return CURLE_OPERATION_TIMEDOUT;
46413498266Sopenharmony_ci  }
46513498266Sopenharmony_ci  if(!timeout)
46613498266Sopenharmony_ci    timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
46713498266Sopenharmony_ci
46813498266Sopenharmony_ci  /* Wait for the name resolve query to complete. */
46913498266Sopenharmony_ci  while(!result) {
47013498266Sopenharmony_ci    struct timeval *tvp, tv, store;
47113498266Sopenharmony_ci    int itimeout;
47213498266Sopenharmony_ci    timediff_t timeout_ms;
47313498266Sopenharmony_ci
47413498266Sopenharmony_ci#if TIMEDIFF_T_MAX > INT_MAX
47513498266Sopenharmony_ci    itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
47613498266Sopenharmony_ci#else
47713498266Sopenharmony_ci    itimeout = (int)timeout;
47813498266Sopenharmony_ci#endif
47913498266Sopenharmony_ci
48013498266Sopenharmony_ci    store.tv_sec = itimeout/1000;
48113498266Sopenharmony_ci    store.tv_usec = (itimeout%1000)*1000;
48213498266Sopenharmony_ci
48313498266Sopenharmony_ci    tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
48413498266Sopenharmony_ci
48513498266Sopenharmony_ci    /* use the timeout period ares returned to us above if less than one
48613498266Sopenharmony_ci       second is left, otherwise just use 1000ms to make sure the progress
48713498266Sopenharmony_ci       callback gets called frequent enough */
48813498266Sopenharmony_ci    if(!tvp->tv_sec)
48913498266Sopenharmony_ci      timeout_ms = (timediff_t)(tvp->tv_usec/1000);
49013498266Sopenharmony_ci    else
49113498266Sopenharmony_ci      timeout_ms = 1000;
49213498266Sopenharmony_ci
49313498266Sopenharmony_ci    if(waitperform(data, timeout_ms) < 0)
49413498266Sopenharmony_ci      return CURLE_UNRECOVERABLE_POLL;
49513498266Sopenharmony_ci    result = Curl_resolver_is_resolved(data, entry);
49613498266Sopenharmony_ci
49713498266Sopenharmony_ci    if(result || data->state.async.done)
49813498266Sopenharmony_ci      break;
49913498266Sopenharmony_ci
50013498266Sopenharmony_ci    if(Curl_pgrsUpdate(data))
50113498266Sopenharmony_ci      result = CURLE_ABORTED_BY_CALLBACK;
50213498266Sopenharmony_ci    else {
50313498266Sopenharmony_ci      struct curltime now2 = Curl_now();
50413498266Sopenharmony_ci      timediff_t timediff = Curl_timediff(now2, now); /* spent time */
50513498266Sopenharmony_ci      if(timediff <= 0)
50613498266Sopenharmony_ci        timeout -= 1; /* always deduct at least 1 */
50713498266Sopenharmony_ci      else if(timediff > timeout)
50813498266Sopenharmony_ci        timeout = -1;
50913498266Sopenharmony_ci      else
51013498266Sopenharmony_ci        timeout -= timediff;
51113498266Sopenharmony_ci      now = now2; /* for next loop */
51213498266Sopenharmony_ci    }
51313498266Sopenharmony_ci    if(timeout < 0)
51413498266Sopenharmony_ci      result = CURLE_OPERATION_TIMEDOUT;
51513498266Sopenharmony_ci  }
51613498266Sopenharmony_ci  if(result)
51713498266Sopenharmony_ci    /* failure, so we cancel the ares operation */
51813498266Sopenharmony_ci    ares_cancel((ares_channel)data->state.async.resolver);
51913498266Sopenharmony_ci
52013498266Sopenharmony_ci  /* Operation complete, if the lookup was successful we now have the entry
52113498266Sopenharmony_ci     in the cache. */
52213498266Sopenharmony_ci  if(entry)
52313498266Sopenharmony_ci    *entry = data->state.async.dns;
52413498266Sopenharmony_ci
52513498266Sopenharmony_ci  if(result)
52613498266Sopenharmony_ci    /* close the connection, since we can't return failure here without
52713498266Sopenharmony_ci       cleaning up this connection properly. */
52813498266Sopenharmony_ci    connclose(data->conn, "c-ares resolve failed");
52913498266Sopenharmony_ci
53013498266Sopenharmony_ci  return result;
53113498266Sopenharmony_ci}
53213498266Sopenharmony_ci
53313498266Sopenharmony_ci#ifndef HAVE_CARES_GETADDRINFO
53413498266Sopenharmony_ci
53513498266Sopenharmony_ci/* Connects results to the list */
53613498266Sopenharmony_cistatic void compound_results(struct thread_data *res,
53713498266Sopenharmony_ci                             struct Curl_addrinfo *ai)
53813498266Sopenharmony_ci{
53913498266Sopenharmony_ci  if(!ai)
54013498266Sopenharmony_ci    return;
54113498266Sopenharmony_ci
54213498266Sopenharmony_ci#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
54313498266Sopenharmony_ci  if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
54413498266Sopenharmony_ci    /* We have results already, put the new IPv6 entries at the head of the
54513498266Sopenharmony_ci       list. */
54613498266Sopenharmony_ci    struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
54713498266Sopenharmony_ci
54813498266Sopenharmony_ci    while(temp_ai_tail->ai_next)
54913498266Sopenharmony_ci      temp_ai_tail = temp_ai_tail->ai_next;
55013498266Sopenharmony_ci
55113498266Sopenharmony_ci    temp_ai_tail->ai_next = ai;
55213498266Sopenharmony_ci  }
55313498266Sopenharmony_ci  else
55413498266Sopenharmony_ci#endif /* CURLRES_IPV6 */
55513498266Sopenharmony_ci  {
55613498266Sopenharmony_ci    /* Add the new results to the list of old results. */
55713498266Sopenharmony_ci    struct Curl_addrinfo *ai_tail = ai;
55813498266Sopenharmony_ci    while(ai_tail->ai_next)
55913498266Sopenharmony_ci      ai_tail = ai_tail->ai_next;
56013498266Sopenharmony_ci
56113498266Sopenharmony_ci    ai_tail->ai_next = res->temp_ai;
56213498266Sopenharmony_ci    res->temp_ai = ai;
56313498266Sopenharmony_ci  }
56413498266Sopenharmony_ci}
56513498266Sopenharmony_ci
56613498266Sopenharmony_ci/*
56713498266Sopenharmony_ci * ares_query_completed_cb() is the callback that ares will call when
56813498266Sopenharmony_ci * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
56913498266Sopenharmony_ci * when using ares, is completed either successfully or with failure.
57013498266Sopenharmony_ci */
57113498266Sopenharmony_cistatic void query_completed_cb(void *arg,  /* (struct connectdata *) */
57213498266Sopenharmony_ci                               int status,
57313498266Sopenharmony_ci#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
57413498266Sopenharmony_ci                               int timeouts,
57513498266Sopenharmony_ci#endif
57613498266Sopenharmony_ci                               struct hostent *hostent)
57713498266Sopenharmony_ci{
57813498266Sopenharmony_ci  struct Curl_easy *data = (struct Curl_easy *)arg;
57913498266Sopenharmony_ci  struct thread_data *res;
58013498266Sopenharmony_ci
58113498266Sopenharmony_ci#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
58213498266Sopenharmony_ci  (void)timeouts; /* ignored */
58313498266Sopenharmony_ci#endif
58413498266Sopenharmony_ci
58513498266Sopenharmony_ci  if(ARES_EDESTRUCTION == status)
58613498266Sopenharmony_ci    /* when this ares handle is getting destroyed, the 'arg' pointer may not
58713498266Sopenharmony_ci       be valid so only defer it when we know the 'status' says its fine! */
58813498266Sopenharmony_ci    return;
58913498266Sopenharmony_ci
59013498266Sopenharmony_ci  res = data->state.async.tdata;
59113498266Sopenharmony_ci  if(res) {
59213498266Sopenharmony_ci    res->num_pending--;
59313498266Sopenharmony_ci
59413498266Sopenharmony_ci    if(CURL_ASYNC_SUCCESS == status) {
59513498266Sopenharmony_ci      struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
59613498266Sopenharmony_ci      if(ai) {
59713498266Sopenharmony_ci        compound_results(res, ai);
59813498266Sopenharmony_ci      }
59913498266Sopenharmony_ci    }
60013498266Sopenharmony_ci    /* A successful result overwrites any previous error */
60113498266Sopenharmony_ci    if(res->last_status != ARES_SUCCESS)
60213498266Sopenharmony_ci      res->last_status = status;
60313498266Sopenharmony_ci
60413498266Sopenharmony_ci    /* If there are responses still pending, we presume they must be the
60513498266Sopenharmony_ci       complementary IPv4 or IPv6 lookups that we started in parallel in
60613498266Sopenharmony_ci       Curl_resolver_getaddrinfo() (for Happy Eyeballs).  If we've got a
60713498266Sopenharmony_ci       "definitive" response from one of a set of parallel queries, we need to
60813498266Sopenharmony_ci       think about how long we're willing to wait for more responses. */
60913498266Sopenharmony_ci    if(res->num_pending
61013498266Sopenharmony_ci       /* Only these c-ares status values count as "definitive" for these
61113498266Sopenharmony_ci          purposes.  For example, ARES_ENODATA is what we expect when there is
61213498266Sopenharmony_ci          no IPv6 entry for a domain name, and that's not a reason to get more
61313498266Sopenharmony_ci          aggressive in our timeouts for the other response.  Other errors are
61413498266Sopenharmony_ci          either a result of bad input (which should affect all parallel
61513498266Sopenharmony_ci          requests), local or network conditions, non-definitive server
61613498266Sopenharmony_ci          responses, or us cancelling the request. */
61713498266Sopenharmony_ci       && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
61813498266Sopenharmony_ci      /* Right now, there can only be up to two parallel queries, so don't
61913498266Sopenharmony_ci         bother handling any other cases. */
62013498266Sopenharmony_ci      DEBUGASSERT(res->num_pending == 1);
62113498266Sopenharmony_ci
62213498266Sopenharmony_ci      /* It's possible that one of these parallel queries could succeed
62313498266Sopenharmony_ci         quickly, but the other could always fail or timeout (when we're
62413498266Sopenharmony_ci         talking to a pool of DNS servers that can only successfully resolve
62513498266Sopenharmony_ci         IPv4 address, for example).
62613498266Sopenharmony_ci
62713498266Sopenharmony_ci         It's also possible that the other request could always just take
62813498266Sopenharmony_ci         longer because it needs more time or only the second DNS server can
62913498266Sopenharmony_ci         fulfill it successfully.  But, to align with the philosophy of Happy
63013498266Sopenharmony_ci         Eyeballs, we don't want to wait _too_ long or users will think
63113498266Sopenharmony_ci         requests are slow when IPv6 lookups don't actually work (but IPv4 ones
63213498266Sopenharmony_ci         do).
63313498266Sopenharmony_ci
63413498266Sopenharmony_ci         So, now that we have a usable answer (some IPv4 addresses, some IPv6
63513498266Sopenharmony_ci         addresses, or "no such domain"), we start a timeout for the remaining
63613498266Sopenharmony_ci         pending responses.  Even though it is typical that this resolved
63713498266Sopenharmony_ci         request came back quickly, that needn't be the case.  It might be that
63813498266Sopenharmony_ci         this completing request didn't get a result from the first DNS server
63913498266Sopenharmony_ci         or even the first round of the whole DNS server pool.  So it could
64013498266Sopenharmony_ci         already be quite some time after we issued the DNS queries in the
64113498266Sopenharmony_ci         first place.  Without modifying c-ares, we can't know exactly where in
64213498266Sopenharmony_ci         its retry cycle we are.  We could guess based on how much time has
64313498266Sopenharmony_ci         gone by, but it doesn't really matter.  Happy Eyeballs tells us that,
64413498266Sopenharmony_ci         given usable information in hand, we simply don't want to wait "too
64513498266Sopenharmony_ci         much longer" after we get a result.
64613498266Sopenharmony_ci
64713498266Sopenharmony_ci         We simply wait an additional amount of time equal to the default
64813498266Sopenharmony_ci         c-ares query timeout.  That is enough time for a typical parallel
64913498266Sopenharmony_ci         response to arrive without being "too long".  Even on a network
65013498266Sopenharmony_ci         where one of the two types of queries is failing or timing out
65113498266Sopenharmony_ci         constantly, this will usually mean we wait a total of the default
65213498266Sopenharmony_ci         c-ares timeout (5 seconds) plus the round trip time for the successful
65313498266Sopenharmony_ci         request, which seems bearable.  The downside is that c-ares might race
65413498266Sopenharmony_ci         with us to issue one more retry just before we give up, but it seems
65513498266Sopenharmony_ci         better to "waste" that request instead of trying to guess the perfect
65613498266Sopenharmony_ci         timeout to prevent it.  After all, we don't even know where in the
65713498266Sopenharmony_ci         c-ares retry cycle each request is.
65813498266Sopenharmony_ci      */
65913498266Sopenharmony_ci      res->happy_eyeballs_dns_time = Curl_now();
66013498266Sopenharmony_ci      Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
66113498266Sopenharmony_ci                  EXPIRE_HAPPY_EYEBALLS_DNS);
66213498266Sopenharmony_ci    }
66313498266Sopenharmony_ci  }
66413498266Sopenharmony_ci}
66513498266Sopenharmony_ci#else
66613498266Sopenharmony_ci/* c-ares 1.16.0 or later */
66713498266Sopenharmony_ci
66813498266Sopenharmony_ci/*
66913498266Sopenharmony_ci * ares2addr() converts an address list provided by c-ares to an internal
67013498266Sopenharmony_ci * libcurl compatible list
67113498266Sopenharmony_ci */
67213498266Sopenharmony_cistatic struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
67313498266Sopenharmony_ci{
67413498266Sopenharmony_ci  /* traverse the ares_addrinfo_node list */
67513498266Sopenharmony_ci  struct ares_addrinfo_node *ai;
67613498266Sopenharmony_ci  struct Curl_addrinfo *cafirst = NULL;
67713498266Sopenharmony_ci  struct Curl_addrinfo *calast = NULL;
67813498266Sopenharmony_ci  int error = 0;
67913498266Sopenharmony_ci
68013498266Sopenharmony_ci  for(ai = node; ai != NULL; ai = ai->ai_next) {
68113498266Sopenharmony_ci    size_t ss_size;
68213498266Sopenharmony_ci    struct Curl_addrinfo *ca;
68313498266Sopenharmony_ci    /* ignore elements with unsupported address family, */
68413498266Sopenharmony_ci    /* settle family-specific sockaddr structure size.  */
68513498266Sopenharmony_ci    if(ai->ai_family == AF_INET)
68613498266Sopenharmony_ci      ss_size = sizeof(struct sockaddr_in);
68713498266Sopenharmony_ci#ifdef ENABLE_IPV6
68813498266Sopenharmony_ci    else if(ai->ai_family == AF_INET6)
68913498266Sopenharmony_ci      ss_size = sizeof(struct sockaddr_in6);
69013498266Sopenharmony_ci#endif
69113498266Sopenharmony_ci    else
69213498266Sopenharmony_ci      continue;
69313498266Sopenharmony_ci
69413498266Sopenharmony_ci    /* ignore elements without required address info */
69513498266Sopenharmony_ci    if(!ai->ai_addr || !(ai->ai_addrlen > 0))
69613498266Sopenharmony_ci      continue;
69713498266Sopenharmony_ci
69813498266Sopenharmony_ci    /* ignore elements with bogus address size */
69913498266Sopenharmony_ci    if((size_t)ai->ai_addrlen < ss_size)
70013498266Sopenharmony_ci      continue;
70113498266Sopenharmony_ci
70213498266Sopenharmony_ci    ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
70313498266Sopenharmony_ci    if(!ca) {
70413498266Sopenharmony_ci      error = EAI_MEMORY;
70513498266Sopenharmony_ci      break;
70613498266Sopenharmony_ci    }
70713498266Sopenharmony_ci
70813498266Sopenharmony_ci    /* copy each structure member individually, member ordering, */
70913498266Sopenharmony_ci    /* size, or padding might be different for each platform.    */
71013498266Sopenharmony_ci
71113498266Sopenharmony_ci    ca->ai_flags     = ai->ai_flags;
71213498266Sopenharmony_ci    ca->ai_family    = ai->ai_family;
71313498266Sopenharmony_ci    ca->ai_socktype  = ai->ai_socktype;
71413498266Sopenharmony_ci    ca->ai_protocol  = ai->ai_protocol;
71513498266Sopenharmony_ci    ca->ai_addrlen   = (curl_socklen_t)ss_size;
71613498266Sopenharmony_ci    ca->ai_addr      = NULL;
71713498266Sopenharmony_ci    ca->ai_canonname = NULL;
71813498266Sopenharmony_ci    ca->ai_next      = NULL;
71913498266Sopenharmony_ci
72013498266Sopenharmony_ci    ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
72113498266Sopenharmony_ci    memcpy(ca->ai_addr, ai->ai_addr, ss_size);
72213498266Sopenharmony_ci
72313498266Sopenharmony_ci    /* if the return list is empty, this becomes the first element */
72413498266Sopenharmony_ci    if(!cafirst)
72513498266Sopenharmony_ci      cafirst = ca;
72613498266Sopenharmony_ci
72713498266Sopenharmony_ci    /* add this element last in the return list */
72813498266Sopenharmony_ci    if(calast)
72913498266Sopenharmony_ci      calast->ai_next = ca;
73013498266Sopenharmony_ci    calast = ca;
73113498266Sopenharmony_ci  }
73213498266Sopenharmony_ci
73313498266Sopenharmony_ci  /* if we failed, destroy the Curl_addrinfo list */
73413498266Sopenharmony_ci  if(error) {
73513498266Sopenharmony_ci    Curl_freeaddrinfo(cafirst);
73613498266Sopenharmony_ci    cafirst = NULL;
73713498266Sopenharmony_ci  }
73813498266Sopenharmony_ci
73913498266Sopenharmony_ci  return cafirst;
74013498266Sopenharmony_ci}
74113498266Sopenharmony_ci
74213498266Sopenharmony_cistatic void addrinfo_cb(void *arg, int status, int timeouts,
74313498266Sopenharmony_ci                        struct ares_addrinfo *result)
74413498266Sopenharmony_ci{
74513498266Sopenharmony_ci  struct Curl_easy *data = (struct Curl_easy *)arg;
74613498266Sopenharmony_ci  struct thread_data *res = data->state.async.tdata;
74713498266Sopenharmony_ci  (void)timeouts;
74813498266Sopenharmony_ci  if(ARES_SUCCESS == status) {
74913498266Sopenharmony_ci    res->temp_ai = ares2addr(result->nodes);
75013498266Sopenharmony_ci    res->last_status = CURL_ASYNC_SUCCESS;
75113498266Sopenharmony_ci    ares_freeaddrinfo(result);
75213498266Sopenharmony_ci  }
75313498266Sopenharmony_ci  res->num_pending--;
75413498266Sopenharmony_ci}
75513498266Sopenharmony_ci
75613498266Sopenharmony_ci#endif
75713498266Sopenharmony_ci/*
75813498266Sopenharmony_ci * Curl_resolver_getaddrinfo() - when using ares
75913498266Sopenharmony_ci *
76013498266Sopenharmony_ci * Returns name information about the given hostname and port number. If
76113498266Sopenharmony_ci * successful, the 'hostent' is returned and the fourth argument will point to
76213498266Sopenharmony_ci * memory we need to free after use. That memory *MUST* be freed with
76313498266Sopenharmony_ci * Curl_freeaddrinfo(), nothing else.
76413498266Sopenharmony_ci */
76513498266Sopenharmony_cistruct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
76613498266Sopenharmony_ci                                                const char *hostname,
76713498266Sopenharmony_ci                                                int port,
76813498266Sopenharmony_ci                                                int *waitp)
76913498266Sopenharmony_ci{
77013498266Sopenharmony_ci  struct thread_data *res = NULL;
77113498266Sopenharmony_ci  size_t namelen = strlen(hostname);
77213498266Sopenharmony_ci  *waitp = 0; /* default to synchronous response */
77313498266Sopenharmony_ci
77413498266Sopenharmony_ci  res = calloc(1, sizeof(struct thread_data) + namelen);
77513498266Sopenharmony_ci  if(res) {
77613498266Sopenharmony_ci    strcpy(res->hostname, hostname);
77713498266Sopenharmony_ci    data->state.async.hostname = res->hostname;
77813498266Sopenharmony_ci    data->state.async.port = port;
77913498266Sopenharmony_ci    data->state.async.done = FALSE;   /* not done */
78013498266Sopenharmony_ci    data->state.async.status = 0;     /* clear */
78113498266Sopenharmony_ci    data->state.async.dns = NULL;     /* clear */
78213498266Sopenharmony_ci    data->state.async.tdata = res;
78313498266Sopenharmony_ci
78413498266Sopenharmony_ci    /* initial status - failed */
78513498266Sopenharmony_ci    res->last_status = ARES_ENOTFOUND;
78613498266Sopenharmony_ci
78713498266Sopenharmony_ci#ifdef HAVE_CARES_GETADDRINFO
78813498266Sopenharmony_ci    {
78913498266Sopenharmony_ci      struct ares_addrinfo_hints hints;
79013498266Sopenharmony_ci      char service[12];
79113498266Sopenharmony_ci      int pf = PF_INET;
79213498266Sopenharmony_ci      memset(&hints, 0, sizeof(hints));
79313498266Sopenharmony_ci#ifdef CURLRES_IPV6
79413498266Sopenharmony_ci      if((data->conn->ip_version != CURL_IPRESOLVE_V4) &&
79513498266Sopenharmony_ci         Curl_ipv6works(data)) {
79613498266Sopenharmony_ci        /* The stack seems to be IPv6-enabled */
79713498266Sopenharmony_ci        if(data->conn->ip_version == CURL_IPRESOLVE_V6)
79813498266Sopenharmony_ci          pf = PF_INET6;
79913498266Sopenharmony_ci        else
80013498266Sopenharmony_ci          pf = PF_UNSPEC;
80113498266Sopenharmony_ci      }
80213498266Sopenharmony_ci#endif /* CURLRES_IPV6 */
80313498266Sopenharmony_ci      hints.ai_family = pf;
80413498266Sopenharmony_ci      hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
80513498266Sopenharmony_ci        SOCK_STREAM : SOCK_DGRAM;
80613498266Sopenharmony_ci      /* Since the service is a numerical one, set the hint flags
80713498266Sopenharmony_ci       * accordingly to save a call to getservbyname in inside C-Ares
80813498266Sopenharmony_ci       */
80913498266Sopenharmony_ci      hints.ai_flags = ARES_AI_NUMERICSERV;
81013498266Sopenharmony_ci      msnprintf(service, sizeof(service), "%d", port);
81113498266Sopenharmony_ci      res->num_pending = 1;
81213498266Sopenharmony_ci      ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
81313498266Sopenharmony_ci                       service, &hints, addrinfo_cb, data);
81413498266Sopenharmony_ci    }
81513498266Sopenharmony_ci#else
81613498266Sopenharmony_ci
81713498266Sopenharmony_ci#ifdef HAVE_CARES_IPV6
81813498266Sopenharmony_ci    if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
81913498266Sopenharmony_ci      /* The stack seems to be IPv6-enabled */
82013498266Sopenharmony_ci      res->num_pending = 2;
82113498266Sopenharmony_ci
82213498266Sopenharmony_ci      /* areschannel is already setup in the Curl_open() function */
82313498266Sopenharmony_ci      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
82413498266Sopenharmony_ci                          PF_INET, query_completed_cb, data);
82513498266Sopenharmony_ci      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
82613498266Sopenharmony_ci                          PF_INET6, query_completed_cb, data);
82713498266Sopenharmony_ci    }
82813498266Sopenharmony_ci    else
82913498266Sopenharmony_ci#endif
83013498266Sopenharmony_ci    {
83113498266Sopenharmony_ci      res->num_pending = 1;
83213498266Sopenharmony_ci
83313498266Sopenharmony_ci      /* areschannel is already setup in the Curl_open() function */
83413498266Sopenharmony_ci      ares_gethostbyname((ares_channel)data->state.async.resolver,
83513498266Sopenharmony_ci                         hostname, PF_INET,
83613498266Sopenharmony_ci                         query_completed_cb, data);
83713498266Sopenharmony_ci    }
83813498266Sopenharmony_ci#endif
83913498266Sopenharmony_ci    *waitp = 1; /* expect asynchronous response */
84013498266Sopenharmony_ci  }
84113498266Sopenharmony_ci  return NULL; /* no struct yet */
84213498266Sopenharmony_ci}
84313498266Sopenharmony_ci
84413498266Sopenharmony_ciCURLcode Curl_set_dns_servers(struct Curl_easy *data,
84513498266Sopenharmony_ci                              char *servers)
84613498266Sopenharmony_ci{
84713498266Sopenharmony_ci  CURLcode result = CURLE_NOT_BUILT_IN;
84813498266Sopenharmony_ci  int ares_result;
84913498266Sopenharmony_ci
85013498266Sopenharmony_ci  /* If server is NULL or empty, this would purge all DNS servers
85113498266Sopenharmony_ci   * from ares library, which will cause any and all queries to fail.
85213498266Sopenharmony_ci   * So, just return OK if none are configured and don't actually make
85313498266Sopenharmony_ci   * any changes to c-ares.  This lets c-ares use it's defaults, which
85413498266Sopenharmony_ci   * it gets from the OS (for instance from /etc/resolv.conf on Linux).
85513498266Sopenharmony_ci   */
85613498266Sopenharmony_ci  if(!(servers && servers[0]))
85713498266Sopenharmony_ci    return CURLE_OK;
85813498266Sopenharmony_ci
85913498266Sopenharmony_ci#ifdef HAVE_CARES_SERVERS_CSV
86013498266Sopenharmony_ci#ifdef HAVE_CARES_PORTS_CSV
86113498266Sopenharmony_ci  ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
86213498266Sopenharmony_ci                                           servers);
86313498266Sopenharmony_ci#else
86413498266Sopenharmony_ci  ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
86513498266Sopenharmony_ci#endif
86613498266Sopenharmony_ci  switch(ares_result) {
86713498266Sopenharmony_ci  case ARES_SUCCESS:
86813498266Sopenharmony_ci    result = CURLE_OK;
86913498266Sopenharmony_ci    break;
87013498266Sopenharmony_ci  case ARES_ENOMEM:
87113498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
87213498266Sopenharmony_ci    break;
87313498266Sopenharmony_ci  case ARES_ENOTINITIALIZED:
87413498266Sopenharmony_ci  case ARES_ENODATA:
87513498266Sopenharmony_ci  case ARES_EBADSTR:
87613498266Sopenharmony_ci  default:
87713498266Sopenharmony_ci    DEBUGF(infof(data, "bad servers set"));
87813498266Sopenharmony_ci    result = CURLE_BAD_FUNCTION_ARGUMENT;
87913498266Sopenharmony_ci    break;
88013498266Sopenharmony_ci  }
88113498266Sopenharmony_ci#else /* too old c-ares version! */
88213498266Sopenharmony_ci  (void)data;
88313498266Sopenharmony_ci  (void)(ares_result);
88413498266Sopenharmony_ci#endif
88513498266Sopenharmony_ci  return result;
88613498266Sopenharmony_ci}
88713498266Sopenharmony_ci
88813498266Sopenharmony_ciCURLcode Curl_set_dns_interface(struct Curl_easy *data,
88913498266Sopenharmony_ci                                const char *interf)
89013498266Sopenharmony_ci{
89113498266Sopenharmony_ci#ifdef HAVE_CARES_LOCAL_DEV
89213498266Sopenharmony_ci  if(!interf)
89313498266Sopenharmony_ci    interf = "";
89413498266Sopenharmony_ci
89513498266Sopenharmony_ci  ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
89613498266Sopenharmony_ci
89713498266Sopenharmony_ci  return CURLE_OK;
89813498266Sopenharmony_ci#else /* c-ares version too old! */
89913498266Sopenharmony_ci  (void)data;
90013498266Sopenharmony_ci  (void)interf;
90113498266Sopenharmony_ci  return CURLE_NOT_BUILT_IN;
90213498266Sopenharmony_ci#endif
90313498266Sopenharmony_ci}
90413498266Sopenharmony_ci
90513498266Sopenharmony_ciCURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
90613498266Sopenharmony_ci                                const char *local_ip4)
90713498266Sopenharmony_ci{
90813498266Sopenharmony_ci#ifdef HAVE_CARES_SET_LOCAL
90913498266Sopenharmony_ci  struct in_addr a4;
91013498266Sopenharmony_ci
91113498266Sopenharmony_ci  if((!local_ip4) || (local_ip4[0] == 0)) {
91213498266Sopenharmony_ci    a4.s_addr = 0; /* disabled: do not bind to a specific address */
91313498266Sopenharmony_ci  }
91413498266Sopenharmony_ci  else {
91513498266Sopenharmony_ci    if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
91613498266Sopenharmony_ci      DEBUGF(infof(data, "bad DNS IPv4 address"));
91713498266Sopenharmony_ci      return CURLE_BAD_FUNCTION_ARGUMENT;
91813498266Sopenharmony_ci    }
91913498266Sopenharmony_ci  }
92013498266Sopenharmony_ci
92113498266Sopenharmony_ci  ares_set_local_ip4((ares_channel)data->state.async.resolver,
92213498266Sopenharmony_ci                     ntohl(a4.s_addr));
92313498266Sopenharmony_ci
92413498266Sopenharmony_ci  return CURLE_OK;
92513498266Sopenharmony_ci#else /* c-ares version too old! */
92613498266Sopenharmony_ci  (void)data;
92713498266Sopenharmony_ci  (void)local_ip4;
92813498266Sopenharmony_ci  return CURLE_NOT_BUILT_IN;
92913498266Sopenharmony_ci#endif
93013498266Sopenharmony_ci}
93113498266Sopenharmony_ci
93213498266Sopenharmony_ciCURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
93313498266Sopenharmony_ci                                const char *local_ip6)
93413498266Sopenharmony_ci{
93513498266Sopenharmony_ci#if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6)
93613498266Sopenharmony_ci  unsigned char a6[INET6_ADDRSTRLEN];
93713498266Sopenharmony_ci
93813498266Sopenharmony_ci  if((!local_ip6) || (local_ip6[0] == 0)) {
93913498266Sopenharmony_ci    /* disabled: do not bind to a specific address */
94013498266Sopenharmony_ci    memset(a6, 0, sizeof(a6));
94113498266Sopenharmony_ci  }
94213498266Sopenharmony_ci  else {
94313498266Sopenharmony_ci    if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
94413498266Sopenharmony_ci      DEBUGF(infof(data, "bad DNS IPv6 address"));
94513498266Sopenharmony_ci      return CURLE_BAD_FUNCTION_ARGUMENT;
94613498266Sopenharmony_ci    }
94713498266Sopenharmony_ci  }
94813498266Sopenharmony_ci
94913498266Sopenharmony_ci  ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
95013498266Sopenharmony_ci
95113498266Sopenharmony_ci  return CURLE_OK;
95213498266Sopenharmony_ci#else /* c-ares version too old! */
95313498266Sopenharmony_ci  (void)data;
95413498266Sopenharmony_ci  (void)local_ip6;
95513498266Sopenharmony_ci  return CURLE_NOT_BUILT_IN;
95613498266Sopenharmony_ci#endif
95713498266Sopenharmony_ci}
95813498266Sopenharmony_ci#endif /* CURLRES_ARES */
959