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