xref: /curl/lib/asyn-ares.c (revision e411c98f)
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 
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 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 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're 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 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  */
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'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 
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;
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 
waitperform(struct Curl_easy * data,timediff_t timeout_ms)323 static 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  */
Curl_resolver_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dns)384 CURLcode 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  */
Curl_resolver_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry)449 CURLcode 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 */
compound_results(struct thread_data * res,struct Curl_addrinfo * ai)536 static void compound_results(struct thread_data *res,
537                              struct Curl_addrinfo *ai)
538 {
539   if(!ai)
540     return;
541 
542 #ifdef USE_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  */
query_completed_cb(void * arg,int status,int timeouts,struct hostent * hostent)571 static 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  */
ares2addr(struct ares_addrinfo_node * node)672 static 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 USE_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 
addrinfo_cb(void * arg,int status,int timeouts,struct ares_addrinfo * result)742 static 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  */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)765 struct 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 
Curl_set_dns_servers(struct Curl_easy * data,char * servers)844 CURLcode 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 its 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 
Curl_set_dns_interface(struct Curl_easy * data,const char * interf)888 CURLcode 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 
Curl_set_dns_local_ip4(struct Curl_easy * data,const char * local_ip4)905 CURLcode 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 
Curl_set_dns_local_ip6(struct Curl_easy * data,const char * local_ip6)932 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
933                                 const char *local_ip6)
934 {
935 #if defined(HAVE_CARES_SET_LOCAL) && defined(USE_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