openssl: guard against OOM on context creation
[platform/upstream/curl.git] / lib / asyn-ares.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2020, 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.haxx.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 ResolverResults {
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  */
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  */
128 void Curl_resolver_global_cleanup(void)
129 {
130 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
131   ares_library_cleanup();
132 #endif
133 }
134
135
136 static void Curl_ares_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  */
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 = Curl_ares_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  */
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  */
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  */
207 void Curl_resolver_cancel(struct connectdata *conn)
208 {
209   if(conn->data && conn->data->state.resolver)
210     ares_cancel((ares_channel)conn->data->state.resolver);
211   destroy_async_data(&conn->async);
212 }
213
214 /*
215  * We're equivalent to Curl_resolver_cancel() for the c-ares resolver.  We
216  * never block.
217  */
218 void Curl_resolver_kill(struct connectdata *conn)
219 {
220   /* We don't need to check the resolver state because we can be called safely
221      at any time and we always do the same thing. */
222   Curl_resolver_cancel(conn);
223 }
224
225 /*
226  * destroy_async_data() cleans up async resolver data.
227  */
228 static void destroy_async_data(struct Curl_async *async)
229 {
230   free(async->hostname);
231
232   if(async->os_specific) {
233     struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
234     if(res) {
235       if(res->temp_ai) {
236         Curl_freeaddrinfo(res->temp_ai);
237         res->temp_ai = NULL;
238       }
239       free(res);
240     }
241     async->os_specific = NULL;
242   }
243
244   async->hostname = NULL;
245 }
246
247 /*
248  * Curl_resolver_getsock() is called when someone from the outside world
249  * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
250  * with ares. The caller must make sure that this function is only called when
251  * we have a working ares channel.
252  *
253  * Returns: sockets-in-use-bitmap
254  */
255
256 int Curl_resolver_getsock(struct connectdata *conn,
257                           curl_socket_t *socks)
258 {
259   struct timeval maxtime;
260   struct timeval timebuf;
261   struct timeval *timeout;
262   long milli;
263   int max = ares_getsock((ares_channel)conn->data->state.resolver,
264                          (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
265
266   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
267   maxtime.tv_usec = 0;
268
269   timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
270                          &timebuf);
271   milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
272   if(milli == 0)
273     milli += 10;
274   Curl_expire(conn->data, milli, EXPIRE_ASYNC_NAME);
275
276   return max;
277 }
278
279 /*
280  * waitperform()
281  *
282  * 1) Ask ares what sockets it currently plays with, then
283  * 2) wait for the timeout period to check for action on ares' sockets.
284  * 3) tell ares to act on all the sockets marked as "with action"
285  *
286  * return number of sockets it worked on
287  */
288
289 static int waitperform(struct connectdata *conn, timediff_t timeout_ms)
290 {
291   struct Curl_easy *data = conn->data;
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.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 != 0)
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.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.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  */
348 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
349                                    struct Curl_dns_entry **dns)
350 {
351   struct Curl_easy *data = conn->data;
352   struct ResolverResults *res = (struct ResolverResults *)
353     conn->async.os_specific;
354   CURLcode result = CURLE_OK;
355
356   DEBUGASSERT(dns);
357   *dns = NULL;
358
359   waitperform(conn, 0);
360
361   /* Now that we've checked for any last minute results above, see if there are
362      any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
363      expires. */
364   if(res
365      && res->num_pending
366      /* This is only set to non-zero if the timer was started. */
367      && (res->happy_eyeballs_dns_time.tv_sec
368          || res->happy_eyeballs_dns_time.tv_usec)
369      && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
370          >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
371     /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
372        running. */
373     memset(
374       &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
375
376     /* Cancel the raw c-ares request, which will fire query_completed_cb() with
377        ARES_ECANCELLED synchronously for all pending responses.  This will
378        leave us with res->num_pending == 0, which is perfect for the next
379        block. */
380     ares_cancel((ares_channel)data->state.resolver);
381     DEBUGASSERT(res->num_pending == 0);
382   }
383
384   if(res && !res->num_pending) {
385     (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
386     /* temp_ai ownership is moved to the connection, so we need not free-up
387        them */
388     res->temp_ai = NULL;
389
390     if(!conn->async.dns) {
391       failf(data, "Could not resolve: %s (%s)",
392             conn->async.hostname, ares_strerror(conn->async.status));
393       result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
394         CURLE_COULDNT_RESOLVE_HOST;
395     }
396     else
397       *dns = conn->async.dns;
398
399     destroy_async_data(&conn->async);
400   }
401
402   return result;
403 }
404
405 /*
406  * Curl_resolver_wait_resolv()
407  *
408  * Waits for a resolve to finish. This function should be avoided since using
409  * this risk getting the multi interface to "hang".
410  *
411  * 'entry' MUST be non-NULL.
412  *
413  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
414  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
415  */
416 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
417                                    struct Curl_dns_entry **entry)
418 {
419   CURLcode result = CURLE_OK;
420   struct Curl_easy *data = conn->data;
421   timediff_t timeout;
422   struct curltime now = Curl_now();
423
424   DEBUGASSERT(entry);
425   *entry = NULL; /* clear on entry */
426
427   timeout = Curl_timeleft(data, &now, TRUE);
428   if(timeout < 0) {
429     /* already expired! */
430     connclose(conn, "Timed out before name resolve started");
431     return CURLE_OPERATION_TIMEDOUT;
432   }
433   if(!timeout)
434     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
435
436   /* Wait for the name resolve query to complete. */
437   while(!result) {
438     struct timeval *tvp, tv, store;
439     int itimeout;
440     timediff_t timeout_ms;
441
442 #if TIMEDIFF_T_MAX > INT_MAX
443     itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
444 #else
445     itimeout = (int)timeout;
446 #endif
447
448     store.tv_sec = itimeout/1000;
449     store.tv_usec = (itimeout%1000)*1000;
450
451     tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
452
453     /* use the timeout period ares returned to us above if less than one
454        second is left, otherwise just use 1000ms to make sure the progress
455        callback gets called frequent enough */
456     if(!tvp->tv_sec)
457       timeout_ms = (timediff_t)(tvp->tv_usec/1000);
458     else
459       timeout_ms = 1000;
460
461     waitperform(conn, timeout_ms);
462     result = Curl_resolver_is_resolved(conn, entry);
463
464     if(result || conn->async.done)
465       break;
466
467     if(Curl_pgrsUpdate(conn))
468       result = CURLE_ABORTED_BY_CALLBACK;
469     else {
470       struct curltime now2 = Curl_now();
471       timediff_t timediff = Curl_timediff(now2, now); /* spent time */
472       if(timediff <= 0)
473         timeout -= 1; /* always deduct at least 1 */
474       else if(timediff > timeout)
475         timeout = -1;
476       else
477         timeout -= timediff;
478       now = now2; /* for next loop */
479     }
480     if(timeout < 0)
481       result = CURLE_OPERATION_TIMEDOUT;
482   }
483   if(result)
484     /* failure, so we cancel the ares operation */
485     ares_cancel((ares_channel)data->state.resolver);
486
487   /* Operation complete, if the lookup was successful we now have the entry
488      in the cache. */
489   if(entry)
490     *entry = conn->async.dns;
491
492   if(result)
493     /* close the connection, since we can't return failure here without
494        cleaning up this connection properly. */
495     connclose(conn, "c-ares resolve failed");
496
497   return result;
498 }
499
500 /* Connects results to the list */
501 static void compound_results(struct ResolverResults *res,
502                              struct Curl_addrinfo *ai)
503 {
504   struct Curl_addrinfo *ai_tail;
505   if(!ai)
506     return;
507   ai_tail = ai;
508
509   while(ai_tail->ai_next)
510     ai_tail = ai_tail->ai_next;
511
512   /* Add the new results to the list of old results. */
513   ai_tail->ai_next = res->temp_ai;
514   res->temp_ai = ai;
515 }
516
517 /*
518  * ares_query_completed_cb() is the callback that ares will call when
519  * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
520  * when using ares, is completed either successfully or with failure.
521  */
522 static void query_completed_cb(void *arg,  /* (struct connectdata *) */
523                                int status,
524 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
525                                int timeouts,
526 #endif
527                                struct hostent *hostent)
528 {
529   struct connectdata *conn = (struct connectdata *)arg;
530   struct ResolverResults *res;
531
532 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
533   (void)timeouts; /* ignored */
534 #endif
535
536   if(ARES_EDESTRUCTION == status)
537     /* when this ares handle is getting destroyed, the 'arg' pointer may not
538        be valid so only defer it when we know the 'status' says its fine! */
539     return;
540
541   res = (struct ResolverResults *)conn->async.os_specific;
542   if(res) {
543     res->num_pending--;
544
545     if(CURL_ASYNC_SUCCESS == status) {
546       struct Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
547       if(ai) {
548         compound_results(res, ai);
549       }
550     }
551     /* A successful result overwrites any previous error */
552     if(res->last_status != ARES_SUCCESS)
553       res->last_status = status;
554
555     /* If there are responses still pending, we presume they must be the
556        complementary IPv4 or IPv6 lookups that we started in parallel in
557        Curl_resolver_getaddrinfo() (for Happy Eyeballs).  If we've got a
558        "definitive" response from one of a set of parallel queries, we need to
559        think about how long we're willing to wait for more responses. */
560     if(res->num_pending
561        /* Only these c-ares status values count as "definitive" for these
562           purposes.  For example, ARES_ENODATA is what we expect when there is
563           no IPv6 entry for a domain name, and that's not a reason to get more
564           aggressive in our timeouts for the other response.  Other errors are
565           either a result of bad input (which should affect all parallel
566           requests), local or network conditions, non-definitive server
567           responses, or us cancelling the request. */
568        && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
569       /* Right now, there can only be up to two parallel queries, so don't
570          bother handling any other cases. */
571       DEBUGASSERT(res->num_pending == 1);
572
573       /* It's possible that one of these parallel queries could succeed
574          quickly, but the other could always fail or timeout (when we're
575          talking to a pool of DNS servers that can only successfully resolve
576          IPv4 address, for example).
577
578          It's also possible that the other request could always just take
579          longer because it needs more time or only the second DNS server can
580          fulfill it successfully.  But, to align with the philosophy of Happy
581          Eyeballs, we don't want to wait _too_ long or users will think
582          requests are slow when IPv6 lookups don't actually work (but IPv4 ones
583          do).
584
585          So, now that we have a usable answer (some IPv4 addresses, some IPv6
586          addresses, or "no such domain"), we start a timeout for the remaining
587          pending responses.  Even though it is typical that this resolved
588          request came back quickly, that needn't be the case.  It might be that
589          this completing request didn't get a result from the first DNS server
590          or even the first round of the whole DNS server pool.  So it could
591          already be quite some time after we issued the DNS queries in the
592          first place.  Without modifying c-ares, we can't know exactly where in
593          its retry cycle we are.  We could guess based on how much time has
594          gone by, but it doesn't really matter.  Happy Eyeballs tells us that,
595          given usable information in hand, we simply don't want to wait "too
596          much longer" after we get a result.
597
598          We simply wait an additional amount of time equal to the default
599          c-ares query timeout.  That is enough time for a typical parallel
600          response to arrive without being "too long".  Even on a network
601          where one of the two types of queries is failing or timing out
602          constantly, this will usually mean we wait a total of the default
603          c-ares timeout (5 seconds) plus the round trip time for the successful
604          request, which seems bearable.  The downside is that c-ares might race
605          with us to issue one more retry just before we give up, but it seems
606          better to "waste" that request instead of trying to guess the perfect
607          timeout to prevent it.  After all, we don't even know where in the
608          c-ares retry cycle each request is.
609       */
610       res->happy_eyeballs_dns_time = Curl_now();
611       Curl_expire(
612         conn->data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS);
613     }
614   }
615 }
616
617 /*
618  * Curl_resolver_getaddrinfo() - when using ares
619  *
620  * Returns name information about the given hostname and port number. If
621  * successful, the 'hostent' is returned and the forth argument will point to
622  * memory we need to free after use. That memory *MUST* be freed with
623  * Curl_freeaddrinfo(), nothing else.
624  */
625 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
626                                                 const char *hostname,
627                                                 int port,
628                                                 int *waitp)
629 {
630   char *bufp;
631   struct Curl_easy *data = conn->data;
632   int family = PF_INET;
633
634   *waitp = 0; /* default to synchronous response */
635
636 #ifdef ENABLE_IPV6
637   switch(conn->ip_version) {
638   default:
639 #if ARES_VERSION >= 0x010601
640     family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
641                            c-ares versions this just falls through and defaults
642                            to PF_INET */
643     break;
644 #endif
645   case CURL_IPRESOLVE_V4:
646     family = PF_INET;
647     break;
648   case CURL_IPRESOLVE_V6:
649     family = PF_INET6;
650     break;
651   }
652 #endif /* ENABLE_IPV6 */
653
654   bufp = strdup(hostname);
655   if(bufp) {
656     struct ResolverResults *res = NULL;
657     free(conn->async.hostname);
658     conn->async.hostname = bufp;
659     conn->async.port = port;
660     conn->async.done = FALSE;   /* not done */
661     conn->async.status = 0;     /* clear */
662     conn->async.dns = NULL;     /* clear */
663     res = calloc(sizeof(struct ResolverResults), 1);
664     if(!res) {
665       free(conn->async.hostname);
666       conn->async.hostname = NULL;
667       return NULL;
668     }
669     conn->async.os_specific = res;
670
671     /* initial status - failed */
672     res->last_status = ARES_ENOTFOUND;
673 #ifdef ENABLE_IPV6
674     if(family == PF_UNSPEC) {
675       if(Curl_ipv6works(conn)) {
676         res->num_pending = 2;
677
678         /* areschannel is already setup in the Curl_open() function */
679         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
680                             PF_INET, query_completed_cb, conn);
681         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
682                             PF_INET6, query_completed_cb, conn);
683       }
684       else {
685         res->num_pending = 1;
686
687         /* areschannel is already setup in the Curl_open() function */
688         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
689                             PF_INET, query_completed_cb, conn);
690       }
691     }
692     else
693 #endif /* ENABLE_IPV6 */
694     {
695       res->num_pending = 1;
696
697       /* areschannel is already setup in the Curl_open() function */
698       ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
699                          query_completed_cb, conn);
700     }
701
702     *waitp = 1; /* expect asynchronous response */
703   }
704   return NULL; /* no struct yet */
705 }
706
707 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
708                               char *servers)
709 {
710   CURLcode result = CURLE_NOT_BUILT_IN;
711   int ares_result;
712
713   /* If server is NULL or empty, this would purge all DNS servers
714    * from ares library, which will cause any and all queries to fail.
715    * So, just return OK if none are configured and don't actually make
716    * any changes to c-ares.  This lets c-ares use it's defaults, which
717    * it gets from the OS (for instance from /etc/resolv.conf on Linux).
718    */
719   if(!(servers && servers[0]))
720     return CURLE_OK;
721
722 #if (ARES_VERSION >= 0x010704)
723 #if (ARES_VERSION >= 0x010b00)
724   ares_result = ares_set_servers_ports_csv(data->state.resolver, servers);
725 #else
726   ares_result = ares_set_servers_csv(data->state.resolver, servers);
727 #endif
728   switch(ares_result) {
729   case ARES_SUCCESS:
730     result = CURLE_OK;
731     break;
732   case ARES_ENOMEM:
733     result = CURLE_OUT_OF_MEMORY;
734     break;
735   case ARES_ENOTINITIALIZED:
736   case ARES_ENODATA:
737   case ARES_EBADSTR:
738   default:
739     result = CURLE_BAD_FUNCTION_ARGUMENT;
740     break;
741   }
742 #else /* too old c-ares version! */
743   (void)data;
744   (void)(ares_result);
745 #endif
746   return result;
747 }
748
749 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
750                                 const char *interf)
751 {
752 #if (ARES_VERSION >= 0x010704)
753   if(!interf)
754     interf = "";
755
756   ares_set_local_dev((ares_channel)data->state.resolver, interf);
757
758   return CURLE_OK;
759 #else /* c-ares version too old! */
760   (void)data;
761   (void)interf;
762   return CURLE_NOT_BUILT_IN;
763 #endif
764 }
765
766 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
767                                 const char *local_ip4)
768 {
769 #if (ARES_VERSION >= 0x010704)
770   struct in_addr a4;
771
772   if((!local_ip4) || (local_ip4[0] == 0)) {
773     a4.s_addr = 0; /* disabled: do not bind to a specific address */
774   }
775   else {
776     if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
777       return CURLE_BAD_FUNCTION_ARGUMENT;
778     }
779   }
780
781   ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
782
783   return CURLE_OK;
784 #else /* c-ares version too old! */
785   (void)data;
786   (void)local_ip4;
787   return CURLE_NOT_BUILT_IN;
788 #endif
789 }
790
791 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
792                                 const char *local_ip6)
793 {
794 #if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
795   unsigned char a6[INET6_ADDRSTRLEN];
796
797   if((!local_ip6) || (local_ip6[0] == 0)) {
798     /* disabled: do not bind to a specific address */
799     memset(a6, 0, sizeof(a6));
800   }
801   else {
802     if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
803       return CURLE_BAD_FUNCTION_ARGUMENT;
804     }
805   }
806
807   ares_set_local_ip6((ares_channel)data->state.resolver, a6);
808
809   return CURLE_OK;
810 #else /* c-ares version too old! */
811   (void)data;
812   (void)local_ip6;
813   return CURLE_NOT_BUILT_IN;
814 #endif
815 }
816 #endif /* CURLRES_ARES */