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