xref: /third_party/curl/lib/asyn-ares.c (revision 13498266)
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/***********************************************************************
28 * Only for ares-enabled builds
29 * And only for functions that fulfill the asynch resolver backend API
30 * as defined in asyn.h, nothing else belongs in this file!
31 **********************************************************************/
32
33#ifdef CURLRES_ARES
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 "sendf.h"
52#include "hostip.h"
53#include "hash.h"
54#include "share.h"
55#include "url.h"
56#include "multiif.h"
57#include "inet_pton.h"
58#include "connect.h"
59#include "select.h"
60#include "progress.h"
61#include "timediff.h"
62
63#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
64  defined(_WIN32)
65#  define CARES_STATICLIB
66#endif
67#include <ares.h>
68#include <ares_version.h> /* really old c-ares didn't include this by
69                             itself */
70
71#if ARES_VERSION >= 0x010500
72/* c-ares 1.5.0 or later, the callback proto is modified */
73#define HAVE_CARES_CALLBACK_TIMEOUTS 1
74#endif
75
76#if ARES_VERSION >= 0x010601
77/* IPv6 supported since 1.6.1 */
78#define HAVE_CARES_IPV6 1
79#endif
80
81#if ARES_VERSION >= 0x010704
82#define HAVE_CARES_SERVERS_CSV 1
83#define HAVE_CARES_LOCAL_DEV 1
84#define HAVE_CARES_SET_LOCAL 1
85#endif
86
87#if ARES_VERSION >= 0x010b00
88#define HAVE_CARES_PORTS_CSV 1
89#endif
90
91#if ARES_VERSION >= 0x011000
92/* 1.16.0 or later has ares_getaddrinfo */
93#define HAVE_CARES_GETADDRINFO 1
94#endif
95
96/* The last 3 #include files should be in this order */
97#include "curl_printf.h"
98#include "curl_memory.h"
99#include "memdebug.h"
100
101struct thread_data {
102  int num_pending; /* number of outstanding c-ares requests */
103  struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
104                                    parts */
105  int last_status;
106#ifndef HAVE_CARES_GETADDRINFO
107  struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
108#endif
109  char hostname[1];
110};
111
112/* How long we are willing to wait for additional parallel responses after
113   obtaining a "definitive" one. For old c-ares without getaddrinfo.
114
115   This is intended to equal the c-ares default timeout.  cURL always uses that
116   default value.  Unfortunately, c-ares doesn't expose its default timeout in
117   its API, but it is officially documented as 5 seconds.
118
119   See query_completed_cb() for an explanation of how this is used.
120 */
121#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
122
123#define CARES_TIMEOUT_PER_ATTEMPT 2000
124
125/*
126 * Curl_resolver_global_init() - the generic low-level asynchronous name
127 * resolve API.  Called from curl_global_init() to initialize global resolver
128 * environment.  Initializes ares library.
129 */
130int Curl_resolver_global_init(void)
131{
132#ifdef CARES_HAVE_ARES_LIBRARY_INIT
133  if(ares_library_init(ARES_LIB_INIT_ALL)) {
134    return CURLE_FAILED_INIT;
135  }
136#endif
137  return CURLE_OK;
138}
139
140/*
141 * Curl_resolver_global_cleanup()
142 *
143 * Called from curl_global_cleanup() to destroy global resolver environment.
144 * Deinitializes ares library.
145 */
146void Curl_resolver_global_cleanup(void)
147{
148#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
149  ares_library_cleanup();
150#endif
151}
152
153
154static void sock_state_cb(void *data, ares_socket_t socket_fd,
155                          int readable, int writable)
156{
157  struct Curl_easy *easy = data;
158  if(!readable && !writable) {
159    DEBUGASSERT(easy);
160    Curl_multi_closed(easy, socket_fd);
161  }
162}
163
164/*
165 * Curl_resolver_init()
166 *
167 * Called from curl_easy_init() -> Curl_open() to initialize resolver
168 * URL-state specific environment ('resolver' member of the UrlState
169 * structure).  Fills the passed pointer by the initialized ares_channel.
170 */
171CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
172{
173  int status;
174  struct ares_options options;
175  int optmask = ARES_OPT_SOCK_STATE_CB;
176  static int ares_ver = 0;
177  options.sock_state_cb = sock_state_cb;
178  options.sock_state_cb_data = easy;
179  if(ares_ver == 0)
180    ares_version(&ares_ver);
181
182  if(ares_ver < 0x011400) { /* c-ares included similar change since 1.20.0 */
183    options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
184    optmask |= ARES_OPT_TIMEOUTMS;
185  }
186
187  /*
188     if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
189
190     if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
191     to set the timeout value;
192
193     if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
194     overwrite c-ares' timeout.
195  */
196
197  status = ares_init_options((ares_channel*)resolver, &options, optmask);
198  if(status != ARES_SUCCESS) {
199    if(status == ARES_ENOMEM)
200      return CURLE_OUT_OF_MEMORY;
201    else
202      return CURLE_FAILED_INIT;
203  }
204  return CURLE_OK;
205  /* make sure that all other returns from this function should destroy the
206     ares channel before returning error! */
207}
208
209/*
210 * Curl_resolver_cleanup()
211 *
212 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
213 * URL-state specific environment ('resolver' member of the UrlState
214 * structure).  Destroys the ares channel.
215 */
216void Curl_resolver_cleanup(void *resolver)
217{
218  ares_destroy((ares_channel)resolver);
219}
220
221/*
222 * Curl_resolver_duphandle()
223 *
224 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
225 * environment ('resolver' member of the UrlState structure).  Duplicates the
226 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
227 */
228CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
229{
230  (void)from;
231  /*
232   * it would be better to call ares_dup instead, but right now
233   * it is not possible to set 'sock_state_cb_data' outside of
234   * ares_init_options
235   */
236  return Curl_resolver_init(easy, to);
237}
238
239static void destroy_async_data(struct Curl_async *async);
240
241/*
242 * Cancel all possibly still on-going resolves for this connection.
243 */
244void Curl_resolver_cancel(struct Curl_easy *data)
245{
246  DEBUGASSERT(data);
247  if(data->state.async.resolver)
248    ares_cancel((ares_channel)data->state.async.resolver);
249  destroy_async_data(&data->state.async);
250}
251
252/*
253 * We're equivalent to Curl_resolver_cancel() for the c-ares resolver.  We
254 * never block.
255 */
256void Curl_resolver_kill(struct Curl_easy *data)
257{
258  /* We don't need to check the resolver state because we can be called safely
259     at any time and we always do the same thing. */
260  Curl_resolver_cancel(data);
261}
262
263/*
264 * destroy_async_data() cleans up async resolver data.
265 */
266static void destroy_async_data(struct Curl_async *async)
267{
268  if(async->tdata) {
269    struct thread_data *res = async->tdata;
270    if(res) {
271      if(res->temp_ai) {
272        Curl_freeaddrinfo(res->temp_ai);
273        res->temp_ai = NULL;
274      }
275      free(res);
276    }
277    async->tdata = NULL;
278  }
279}
280
281/*
282 * Curl_resolver_getsock() is called when someone from the outside world
283 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
284 * with ares. The caller must make sure that this function is only called when
285 * we have a working ares channel.
286 *
287 * Returns: sockets-in-use-bitmap
288 */
289
290int Curl_resolver_getsock(struct Curl_easy *data,
291                          curl_socket_t *socks)
292{
293  struct timeval maxtime;
294  struct timeval timebuf;
295  struct timeval *timeout;
296  long milli;
297  int max = ares_getsock((ares_channel)data->state.async.resolver,
298                         (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
299
300  maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
301  maxtime.tv_usec = 0;
302
303  timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
304                         &timebuf);
305  milli = (long)curlx_tvtoms(timeout);
306  if(milli == 0)
307    milli += 10;
308  Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
309
310  return max;
311}
312
313/*
314 * waitperform()
315 *
316 * 1) Ask ares what sockets it currently plays with, then
317 * 2) wait for the timeout period to check for action on ares' sockets.
318 * 3) tell ares to act on all the sockets marked as "with action"
319 *
320 * return number of sockets it worked on, or -1 on error
321 */
322
323static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
324{
325  int nfds;
326  int bitmask;
327  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
328  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
329  int i;
330  int num = 0;
331
332  bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
333                         ARES_GETSOCK_MAXNUM);
334
335  for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
336    pfd[i].events = 0;
337    pfd[i].revents = 0;
338    if(ARES_GETSOCK_READABLE(bitmask, i)) {
339      pfd[i].fd = socks[i];
340      pfd[i].events |= POLLRDNORM|POLLIN;
341    }
342    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
343      pfd[i].fd = socks[i];
344      pfd[i].events |= POLLWRNORM|POLLOUT;
345    }
346    if(pfd[i].events)
347      num++;
348    else
349      break;
350  }
351
352  if(num) {
353    nfds = Curl_poll(pfd, num, timeout_ms);
354    if(nfds < 0)
355      return -1;
356  }
357  else
358    nfds = 0;
359
360  if(!nfds)
361    /* Call ares_process() unconditionally here, even if we simply timed out
362       above, as otherwise the ares name resolve won't timeout! */
363    ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
364                    ARES_SOCKET_BAD);
365  else {
366    /* move through the descriptors and ask for processing on them */
367    for(i = 0; i < num; i++)
368      ares_process_fd((ares_channel)data->state.async.resolver,
369                      (pfd[i].revents & (POLLRDNORM|POLLIN))?
370                      pfd[i].fd:ARES_SOCKET_BAD,
371                      (pfd[i].revents & (POLLWRNORM|POLLOUT))?
372                      pfd[i].fd:ARES_SOCKET_BAD);
373  }
374  return nfds;
375}
376
377/*
378 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
379 * name resolve request has completed. It should also make sure to time-out if
380 * the operation seems to take too long.
381 *
382 * Returns normal CURLcode errors.
383 */
384CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
385                                   struct Curl_dns_entry **dns)
386{
387  struct thread_data *res = data->state.async.tdata;
388  CURLcode result = CURLE_OK;
389
390  DEBUGASSERT(dns);
391  *dns = NULL;
392
393  if(waitperform(data, 0) < 0)
394    return CURLE_UNRECOVERABLE_POLL;
395
396#ifndef HAVE_CARES_GETADDRINFO
397  /* Now that we've checked for any last minute results above, see if there are
398     any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
399     expires. */
400  if(res
401     && res->num_pending
402     /* This is only set to non-zero if the timer was started. */
403     && (res->happy_eyeballs_dns_time.tv_sec
404         || res->happy_eyeballs_dns_time.tv_usec)
405     && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
406         >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
407    /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
408       running. */
409    memset(
410      &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
411
412    /* Cancel the raw c-ares request, which will fire query_completed_cb() with
413       ARES_ECANCELLED synchronously for all pending responses.  This will
414       leave us with res->num_pending == 0, which is perfect for the next
415       block. */
416    ares_cancel((ares_channel)data->state.async.resolver);
417    DEBUGASSERT(res->num_pending == 0);
418  }
419#endif
420
421  if(res && !res->num_pending) {
422    (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
423    /* temp_ai ownership is moved to the connection, so we need not free-up
424       them */
425    res->temp_ai = NULL;
426
427    if(!data->state.async.dns)
428      result = Curl_resolver_error(data);
429    else
430      *dns = data->state.async.dns;
431
432    destroy_async_data(&data->state.async);
433  }
434
435  return result;
436}
437
438/*
439 * Curl_resolver_wait_resolv()
440 *
441 * Waits for a resolve to finish. This function should be avoided since using
442 * this risk getting the multi interface to "hang".
443 *
444 * 'entry' MUST be non-NULL.
445 *
446 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
447 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
448 */
449CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
450                                   struct Curl_dns_entry **entry)
451{
452  CURLcode result = CURLE_OK;
453  timediff_t timeout;
454  struct curltime now = Curl_now();
455
456  DEBUGASSERT(entry);
457  *entry = NULL; /* clear on entry */
458
459  timeout = Curl_timeleft(data, &now, TRUE);
460  if(timeout < 0) {
461    /* already expired! */
462    connclose(data->conn, "Timed out before name resolve started");
463    return CURLE_OPERATION_TIMEDOUT;
464  }
465  if(!timeout)
466    timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
467
468  /* Wait for the name resolve query to complete. */
469  while(!result) {
470    struct timeval *tvp, tv, store;
471    int itimeout;
472    timediff_t timeout_ms;
473
474#if TIMEDIFF_T_MAX > INT_MAX
475    itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
476#else
477    itimeout = (int)timeout;
478#endif
479
480    store.tv_sec = itimeout/1000;
481    store.tv_usec = (itimeout%1000)*1000;
482
483    tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
484
485    /* use the timeout period ares returned to us above if less than one
486       second is left, otherwise just use 1000ms to make sure the progress
487       callback gets called frequent enough */
488    if(!tvp->tv_sec)
489      timeout_ms = (timediff_t)(tvp->tv_usec/1000);
490    else
491      timeout_ms = 1000;
492
493    if(waitperform(data, timeout_ms) < 0)
494      return CURLE_UNRECOVERABLE_POLL;
495    result = Curl_resolver_is_resolved(data, entry);
496
497    if(result || data->state.async.done)
498      break;
499
500    if(Curl_pgrsUpdate(data))
501      result = CURLE_ABORTED_BY_CALLBACK;
502    else {
503      struct curltime now2 = Curl_now();
504      timediff_t timediff = Curl_timediff(now2, now); /* spent time */
505      if(timediff <= 0)
506        timeout -= 1; /* always deduct at least 1 */
507      else if(timediff > timeout)
508        timeout = -1;
509      else
510        timeout -= timediff;
511      now = now2; /* for next loop */
512    }
513    if(timeout < 0)
514      result = CURLE_OPERATION_TIMEDOUT;
515  }
516  if(result)
517    /* failure, so we cancel the ares operation */
518    ares_cancel((ares_channel)data->state.async.resolver);
519
520  /* Operation complete, if the lookup was successful we now have the entry
521     in the cache. */
522  if(entry)
523    *entry = data->state.async.dns;
524
525  if(result)
526    /* close the connection, since we can't return failure here without
527       cleaning up this connection properly. */
528    connclose(data->conn, "c-ares resolve failed");
529
530  return result;
531}
532
533#ifndef HAVE_CARES_GETADDRINFO
534
535/* Connects results to the list */
536static void compound_results(struct thread_data *res,
537                             struct Curl_addrinfo *ai)
538{
539  if(!ai)
540    return;
541
542#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
543  if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
544    /* We have results already, put the new IPv6 entries at the head of the
545       list. */
546    struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
547
548    while(temp_ai_tail->ai_next)
549      temp_ai_tail = temp_ai_tail->ai_next;
550
551    temp_ai_tail->ai_next = ai;
552  }
553  else
554#endif /* CURLRES_IPV6 */
555  {
556    /* Add the new results to the list of old results. */
557    struct Curl_addrinfo *ai_tail = ai;
558    while(ai_tail->ai_next)
559      ai_tail = ai_tail->ai_next;
560
561    ai_tail->ai_next = res->temp_ai;
562    res->temp_ai = ai;
563  }
564}
565
566/*
567 * ares_query_completed_cb() is the callback that ares will call when
568 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
569 * when using ares, is completed either successfully or with failure.
570 */
571static void query_completed_cb(void *arg,  /* (struct connectdata *) */
572                               int status,
573#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
574                               int timeouts,
575#endif
576                               struct hostent *hostent)
577{
578  struct Curl_easy *data = (struct Curl_easy *)arg;
579  struct thread_data *res;
580
581#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
582  (void)timeouts; /* ignored */
583#endif
584
585  if(ARES_EDESTRUCTION == status)
586    /* when this ares handle is getting destroyed, the 'arg' pointer may not
587       be valid so only defer it when we know the 'status' says its fine! */
588    return;
589
590  res = data->state.async.tdata;
591  if(res) {
592    res->num_pending--;
593
594    if(CURL_ASYNC_SUCCESS == status) {
595      struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
596      if(ai) {
597        compound_results(res, ai);
598      }
599    }
600    /* A successful result overwrites any previous error */
601    if(res->last_status != ARES_SUCCESS)
602      res->last_status = status;
603
604    /* If there are responses still pending, we presume they must be the
605       complementary IPv4 or IPv6 lookups that we started in parallel in
606       Curl_resolver_getaddrinfo() (for Happy Eyeballs).  If we've got a
607       "definitive" response from one of a set of parallel queries, we need to
608       think about how long we're willing to wait for more responses. */
609    if(res->num_pending
610       /* Only these c-ares status values count as "definitive" for these
611          purposes.  For example, ARES_ENODATA is what we expect when there is
612          no IPv6 entry for a domain name, and that's not a reason to get more
613          aggressive in our timeouts for the other response.  Other errors are
614          either a result of bad input (which should affect all parallel
615          requests), local or network conditions, non-definitive server
616          responses, or us cancelling the request. */
617       && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
618      /* Right now, there can only be up to two parallel queries, so don't
619         bother handling any other cases. */
620      DEBUGASSERT(res->num_pending == 1);
621
622      /* It's possible that one of these parallel queries could succeed
623         quickly, but the other could always fail or timeout (when we're
624         talking to a pool of DNS servers that can only successfully resolve
625         IPv4 address, for example).
626
627         It's also possible that the other request could always just take
628         longer because it needs more time or only the second DNS server can
629         fulfill it successfully.  But, to align with the philosophy of Happy
630         Eyeballs, we don't want to wait _too_ long or users will think
631         requests are slow when IPv6 lookups don't actually work (but IPv4 ones
632         do).
633
634         So, now that we have a usable answer (some IPv4 addresses, some IPv6
635         addresses, or "no such domain"), we start a timeout for the remaining
636         pending responses.  Even though it is typical that this resolved
637         request came back quickly, that needn't be the case.  It might be that
638         this completing request didn't get a result from the first DNS server
639         or even the first round of the whole DNS server pool.  So it could
640         already be quite some time after we issued the DNS queries in the
641         first place.  Without modifying c-ares, we can't know exactly where in
642         its retry cycle we are.  We could guess based on how much time has
643         gone by, but it doesn't really matter.  Happy Eyeballs tells us that,
644         given usable information in hand, we simply don't want to wait "too
645         much longer" after we get a result.
646
647         We simply wait an additional amount of time equal to the default
648         c-ares query timeout.  That is enough time for a typical parallel
649         response to arrive without being "too long".  Even on a network
650         where one of the two types of queries is failing or timing out
651         constantly, this will usually mean we wait a total of the default
652         c-ares timeout (5 seconds) plus the round trip time for the successful
653         request, which seems bearable.  The downside is that c-ares might race
654         with us to issue one more retry just before we give up, but it seems
655         better to "waste" that request instead of trying to guess the perfect
656         timeout to prevent it.  After all, we don't even know where in the
657         c-ares retry cycle each request is.
658      */
659      res->happy_eyeballs_dns_time = Curl_now();
660      Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
661                  EXPIRE_HAPPY_EYEBALLS_DNS);
662    }
663  }
664}
665#else
666/* c-ares 1.16.0 or later */
667
668/*
669 * ares2addr() converts an address list provided by c-ares to an internal
670 * libcurl compatible list
671 */
672static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
673{
674  /* traverse the ares_addrinfo_node list */
675  struct ares_addrinfo_node *ai;
676  struct Curl_addrinfo *cafirst = NULL;
677  struct Curl_addrinfo *calast = NULL;
678  int error = 0;
679
680  for(ai = node; ai != NULL; ai = ai->ai_next) {
681    size_t ss_size;
682    struct Curl_addrinfo *ca;
683    /* ignore elements with unsupported address family, */
684    /* settle family-specific sockaddr structure size.  */
685    if(ai->ai_family == AF_INET)
686      ss_size = sizeof(struct sockaddr_in);
687#ifdef ENABLE_IPV6
688    else if(ai->ai_family == AF_INET6)
689      ss_size = sizeof(struct sockaddr_in6);
690#endif
691    else
692      continue;
693
694    /* ignore elements without required address info */
695    if(!ai->ai_addr || !(ai->ai_addrlen > 0))
696      continue;
697
698    /* ignore elements with bogus address size */
699    if((size_t)ai->ai_addrlen < ss_size)
700      continue;
701
702    ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
703    if(!ca) {
704      error = EAI_MEMORY;
705      break;
706    }
707
708    /* copy each structure member individually, member ordering, */
709    /* size, or padding might be different for each platform.    */
710
711    ca->ai_flags     = ai->ai_flags;
712    ca->ai_family    = ai->ai_family;
713    ca->ai_socktype  = ai->ai_socktype;
714    ca->ai_protocol  = ai->ai_protocol;
715    ca->ai_addrlen   = (curl_socklen_t)ss_size;
716    ca->ai_addr      = NULL;
717    ca->ai_canonname = NULL;
718    ca->ai_next      = NULL;
719
720    ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
721    memcpy(ca->ai_addr, ai->ai_addr, ss_size);
722
723    /* if the return list is empty, this becomes the first element */
724    if(!cafirst)
725      cafirst = ca;
726
727    /* add this element last in the return list */
728    if(calast)
729      calast->ai_next = ca;
730    calast = ca;
731  }
732
733  /* if we failed, destroy the Curl_addrinfo list */
734  if(error) {
735    Curl_freeaddrinfo(cafirst);
736    cafirst = NULL;
737  }
738
739  return cafirst;
740}
741
742static void addrinfo_cb(void *arg, int status, int timeouts,
743                        struct ares_addrinfo *result)
744{
745  struct Curl_easy *data = (struct Curl_easy *)arg;
746  struct thread_data *res = data->state.async.tdata;
747  (void)timeouts;
748  if(ARES_SUCCESS == status) {
749    res->temp_ai = ares2addr(result->nodes);
750    res->last_status = CURL_ASYNC_SUCCESS;
751    ares_freeaddrinfo(result);
752  }
753  res->num_pending--;
754}
755
756#endif
757/*
758 * Curl_resolver_getaddrinfo() - when using ares
759 *
760 * Returns name information about the given hostname and port number. If
761 * successful, the 'hostent' is returned and the fourth argument will point to
762 * memory we need to free after use. That memory *MUST* be freed with
763 * Curl_freeaddrinfo(), nothing else.
764 */
765struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
766                                                const char *hostname,
767                                                int port,
768                                                int *waitp)
769{
770  struct thread_data *res = NULL;
771  size_t namelen = strlen(hostname);
772  *waitp = 0; /* default to synchronous response */
773
774  res = calloc(1, sizeof(struct thread_data) + namelen);
775  if(res) {
776    strcpy(res->hostname, hostname);
777    data->state.async.hostname = res->hostname;
778    data->state.async.port = port;
779    data->state.async.done = FALSE;   /* not done */
780    data->state.async.status = 0;     /* clear */
781    data->state.async.dns = NULL;     /* clear */
782    data->state.async.tdata = res;
783
784    /* initial status - failed */
785    res->last_status = ARES_ENOTFOUND;
786
787#ifdef HAVE_CARES_GETADDRINFO
788    {
789      struct ares_addrinfo_hints hints;
790      char service[12];
791      int pf = PF_INET;
792      memset(&hints, 0, sizeof(hints));
793#ifdef CURLRES_IPV6
794      if((data->conn->ip_version != CURL_IPRESOLVE_V4) &&
795         Curl_ipv6works(data)) {
796        /* The stack seems to be IPv6-enabled */
797        if(data->conn->ip_version == CURL_IPRESOLVE_V6)
798          pf = PF_INET6;
799        else
800          pf = PF_UNSPEC;
801      }
802#endif /* CURLRES_IPV6 */
803      hints.ai_family = pf;
804      hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
805        SOCK_STREAM : SOCK_DGRAM;
806      /* Since the service is a numerical one, set the hint flags
807       * accordingly to save a call to getservbyname in inside C-Ares
808       */
809      hints.ai_flags = ARES_AI_NUMERICSERV;
810      msnprintf(service, sizeof(service), "%d", port);
811      res->num_pending = 1;
812      ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
813                       service, &hints, addrinfo_cb, data);
814    }
815#else
816
817#ifdef HAVE_CARES_IPV6
818    if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
819      /* The stack seems to be IPv6-enabled */
820      res->num_pending = 2;
821
822      /* areschannel is already setup in the Curl_open() function */
823      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
824                          PF_INET, query_completed_cb, data);
825      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
826                          PF_INET6, query_completed_cb, data);
827    }
828    else
829#endif
830    {
831      res->num_pending = 1;
832
833      /* areschannel is already setup in the Curl_open() function */
834      ares_gethostbyname((ares_channel)data->state.async.resolver,
835                         hostname, PF_INET,
836                         query_completed_cb, data);
837    }
838#endif
839    *waitp = 1; /* expect asynchronous response */
840  }
841  return NULL; /* no struct yet */
842}
843
844CURLcode Curl_set_dns_servers(struct Curl_easy *data,
845                              char *servers)
846{
847  CURLcode result = CURLE_NOT_BUILT_IN;
848  int ares_result;
849
850  /* If server is NULL or empty, this would purge all DNS servers
851   * from ares library, which will cause any and all queries to fail.
852   * So, just return OK if none are configured and don't actually make
853   * any changes to c-ares.  This lets c-ares use it's defaults, which
854   * it gets from the OS (for instance from /etc/resolv.conf on Linux).
855   */
856  if(!(servers && servers[0]))
857    return CURLE_OK;
858
859#ifdef HAVE_CARES_SERVERS_CSV
860#ifdef HAVE_CARES_PORTS_CSV
861  ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
862                                           servers);
863#else
864  ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
865#endif
866  switch(ares_result) {
867  case ARES_SUCCESS:
868    result = CURLE_OK;
869    break;
870  case ARES_ENOMEM:
871    result = CURLE_OUT_OF_MEMORY;
872    break;
873  case ARES_ENOTINITIALIZED:
874  case ARES_ENODATA:
875  case ARES_EBADSTR:
876  default:
877    DEBUGF(infof(data, "bad servers set"));
878    result = CURLE_BAD_FUNCTION_ARGUMENT;
879    break;
880  }
881#else /* too old c-ares version! */
882  (void)data;
883  (void)(ares_result);
884#endif
885  return result;
886}
887
888CURLcode Curl_set_dns_interface(struct Curl_easy *data,
889                                const char *interf)
890{
891#ifdef HAVE_CARES_LOCAL_DEV
892  if(!interf)
893    interf = "";
894
895  ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
896
897  return CURLE_OK;
898#else /* c-ares version too old! */
899  (void)data;
900  (void)interf;
901  return CURLE_NOT_BUILT_IN;
902#endif
903}
904
905CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
906                                const char *local_ip4)
907{
908#ifdef HAVE_CARES_SET_LOCAL
909  struct in_addr a4;
910
911  if((!local_ip4) || (local_ip4[0] == 0)) {
912    a4.s_addr = 0; /* disabled: do not bind to a specific address */
913  }
914  else {
915    if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
916      DEBUGF(infof(data, "bad DNS IPv4 address"));
917      return CURLE_BAD_FUNCTION_ARGUMENT;
918    }
919  }
920
921  ares_set_local_ip4((ares_channel)data->state.async.resolver,
922                     ntohl(a4.s_addr));
923
924  return CURLE_OK;
925#else /* c-ares version too old! */
926  (void)data;
927  (void)local_ip4;
928  return CURLE_NOT_BUILT_IN;
929#endif
930}
931
932CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
933                                const char *local_ip6)
934{
935#if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6)
936  unsigned char a6[INET6_ADDRSTRLEN];
937
938  if((!local_ip6) || (local_ip6[0] == 0)) {
939    /* disabled: do not bind to a specific address */
940    memset(a6, 0, sizeof(a6));
941  }
942  else {
943    if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
944      DEBUGF(infof(data, "bad DNS IPv6 address"));
945      return CURLE_BAD_FUNCTION_ARGUMENT;
946    }
947  }
948
949  ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
950
951  return CURLE_OK;
952#else /* c-ares version too old! */
953  (void)data;
954  (void)local_ip6;
955  return CURLE_NOT_BUILT_IN;
956#endif
957}
958#endif /* CURLRES_ARES */
959