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