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