• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2021, 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  ***************************************************************************/
22 
23 #include "curl_setup.h"
24 
25 /***********************************************************************
26  * Only for ares-enabled builds
27  * And only for functions that fulfill the asynch resolver backend API
28  * as defined in asyn.h, nothing else belongs in this file!
29  **********************************************************************/
30 
31 #ifdef CURLRES_ARES
32 
33 #include <limits.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37 #ifdef HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
43 #ifdef __VMS
44 #include <in.h>
45 #include <inet.h>
46 #endif
47 
48 #ifdef HAVE_PROCESS_H
49 #include <process.h>
50 #endif
51 
52 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
53 #undef in_addr_t
54 #define in_addr_t unsigned long
55 #endif
56 
57 #include "urldata.h"
58 #include "sendf.h"
59 #include "hostip.h"
60 #include "hash.h"
61 #include "share.h"
62 #include "strerror.h"
63 #include "url.h"
64 #include "multiif.h"
65 #include "inet_pton.h"
66 #include "connect.h"
67 #include "select.h"
68 #include "progress.h"
69 
70 #  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
71   defined(WIN32)
72 #    define CARES_STATICLIB
73 #  endif
74 #  include <ares.h>
75 #  include <ares_version.h> /* really old c-ares didn't include this by
76                                itself */
77 
78 #if ARES_VERSION >= 0x010500
79 /* c-ares 1.5.0 or later, the callback proto is modified */
80 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
81 #endif
82 
83 /* The last 3 #include files should be in this order */
84 #include "curl_printf.h"
85 #include "curl_memory.h"
86 #include "memdebug.h"
87 
88 struct thread_data {
89   int num_pending; /* number of ares_gethostbyname() requests */
90   struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
91                                     parts */
92   int last_status;
93   struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
94 };
95 
96 /* How long we are willing to wait for additional parallel responses after
97    obtaining a "definitive" one.
98 
99    This is intended to equal the c-ares default timeout.  cURL always uses that
100    default value.  Unfortunately, c-ares doesn't expose its default timeout in
101    its API, but it is officially documented as 5 seconds.
102 
103    See query_completed_cb() for an explanation of how this is used.
104  */
105 #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
106 
107 /*
108  * Curl_resolver_global_init() - the generic low-level asynchronous name
109  * resolve API.  Called from curl_global_init() to initialize global resolver
110  * environment.  Initializes ares library.
111  */
Curl_resolver_global_init(void)112 int Curl_resolver_global_init(void)
113 {
114 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
115   if(ares_library_init(ARES_LIB_INIT_ALL)) {
116     return CURLE_FAILED_INIT;
117   }
118 #endif
119   return CURLE_OK;
120 }
121 
122 /*
123  * Curl_resolver_global_cleanup()
124  *
125  * Called from curl_global_cleanup() to destroy global resolver environment.
126  * Deinitializes ares library.
127  */
Curl_resolver_global_cleanup(void)128 void Curl_resolver_global_cleanup(void)
129 {
130 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
131   ares_library_cleanup();
132 #endif
133 }
134 
135 
sock_state_cb(void * data,ares_socket_t socket_fd,int readable,int writable)136 static void sock_state_cb(void *data, ares_socket_t socket_fd,
137                           int readable, int writable)
138 {
139   struct Curl_easy *easy = data;
140   if(!readable && !writable) {
141     DEBUGASSERT(easy);
142     Curl_multi_closed(easy, socket_fd);
143   }
144 }
145 
146 /*
147  * Curl_resolver_init()
148  *
149  * Called from curl_easy_init() -> Curl_open() to initialize resolver
150  * URL-state specific environment ('resolver' member of the UrlState
151  * structure).  Fills the passed pointer by the initialized ares_channel.
152  */
Curl_resolver_init(struct Curl_easy * easy,void ** resolver)153 CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
154 {
155   int status;
156   struct ares_options options;
157   int optmask = ARES_OPT_SOCK_STATE_CB;
158   options.sock_state_cb = sock_state_cb;
159   options.sock_state_cb_data = easy;
160   status = ares_init_options((ares_channel*)resolver, &options, optmask);
161   if(status != ARES_SUCCESS) {
162     if(status == ARES_ENOMEM)
163       return CURLE_OUT_OF_MEMORY;
164     else
165       return CURLE_FAILED_INIT;
166   }
167   return CURLE_OK;
168   /* make sure that all other returns from this function should destroy the
169      ares channel before returning error! */
170 }
171 
172 /*
173  * Curl_resolver_cleanup()
174  *
175  * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
176  * URL-state specific environment ('resolver' member of the UrlState
177  * structure).  Destroys the ares channel.
178  */
Curl_resolver_cleanup(void * resolver)179 void Curl_resolver_cleanup(void *resolver)
180 {
181   ares_destroy((ares_channel)resolver);
182 }
183 
184 /*
185  * Curl_resolver_duphandle()
186  *
187  * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
188  * environment ('resolver' member of the UrlState structure).  Duplicates the
189  * 'from' ares channel and passes the resulting channel to the 'to' pointer.
190  */
Curl_resolver_duphandle(struct Curl_easy * easy,void ** to,void * from)191 CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
192 {
193   (void)from;
194   /*
195    * it would be better to call ares_dup instead, but right now
196    * it is not possible to set 'sock_state_cb_data' outside of
197    * ares_init_options
198    */
199   return Curl_resolver_init(easy, to);
200 }
201 
202 static void destroy_async_data(struct Curl_async *async);
203 
204 /*
205  * Cancel all possibly still on-going resolves for this connection.
206  */
Curl_resolver_cancel(struct Curl_easy * data)207 void Curl_resolver_cancel(struct Curl_easy *data)
208 {
209   DEBUGASSERT(data);
210   if(data->state.async.resolver)
211     ares_cancel((ares_channel)data->state.async.resolver);
212   destroy_async_data(&data->state.async);
213 }
214 
215 /*
216  * We're equivalent to Curl_resolver_cancel() for the c-ares resolver.  We
217  * never block.
218  */
Curl_resolver_kill(struct Curl_easy * data)219 void Curl_resolver_kill(struct Curl_easy *data)
220 {
221   /* We don't need to check the resolver state because we can be called safely
222      at any time and we always do the same thing. */
223   Curl_resolver_cancel(data);
224 }
225 
226 /*
227  * destroy_async_data() cleans up async resolver data.
228  */
destroy_async_data(struct Curl_async * async)229 static void destroy_async_data(struct Curl_async *async)
230 {
231   free(async->hostname);
232 
233   if(async->tdata) {
234     struct thread_data *res = async->tdata;
235     if(res) {
236       if(res->temp_ai) {
237         Curl_freeaddrinfo(res->temp_ai);
238         res->temp_ai = NULL;
239       }
240       free(res);
241     }
242     async->tdata = NULL;
243   }
244 
245   async->hostname = NULL;
246 }
247 
248 /*
249  * Curl_resolver_getsock() is called when someone from the outside world
250  * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
251  * with ares. The caller must make sure that this function is only called when
252  * we have a working ares channel.
253  *
254  * Returns: sockets-in-use-bitmap
255  */
256 
Curl_resolver_getsock(struct Curl_easy * data,curl_socket_t * socks)257 int Curl_resolver_getsock(struct Curl_easy *data,
258                           curl_socket_t *socks)
259 {
260   struct timeval maxtime;
261   struct timeval timebuf;
262   struct timeval *timeout;
263   long milli;
264   int max = ares_getsock((ares_channel)data->state.async.resolver,
265                          (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
266 
267   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
268   maxtime.tv_usec = 0;
269 
270   timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
271                          &timebuf);
272   milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
273   if(milli == 0)
274     milli += 10;
275   Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
276 
277   return max;
278 }
279 
280 /*
281  * waitperform()
282  *
283  * 1) Ask ares what sockets it currently plays with, then
284  * 2) wait for the timeout period to check for action on ares' sockets.
285  * 3) tell ares to act on all the sockets marked as "with action"
286  *
287  * return number of sockets it worked on
288  */
289 
waitperform(struct Curl_easy * data,timediff_t timeout_ms)290 static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
291 {
292   int nfds;
293   int bitmask;
294   ares_socket_t socks[ARES_GETSOCK_MAXNUM];
295   struct pollfd pfd[ARES_GETSOCK_MAXNUM];
296   int i;
297   int num = 0;
298 
299   bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
300                          ARES_GETSOCK_MAXNUM);
301 
302   for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
303     pfd[i].events = 0;
304     pfd[i].revents = 0;
305     if(ARES_GETSOCK_READABLE(bitmask, i)) {
306       pfd[i].fd = socks[i];
307       pfd[i].events |= POLLRDNORM|POLLIN;
308     }
309     if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
310       pfd[i].fd = socks[i];
311       pfd[i].events |= POLLWRNORM|POLLOUT;
312     }
313     if(pfd[i].events)
314       num++;
315     else
316       break;
317   }
318 
319   if(num)
320     nfds = Curl_poll(pfd, num, timeout_ms);
321   else
322     nfds = 0;
323 
324   if(!nfds)
325     /* Call ares_process() unconditonally here, even if we simply timed out
326        above, as otherwise the ares name resolve won't timeout! */
327     ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
328                     ARES_SOCKET_BAD);
329   else {
330     /* move through the descriptors and ask for processing on them */
331     for(i = 0; i < num; i++)
332       ares_process_fd((ares_channel)data->state.async.resolver,
333                       (pfd[i].revents & (POLLRDNORM|POLLIN))?
334                       pfd[i].fd:ARES_SOCKET_BAD,
335                       (pfd[i].revents & (POLLWRNORM|POLLOUT))?
336                       pfd[i].fd:ARES_SOCKET_BAD);
337   }
338   return nfds;
339 }
340 
341 /*
342  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
343  * name resolve request has completed. It should also make sure to time-out if
344  * the operation seems to take too long.
345  *
346  * Returns normal CURLcode errors.
347  */
Curl_resolver_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dns)348 CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
349                                    struct Curl_dns_entry **dns)
350 {
351   struct thread_data *res = data->state.async.tdata;
352   CURLcode result = CURLE_OK;
353 
354   DEBUGASSERT(dns);
355   *dns = NULL;
356 
357   waitperform(data, 0);
358 
359   /* Now that we've checked for any last minute results above, see if there are
360      any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
361      expires. */
362   if(res
363      && res->num_pending
364      /* This is only set to non-zero if the timer was started. */
365      && (res->happy_eyeballs_dns_time.tv_sec
366          || res->happy_eyeballs_dns_time.tv_usec)
367      && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
368          >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
369     /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
370        running. */
371     memset(
372       &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
373 
374     /* Cancel the raw c-ares request, which will fire query_completed_cb() with
375        ARES_ECANCELLED synchronously for all pending responses.  This will
376        leave us with res->num_pending == 0, which is perfect for the next
377        block. */
378     ares_cancel((ares_channel)data->state.async.resolver);
379     DEBUGASSERT(res->num_pending == 0);
380   }
381 
382   if(res && !res->num_pending) {
383     (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
384     /* temp_ai ownership is moved to the connection, so we need not free-up
385        them */
386     res->temp_ai = NULL;
387 
388     if(!data->state.async.dns)
389       result = Curl_resolver_error(data);
390     else
391       *dns = data->state.async.dns;
392 
393     destroy_async_data(&data->state.async);
394   }
395 
396   return result;
397 }
398 
399 /*
400  * Curl_resolver_wait_resolv()
401  *
402  * Waits for a resolve to finish. This function should be avoided since using
403  * this risk getting the multi interface to "hang".
404  *
405  * 'entry' MUST be non-NULL.
406  *
407  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
408  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
409  */
Curl_resolver_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry)410 CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
411                                    struct Curl_dns_entry **entry)
412 {
413   CURLcode result = CURLE_OK;
414   timediff_t timeout;
415   struct curltime now = Curl_now();
416 
417   DEBUGASSERT(entry);
418   *entry = NULL; /* clear on entry */
419 
420   timeout = Curl_timeleft(data, &now, TRUE);
421   if(timeout < 0) {
422     /* already expired! */
423     connclose(data->conn, "Timed out before name resolve started");
424     return CURLE_OPERATION_TIMEDOUT;
425   }
426   if(!timeout)
427     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
428 
429   /* Wait for the name resolve query to complete. */
430   while(!result) {
431     struct timeval *tvp, tv, store;
432     int itimeout;
433     timediff_t timeout_ms;
434 
435 #if TIMEDIFF_T_MAX > INT_MAX
436     itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
437 #else
438     itimeout = (int)timeout;
439 #endif
440 
441     store.tv_sec = itimeout/1000;
442     store.tv_usec = (itimeout%1000)*1000;
443 
444     tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
445 
446     /* use the timeout period ares returned to us above if less than one
447        second is left, otherwise just use 1000ms to make sure the progress
448        callback gets called frequent enough */
449     if(!tvp->tv_sec)
450       timeout_ms = (timediff_t)(tvp->tv_usec/1000);
451     else
452       timeout_ms = 1000;
453 
454     waitperform(data, timeout_ms);
455     result = Curl_resolver_is_resolved(data, entry);
456 
457     if(result || data->state.async.done)
458       break;
459 
460     if(Curl_pgrsUpdate(data))
461       result = CURLE_ABORTED_BY_CALLBACK;
462     else {
463       struct curltime now2 = Curl_now();
464       timediff_t timediff = Curl_timediff(now2, now); /* spent time */
465       if(timediff <= 0)
466         timeout -= 1; /* always deduct at least 1 */
467       else if(timediff > timeout)
468         timeout = -1;
469       else
470         timeout -= timediff;
471       now = now2; /* for next loop */
472     }
473     if(timeout < 0)
474       result = CURLE_OPERATION_TIMEDOUT;
475   }
476   if(result)
477     /* failure, so we cancel the ares operation */
478     ares_cancel((ares_channel)data->state.async.resolver);
479 
480   /* Operation complete, if the lookup was successful we now have the entry
481      in the cache. */
482   if(entry)
483     *entry = data->state.async.dns;
484 
485   if(result)
486     /* close the connection, since we can't return failure here without
487        cleaning up this connection properly. */
488     connclose(data->conn, "c-ares resolve failed");
489 
490   return result;
491 }
492 
493 /* Connects results to the list */
compound_results(struct thread_data * res,struct Curl_addrinfo * ai)494 static void compound_results(struct thread_data *res,
495                              struct Curl_addrinfo *ai)
496 {
497   if(!ai)
498     return;
499 
500 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
501   if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
502     /* We have results already, put the new IPv6 entries at the head of the
503        list. */
504     struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
505 
506     while(temp_ai_tail->ai_next)
507       temp_ai_tail = temp_ai_tail->ai_next;
508 
509     temp_ai_tail->ai_next = ai;
510   }
511   else
512 #endif /* CURLRES_IPV6 */
513   {
514     /* Add the new results to the list of old results. */
515     struct Curl_addrinfo *ai_tail = ai;
516     while(ai_tail->ai_next)
517       ai_tail = ai_tail->ai_next;
518 
519     ai_tail->ai_next = res->temp_ai;
520     res->temp_ai = ai;
521   }
522 }
523 
524 /*
525  * ares_query_completed_cb() is the callback that ares will call when
526  * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
527  * when using ares, is completed either successfully or with failure.
528  */
query_completed_cb(void * arg,int status,int timeouts,struct hostent * hostent)529 static void query_completed_cb(void *arg,  /* (struct connectdata *) */
530                                int status,
531 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
532                                int timeouts,
533 #endif
534                                struct hostent *hostent)
535 {
536   struct Curl_easy *data = (struct Curl_easy *)arg;
537   struct thread_data *res;
538 
539 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
540   (void)timeouts; /* ignored */
541 #endif
542 
543   if(ARES_EDESTRUCTION == status)
544     /* when this ares handle is getting destroyed, the 'arg' pointer may not
545        be valid so only defer it when we know the 'status' says its fine! */
546     return;
547 
548   res = data->state.async.tdata;
549   if(res) {
550     res->num_pending--;
551 
552     if(CURL_ASYNC_SUCCESS == status) {
553       struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
554       if(ai) {
555         compound_results(res, ai);
556       }
557     }
558     /* A successful result overwrites any previous error */
559     if(res->last_status != ARES_SUCCESS)
560       res->last_status = status;
561 
562     /* If there are responses still pending, we presume they must be the
563        complementary IPv4 or IPv6 lookups that we started in parallel in
564        Curl_resolver_getaddrinfo() (for Happy Eyeballs).  If we've got a
565        "definitive" response from one of a set of parallel queries, we need to
566        think about how long we're willing to wait for more responses. */
567     if(res->num_pending
568        /* Only these c-ares status values count as "definitive" for these
569           purposes.  For example, ARES_ENODATA is what we expect when there is
570           no IPv6 entry for a domain name, and that's not a reason to get more
571           aggressive in our timeouts for the other response.  Other errors are
572           either a result of bad input (which should affect all parallel
573           requests), local or network conditions, non-definitive server
574           responses, or us cancelling the request. */
575        && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
576       /* Right now, there can only be up to two parallel queries, so don't
577          bother handling any other cases. */
578       DEBUGASSERT(res->num_pending == 1);
579 
580       /* It's possible that one of these parallel queries could succeed
581          quickly, but the other could always fail or timeout (when we're
582          talking to a pool of DNS servers that can only successfully resolve
583          IPv4 address, for example).
584 
585          It's also possible that the other request could always just take
586          longer because it needs more time or only the second DNS server can
587          fulfill it successfully.  But, to align with the philosophy of Happy
588          Eyeballs, we don't want to wait _too_ long or users will think
589          requests are slow when IPv6 lookups don't actually work (but IPv4 ones
590          do).
591 
592          So, now that we have a usable answer (some IPv4 addresses, some IPv6
593          addresses, or "no such domain"), we start a timeout for the remaining
594          pending responses.  Even though it is typical that this resolved
595          request came back quickly, that needn't be the case.  It might be that
596          this completing request didn't get a result from the first DNS server
597          or even the first round of the whole DNS server pool.  So it could
598          already be quite some time after we issued the DNS queries in the
599          first place.  Without modifying c-ares, we can't know exactly where in
600          its retry cycle we are.  We could guess based on how much time has
601          gone by, but it doesn't really matter.  Happy Eyeballs tells us that,
602          given usable information in hand, we simply don't want to wait "too
603          much longer" after we get a result.
604 
605          We simply wait an additional amount of time equal to the default
606          c-ares query timeout.  That is enough time for a typical parallel
607          response to arrive without being "too long".  Even on a network
608          where one of the two types of queries is failing or timing out
609          constantly, this will usually mean we wait a total of the default
610          c-ares timeout (5 seconds) plus the round trip time for the successful
611          request, which seems bearable.  The downside is that c-ares might race
612          with us to issue one more retry just before we give up, but it seems
613          better to "waste" that request instead of trying to guess the perfect
614          timeout to prevent it.  After all, we don't even know where in the
615          c-ares retry cycle each request is.
616       */
617       res->happy_eyeballs_dns_time = Curl_now();
618       Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
619                   EXPIRE_HAPPY_EYEBALLS_DNS);
620     }
621   }
622 }
623 
624 /*
625  * Curl_resolver_getaddrinfo() - when using ares
626  *
627  * Returns name information about the given hostname and port number. If
628  * successful, the 'hostent' is returned and the forth argument will point to
629  * memory we need to free after use. That memory *MUST* be freed with
630  * Curl_freeaddrinfo(), nothing else.
631  */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)632 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
633                                                 const char *hostname,
634                                                 int port,
635                                                 int *waitp)
636 {
637   char *bufp;
638 
639   *waitp = 0; /* default to synchronous response */
640 
641   bufp = strdup(hostname);
642   if(bufp) {
643     struct thread_data *res = NULL;
644     free(data->state.async.hostname);
645     data->state.async.hostname = bufp;
646     data->state.async.port = port;
647     data->state.async.done = FALSE;   /* not done */
648     data->state.async.status = 0;     /* clear */
649     data->state.async.dns = NULL;     /* clear */
650     res = calloc(sizeof(struct thread_data), 1);
651     if(!res) {
652       free(data->state.async.hostname);
653       data->state.async.hostname = NULL;
654       return NULL;
655     }
656     data->state.async.tdata = res;
657 
658     /* initial status - failed */
659     res->last_status = ARES_ENOTFOUND;
660 
661 #if ARES_VERSION >= 0x010601
662     /* IPv6 supported by c-ares since 1.6.1 */
663     if(Curl_ipv6works(data)) {
664       /* The stack seems to be IPv6-enabled */
665       res->num_pending = 2;
666 
667       /* areschannel is already setup in the Curl_open() function */
668       ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
669                           PF_INET, query_completed_cb, data);
670       ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
671                           PF_INET6, query_completed_cb, data);
672     }
673     else
674 #endif /* ARES_VERSION >= 0x010601 */
675     {
676       res->num_pending = 1;
677 
678       /* areschannel is already setup in the Curl_open() function */
679       ares_gethostbyname((ares_channel)data->state.async.resolver,
680                          hostname, PF_INET,
681                          query_completed_cb, data);
682     }
683 
684     *waitp = 1; /* expect asynchronous response */
685   }
686   return NULL; /* no struct yet */
687 }
688 
Curl_set_dns_servers(struct Curl_easy * data,char * servers)689 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
690                               char *servers)
691 {
692   CURLcode result = CURLE_NOT_BUILT_IN;
693   int ares_result;
694 
695   /* If server is NULL or empty, this would purge all DNS servers
696    * from ares library, which will cause any and all queries to fail.
697    * So, just return OK if none are configured and don't actually make
698    * any changes to c-ares.  This lets c-ares use it's defaults, which
699    * it gets from the OS (for instance from /etc/resolv.conf on Linux).
700    */
701   if(!(servers && servers[0]))
702     return CURLE_OK;
703 
704 #if (ARES_VERSION >= 0x010704)
705 #if (ARES_VERSION >= 0x010b00)
706   ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
707                                            servers);
708 #else
709   ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
710 #endif
711   switch(ares_result) {
712   case ARES_SUCCESS:
713     result = CURLE_OK;
714     break;
715   case ARES_ENOMEM:
716     result = CURLE_OUT_OF_MEMORY;
717     break;
718   case ARES_ENOTINITIALIZED:
719   case ARES_ENODATA:
720   case ARES_EBADSTR:
721   default:
722     result = CURLE_BAD_FUNCTION_ARGUMENT;
723     break;
724   }
725 #else /* too old c-ares version! */
726   (void)data;
727   (void)(ares_result);
728 #endif
729   return result;
730 }
731 
Curl_set_dns_interface(struct Curl_easy * data,const char * interf)732 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
733                                 const char *interf)
734 {
735 #if (ARES_VERSION >= 0x010704)
736   if(!interf)
737     interf = "";
738 
739   ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
740 
741   return CURLE_OK;
742 #else /* c-ares version too old! */
743   (void)data;
744   (void)interf;
745   return CURLE_NOT_BUILT_IN;
746 #endif
747 }
748 
Curl_set_dns_local_ip4(struct Curl_easy * data,const char * local_ip4)749 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
750                                 const char *local_ip4)
751 {
752 #if (ARES_VERSION >= 0x010704)
753   struct in_addr a4;
754 
755   if((!local_ip4) || (local_ip4[0] == 0)) {
756     a4.s_addr = 0; /* disabled: do not bind to a specific address */
757   }
758   else {
759     if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
760       return CURLE_BAD_FUNCTION_ARGUMENT;
761     }
762   }
763 
764   ares_set_local_ip4((ares_channel)data->state.async.resolver,
765                      ntohl(a4.s_addr));
766 
767   return CURLE_OK;
768 #else /* c-ares version too old! */
769   (void)data;
770   (void)local_ip4;
771   return CURLE_NOT_BUILT_IN;
772 #endif
773 }
774 
Curl_set_dns_local_ip6(struct Curl_easy * data,const char * local_ip6)775 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
776                                 const char *local_ip6)
777 {
778 #if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
779   unsigned char a6[INET6_ADDRSTRLEN];
780 
781   if((!local_ip6) || (local_ip6[0] == 0)) {
782     /* disabled: do not bind to a specific address */
783     memset(a6, 0, sizeof(a6));
784   }
785   else {
786     if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
787       return CURLE_BAD_FUNCTION_ARGUMENT;
788     }
789   }
790 
791   ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
792 
793   return CURLE_OK;
794 #else /* c-ares version too old! */
795   (void)data;
796   (void)local_ip6;
797   return CURLE_NOT_BUILT_IN;
798 #endif
799 }
800 #endif /* CURLRES_ARES */
801