1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
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 **********************************************************************/
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
52 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
54 #define in_addr_t unsigned long
64 #include "inet_pton.h"
70 # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
72 # define CARES_STATICLIB
75 # include <ares_version.h> /* really old c-ares didn't include this by
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
83 #if ARES_VERSION >= 0x010601
84 /* IPv6 supported since 1.6.1 */
85 #define HAVE_CARES_IPV6 1
88 #if ARES_VERSION >= 0x010704
89 #define HAVE_CARES_SERVERS_CSV 1
90 #define HAVE_CARES_LOCAL_DEV 1
91 #define HAVE_CARES_SET_LOCAL 1
94 #if ARES_VERSION >= 0x010b00
95 #define HAVE_CARES_PORTS_CSV 1
98 #if ARES_VERSION >= 0x011000
99 /* 1.16.0 or later has ares_getaddrinfo */
100 #define HAVE_CARES_GETADDRINFO 1
103 /* The last 3 #include files should be in this order */
104 #include "curl_printf.h"
105 #include "curl_memory.h"
106 #include "memdebug.h"
109 int num_pending; /* number of outstanding c-ares requests */
110 struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
113 #ifndef HAVE_CARES_GETADDRINFO
114 struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
118 /* How long we are willing to wait for additional parallel responses after
119 obtaining a "definitive" one.
121 This is intended to equal the c-ares default timeout. cURL always uses that
122 default value. Unfortunately, c-ares doesn't expose its default timeout in
123 its API, but it is officially documented as 5 seconds.
125 See query_completed_cb() for an explanation of how this is used.
127 #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
130 * Curl_resolver_global_init() - the generic low-level asynchronous name
131 * resolve API. Called from curl_global_init() to initialize global resolver
132 * environment. Initializes ares library.
134 int Curl_resolver_global_init(void)
136 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
137 if(ares_library_init(ARES_LIB_INIT_ALL)) {
138 return CURLE_FAILED_INIT;
145 * Curl_resolver_global_cleanup()
147 * Called from curl_global_cleanup() to destroy global resolver environment.
148 * Deinitializes ares library.
150 void Curl_resolver_global_cleanup(void)
152 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
153 ares_library_cleanup();
158 static void sock_state_cb(void *data, ares_socket_t socket_fd,
159 int readable, int writable)
161 struct Curl_easy *easy = data;
162 if(!readable && !writable) {
164 Curl_multi_closed(easy, socket_fd);
169 * Curl_resolver_init()
171 * Called from curl_easy_init() -> Curl_open() to initialize resolver
172 * URL-state specific environment ('resolver' member of the UrlState
173 * structure). Fills the passed pointer by the initialized ares_channel.
175 CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
178 struct ares_options options;
179 int optmask = ARES_OPT_SOCK_STATE_CB;
180 options.sock_state_cb = sock_state_cb;
181 options.sock_state_cb_data = easy;
182 status = ares_init_options((ares_channel*)resolver, &options, optmask);
183 if(status != ARES_SUCCESS) {
184 if(status == ARES_ENOMEM)
185 return CURLE_OUT_OF_MEMORY;
187 return CURLE_FAILED_INIT;
190 /* make sure that all other returns from this function should destroy the
191 ares channel before returning error! */
195 * Curl_resolver_cleanup()
197 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
198 * URL-state specific environment ('resolver' member of the UrlState
199 * structure). Destroys the ares channel.
201 void Curl_resolver_cleanup(void *resolver)
203 ares_destroy((ares_channel)resolver);
207 * Curl_resolver_duphandle()
209 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
210 * environment ('resolver' member of the UrlState structure). Duplicates the
211 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
213 CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
217 * it would be better to call ares_dup instead, but right now
218 * it is not possible to set 'sock_state_cb_data' outside of
221 return Curl_resolver_init(easy, to);
224 static void destroy_async_data(struct Curl_async *async);
227 * Cancel all possibly still on-going resolves for this connection.
229 void Curl_resolver_cancel(struct Curl_easy *data)
232 if(data->state.async.resolver)
233 ares_cancel((ares_channel)data->state.async.resolver);
234 destroy_async_data(&data->state.async);
238 * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We
241 void Curl_resolver_kill(struct Curl_easy *data)
243 /* We don't need to check the resolver state because we can be called safely
244 at any time and we always do the same thing. */
245 Curl_resolver_cancel(data);
249 * destroy_async_data() cleans up async resolver data.
251 static void destroy_async_data(struct Curl_async *async)
253 free(async->hostname);
256 struct thread_data *res = async->tdata;
259 Curl_freeaddrinfo(res->temp_ai);
267 async->hostname = NULL;
271 * Curl_resolver_getsock() is called when someone from the outside world
272 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
273 * with ares. The caller must make sure that this function is only called when
274 * we have a working ares channel.
276 * Returns: sockets-in-use-bitmap
279 int Curl_resolver_getsock(struct Curl_easy *data,
280 curl_socket_t *socks)
282 struct timeval maxtime;
283 struct timeval timebuf;
284 struct timeval *timeout;
286 int max = ares_getsock((ares_channel)data->state.async.resolver,
287 (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
289 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
292 timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
294 milli = (long)curlx_tvtoms(timeout);
297 Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
305 * 1) Ask ares what sockets it currently plays with, then
306 * 2) wait for the timeout period to check for action on ares' sockets.
307 * 3) tell ares to act on all the sockets marked as "with action"
309 * return number of sockets it worked on
312 static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
316 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
317 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
321 bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
322 ARES_GETSOCK_MAXNUM);
324 for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
327 if(ARES_GETSOCK_READABLE(bitmask, i)) {
328 pfd[i].fd = socks[i];
329 pfd[i].events |= POLLRDNORM|POLLIN;
331 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
332 pfd[i].fd = socks[i];
333 pfd[i].events |= POLLWRNORM|POLLOUT;
342 nfds = Curl_poll(pfd, num, timeout_ms);
347 /* Call ares_process() unconditionally here, even if we simply timed out
348 above, as otherwise the ares name resolve won't timeout! */
349 ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
352 /* move through the descriptors and ask for processing on them */
353 for(i = 0; i < num; i++)
354 ares_process_fd((ares_channel)data->state.async.resolver,
355 (pfd[i].revents & (POLLRDNORM|POLLIN))?
356 pfd[i].fd:ARES_SOCKET_BAD,
357 (pfd[i].revents & (POLLWRNORM|POLLOUT))?
358 pfd[i].fd:ARES_SOCKET_BAD);
364 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
365 * name resolve request has completed. It should also make sure to time-out if
366 * the operation seems to take too long.
368 * Returns normal CURLcode errors.
370 CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
371 struct Curl_dns_entry **dns)
373 struct thread_data *res = data->state.async.tdata;
374 CURLcode result = CURLE_OK;
379 waitperform(data, 0);
381 #ifndef HAVE_CARES_GETADDRINFO
382 /* Now that we've checked for any last minute results above, see if there are
383 any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
387 /* This is only set to non-zero if the timer was started. */
388 && (res->happy_eyeballs_dns_time.tv_sec
389 || res->happy_eyeballs_dns_time.tv_usec)
390 && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
391 >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
392 /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
395 &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
397 /* Cancel the raw c-ares request, which will fire query_completed_cb() with
398 ARES_ECANCELLED synchronously for all pending responses. This will
399 leave us with res->num_pending == 0, which is perfect for the next
401 ares_cancel((ares_channel)data->state.async.resolver);
402 DEBUGASSERT(res->num_pending == 0);
406 if(res && !res->num_pending) {
407 (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
408 /* temp_ai ownership is moved to the connection, so we need not free-up
412 if(!data->state.async.dns)
413 result = Curl_resolver_error(data);
415 *dns = data->state.async.dns;
417 destroy_async_data(&data->state.async);
424 * Curl_resolver_wait_resolv()
426 * Waits for a resolve to finish. This function should be avoided since using
427 * this risk getting the multi interface to "hang".
429 * 'entry' MUST be non-NULL.
431 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
432 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
434 CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
435 struct Curl_dns_entry **entry)
437 CURLcode result = CURLE_OK;
439 struct curltime now = Curl_now();
442 *entry = NULL; /* clear on entry */
444 timeout = Curl_timeleft(data, &now, TRUE);
446 /* already expired! */
447 connclose(data->conn, "Timed out before name resolve started");
448 return CURLE_OPERATION_TIMEDOUT;
451 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
453 /* Wait for the name resolve query to complete. */
455 struct timeval *tvp, tv, store;
457 timediff_t timeout_ms;
459 #if TIMEDIFF_T_MAX > INT_MAX
460 itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
462 itimeout = (int)timeout;
465 store.tv_sec = itimeout/1000;
466 store.tv_usec = (itimeout%1000)*1000;
468 tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
470 /* use the timeout period ares returned to us above if less than one
471 second is left, otherwise just use 1000ms to make sure the progress
472 callback gets called frequent enough */
474 timeout_ms = (timediff_t)(tvp->tv_usec/1000);
478 waitperform(data, timeout_ms);
479 result = Curl_resolver_is_resolved(data, entry);
481 if(result || data->state.async.done)
484 if(Curl_pgrsUpdate(data))
485 result = CURLE_ABORTED_BY_CALLBACK;
487 struct curltime now2 = Curl_now();
488 timediff_t timediff = Curl_timediff(now2, now); /* spent time */
490 timeout -= 1; /* always deduct at least 1 */
491 else if(timediff > timeout)
495 now = now2; /* for next loop */
498 result = CURLE_OPERATION_TIMEDOUT;
501 /* failure, so we cancel the ares operation */
502 ares_cancel((ares_channel)data->state.async.resolver);
504 /* Operation complete, if the lookup was successful we now have the entry
507 *entry = data->state.async.dns;
510 /* close the connection, since we can't return failure here without
511 cleaning up this connection properly. */
512 connclose(data->conn, "c-ares resolve failed");
517 #ifndef HAVE_CARES_GETADDRINFO
519 /* Connects results to the list */
520 static void compound_results(struct thread_data *res,
521 struct Curl_addrinfo *ai)
526 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
527 if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
528 /* We have results already, put the new IPv6 entries at the head of the
530 struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
532 while(temp_ai_tail->ai_next)
533 temp_ai_tail = temp_ai_tail->ai_next;
535 temp_ai_tail->ai_next = ai;
538 #endif /* CURLRES_IPV6 */
540 /* Add the new results to the list of old results. */
541 struct Curl_addrinfo *ai_tail = ai;
542 while(ai_tail->ai_next)
543 ai_tail = ai_tail->ai_next;
545 ai_tail->ai_next = res->temp_ai;
551 * ares_query_completed_cb() is the callback that ares will call when
552 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
553 * when using ares, is completed either successfully or with failure.
555 static void query_completed_cb(void *arg, /* (struct connectdata *) */
557 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
560 struct hostent *hostent)
562 struct Curl_easy *data = (struct Curl_easy *)arg;
563 struct thread_data *res;
565 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
566 (void)timeouts; /* ignored */
569 if(ARES_EDESTRUCTION == status)
570 /* when this ares handle is getting destroyed, the 'arg' pointer may not
571 be valid so only defer it when we know the 'status' says its fine! */
574 res = data->state.async.tdata;
578 if(CURL_ASYNC_SUCCESS == status) {
579 struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
581 compound_results(res, ai);
584 /* A successful result overwrites any previous error */
585 if(res->last_status != ARES_SUCCESS)
586 res->last_status = status;
588 /* If there are responses still pending, we presume they must be the
589 complementary IPv4 or IPv6 lookups that we started in parallel in
590 Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a
591 "definitive" response from one of a set of parallel queries, we need to
592 think about how long we're willing to wait for more responses. */
594 /* Only these c-ares status values count as "definitive" for these
595 purposes. For example, ARES_ENODATA is what we expect when there is
596 no IPv6 entry for a domain name, and that's not a reason to get more
597 aggressive in our timeouts for the other response. Other errors are
598 either a result of bad input (which should affect all parallel
599 requests), local or network conditions, non-definitive server
600 responses, or us cancelling the request. */
601 && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
602 /* Right now, there can only be up to two parallel queries, so don't
603 bother handling any other cases. */
604 DEBUGASSERT(res->num_pending == 1);
606 /* It's possible that one of these parallel queries could succeed
607 quickly, but the other could always fail or timeout (when we're
608 talking to a pool of DNS servers that can only successfully resolve
609 IPv4 address, for example).
611 It's also possible that the other request could always just take
612 longer because it needs more time or only the second DNS server can
613 fulfill it successfully. But, to align with the philosophy of Happy
614 Eyeballs, we don't want to wait _too_ long or users will think
615 requests are slow when IPv6 lookups don't actually work (but IPv4 ones
618 So, now that we have a usable answer (some IPv4 addresses, some IPv6
619 addresses, or "no such domain"), we start a timeout for the remaining
620 pending responses. Even though it is typical that this resolved
621 request came back quickly, that needn't be the case. It might be that
622 this completing request didn't get a result from the first DNS server
623 or even the first round of the whole DNS server pool. So it could
624 already be quite some time after we issued the DNS queries in the
625 first place. Without modifying c-ares, we can't know exactly where in
626 its retry cycle we are. We could guess based on how much time has
627 gone by, but it doesn't really matter. Happy Eyeballs tells us that,
628 given usable information in hand, we simply don't want to wait "too
629 much longer" after we get a result.
631 We simply wait an additional amount of time equal to the default
632 c-ares query timeout. That is enough time for a typical parallel
633 response to arrive without being "too long". Even on a network
634 where one of the two types of queries is failing or timing out
635 constantly, this will usually mean we wait a total of the default
636 c-ares timeout (5 seconds) plus the round trip time for the successful
637 request, which seems bearable. The downside is that c-ares might race
638 with us to issue one more retry just before we give up, but it seems
639 better to "waste" that request instead of trying to guess the perfect
640 timeout to prevent it. After all, we don't even know where in the
641 c-ares retry cycle each request is.
643 res->happy_eyeballs_dns_time = Curl_now();
644 Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
645 EXPIRE_HAPPY_EYEBALLS_DNS);
650 /* c-ares 1.16.0 or later */
653 * ares2addr() converts an address list provided by c-ares to an internal
654 * libcurl compatible list
656 static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
658 /* traverse the ares_addrinfo_node list */
659 struct ares_addrinfo_node *ai;
660 struct Curl_addrinfo *cafirst = NULL;
661 struct Curl_addrinfo *calast = NULL;
664 for(ai = node; ai != NULL; ai = ai->ai_next) {
666 struct Curl_addrinfo *ca;
667 /* ignore elements with unsupported address family, */
668 /* settle family-specific sockaddr structure size. */
669 if(ai->ai_family == AF_INET)
670 ss_size = sizeof(struct sockaddr_in);
672 else if(ai->ai_family == AF_INET6)
673 ss_size = sizeof(struct sockaddr_in6);
678 /* ignore elements without required address info */
679 if(!ai->ai_addr || !(ai->ai_addrlen > 0))
682 /* ignore elements with bogus address size */
683 if((size_t)ai->ai_addrlen < ss_size)
686 ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
692 /* copy each structure member individually, member ordering, */
693 /* size, or padding might be different for each platform. */
695 ca->ai_flags = ai->ai_flags;
696 ca->ai_family = ai->ai_family;
697 ca->ai_socktype = ai->ai_socktype;
698 ca->ai_protocol = ai->ai_protocol;
699 ca->ai_addrlen = (curl_socklen_t)ss_size;
701 ca->ai_canonname = NULL;
704 ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
705 memcpy(ca->ai_addr, ai->ai_addr, ss_size);
707 /* if the return list is empty, this becomes the first element */
711 /* add this element last in the return list */
713 calast->ai_next = ca;
717 /* if we failed, destroy the Curl_addrinfo list */
719 Curl_freeaddrinfo(cafirst);
726 static void addrinfo_cb(void *arg, int status, int timeouts,
727 struct ares_addrinfo *result)
729 struct Curl_easy *data = (struct Curl_easy *)arg;
730 struct thread_data *res = data->state.async.tdata;
732 if(ARES_SUCCESS == status) {
733 res->temp_ai = ares2addr(result->nodes);
734 res->last_status = CURL_ASYNC_SUCCESS;
735 ares_freeaddrinfo(result);
742 * Curl_resolver_getaddrinfo() - when using ares
744 * Returns name information about the given hostname and port number. If
745 * successful, the 'hostent' is returned and the forth argument will point to
746 * memory we need to free after use. That memory *MUST* be freed with
747 * Curl_freeaddrinfo(), nothing else.
749 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
750 const char *hostname,
756 *waitp = 0; /* default to synchronous response */
758 bufp = strdup(hostname);
760 struct thread_data *res = NULL;
761 free(data->state.async.hostname);
762 data->state.async.hostname = bufp;
763 data->state.async.port = port;
764 data->state.async.done = FALSE; /* not done */
765 data->state.async.status = 0; /* clear */
766 data->state.async.dns = NULL; /* clear */
767 res = calloc(sizeof(struct thread_data), 1);
769 free(data->state.async.hostname);
770 data->state.async.hostname = NULL;
773 data->state.async.tdata = res;
775 /* initial status - failed */
776 res->last_status = ARES_ENOTFOUND;
778 #ifdef HAVE_CARES_GETADDRINFO
780 struct ares_addrinfo_hints hints;
783 memset(&hints, 0, sizeof(hints));
785 if(Curl_ipv6works(data))
786 /* The stack seems to be IPv6-enabled */
788 #endif /* CURLRES_IPV6 */
789 hints.ai_family = pf;
790 hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
791 SOCK_STREAM : SOCK_DGRAM;
792 msnprintf(service, sizeof(service), "%d", port);
793 res->num_pending = 1;
794 ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
795 service, &hints, addrinfo_cb, data);
799 #ifdef HAVE_CARES_IPV6
800 if(Curl_ipv6works(data)) {
801 /* The stack seems to be IPv6-enabled */
802 res->num_pending = 2;
804 /* areschannel is already setup in the Curl_open() function */
805 ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
806 PF_INET, query_completed_cb, data);
807 ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
808 PF_INET6, query_completed_cb, data);
813 res->num_pending = 1;
815 /* areschannel is already setup in the Curl_open() function */
816 ares_gethostbyname((ares_channel)data->state.async.resolver,
818 query_completed_cb, data);
821 *waitp = 1; /* expect asynchronous response */
823 return NULL; /* no struct yet */
826 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
829 CURLcode result = CURLE_NOT_BUILT_IN;
832 /* If server is NULL or empty, this would purge all DNS servers
833 * from ares library, which will cause any and all queries to fail.
834 * So, just return OK if none are configured and don't actually make
835 * any changes to c-ares. This lets c-ares use it's defaults, which
836 * it gets from the OS (for instance from /etc/resolv.conf on Linux).
838 if(!(servers && servers[0]))
841 #ifdef HAVE_CARES_SERVERS_CSV
842 #ifdef HAVE_CARES_PORTS_CSV
843 ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
846 ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
848 switch(ares_result) {
853 result = CURLE_OUT_OF_MEMORY;
855 case ARES_ENOTINITIALIZED:
859 result = CURLE_BAD_FUNCTION_ARGUMENT;
862 #else /* too old c-ares version! */
869 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
872 #ifdef HAVE_CARES_LOCAL_DEV
876 ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
879 #else /* c-ares version too old! */
882 return CURLE_NOT_BUILT_IN;
886 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
887 const char *local_ip4)
889 #ifdef HAVE_CARES_SET_LOCAL
892 if((!local_ip4) || (local_ip4[0] == 0)) {
893 a4.s_addr = 0; /* disabled: do not bind to a specific address */
896 if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
897 return CURLE_BAD_FUNCTION_ARGUMENT;
901 ares_set_local_ip4((ares_channel)data->state.async.resolver,
905 #else /* c-ares version too old! */
908 return CURLE_NOT_BUILT_IN;
912 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
913 const char *local_ip6)
915 #if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6)
916 unsigned char a6[INET6_ADDRSTRLEN];
918 if((!local_ip6) || (local_ip6[0] == 0)) {
919 /* disabled: do not bind to a specific address */
920 memset(a6, 0, sizeof(a6));
923 if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
924 return CURLE_BAD_FUNCTION_ARGUMENT;
928 ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
931 #else /* c-ares version too old! */
934 return CURLE_NOT_BUILT_IN;
937 #endif /* CURLRES_ARES */