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