1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, 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.haxx.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"
28 #ifdef HAVE_NETINET_IN_H
29 #include <netinet/in.h>
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
46 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
48 #define in_addr_t unsigned long
51 /***********************************************************************
52 * Only for ares-enabled builds
53 * And only for functions that fulfill the asynch resolver backend API
54 * as defined in asyn.h, nothing else belongs in this file!
55 **********************************************************************/
67 #include "inet_pton.h"
72 # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
73 (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
74 # define CARES_STATICLIB
77 # include <ares_version.h> /* really old c-ares didn't include this by
80 #if ARES_VERSION >= 0x010500
81 /* c-ares 1.5.0 or later, the callback proto is modified */
82 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
85 /* The last 3 #include files should be in this order */
86 #include "curl_printf.h"
87 #include "curl_memory.h"
90 struct ResolverResults {
91 int num_pending; /* number of ares_gethostbyname() requests */
92 Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
97 * Curl_resolver_global_init() - the generic low-level asynchronous name
98 * resolve API. Called from curl_global_init() to initialize global resolver
99 * environment. Initializes ares library.
101 int Curl_resolver_global_init(void)
103 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
104 if(ares_library_init(ARES_LIB_INIT_ALL)) {
105 return CURLE_FAILED_INIT;
112 * Curl_resolver_global_cleanup()
114 * Called from curl_global_cleanup() to destroy global resolver environment.
115 * Deinitializes ares library.
117 void Curl_resolver_global_cleanup(void)
119 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
120 ares_library_cleanup();
125 * Curl_resolver_init()
127 * Called from curl_easy_init() -> Curl_open() to initialize resolver
128 * URL-state specific environment ('resolver' member of the UrlState
129 * structure). Fills the passed pointer by the initialized ares_channel.
131 CURLcode Curl_resolver_init(void **resolver)
133 int status = ares_init((ares_channel*)resolver);
134 if(status != ARES_SUCCESS) {
135 if(status == ARES_ENOMEM)
136 return CURLE_OUT_OF_MEMORY;
138 return CURLE_FAILED_INIT;
141 /* make sure that all other returns from this function should destroy the
142 ares channel before returning error! */
146 * Curl_resolver_cleanup()
148 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
149 * URL-state specific environment ('resolver' member of the UrlState
150 * structure). Destroys the ares channel.
152 void Curl_resolver_cleanup(void *resolver)
154 ares_destroy((ares_channel)resolver);
158 * Curl_resolver_duphandle()
160 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
161 * environment ('resolver' member of the UrlState structure). Duplicates the
162 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
164 int Curl_resolver_duphandle(void **to, void *from)
166 /* Clone the ares channel for the new handle */
167 if(ARES_SUCCESS != ares_dup((ares_channel*)to, (ares_channel)from))
168 return CURLE_FAILED_INIT;
172 static void destroy_async_data (struct Curl_async *async);
175 * Cancel all possibly still on-going resolves for this connection.
177 void Curl_resolver_cancel(struct connectdata *conn)
179 if(conn->data && conn->data->state.resolver)
180 ares_cancel((ares_channel)conn->data->state.resolver);
181 destroy_async_data(&conn->async);
185 * destroy_async_data() cleans up async resolver data.
187 static void destroy_async_data (struct Curl_async *async)
189 free(async->hostname);
191 if(async->os_specific) {
192 struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
195 Curl_freeaddrinfo(res->temp_ai);
200 async->os_specific = NULL;
203 async->hostname = NULL;
207 * Curl_resolver_getsock() is called when someone from the outside world
208 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
209 * with ares. The caller must make sure that this function is only called when
210 * we have a working ares channel.
212 * Returns: sockets-in-use-bitmap
215 int Curl_resolver_getsock(struct connectdata *conn,
216 curl_socket_t *socks,
220 struct timeval maxtime;
221 struct timeval timebuf;
222 struct timeval *timeout;
224 int max = ares_getsock((ares_channel)conn->data->state.resolver,
225 (ares_socket_t *)socks, numsocks);
227 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
230 timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
232 milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
235 Curl_expire_latest(conn->data, milli);
243 * 1) Ask ares what sockets it currently plays with, then
244 * 2) wait for the timeout period to check for action on ares' sockets.
245 * 3) tell ares to act on all the sockets marked as "with action"
247 * return number of sockets it worked on
250 static int waitperform(struct connectdata *conn, int timeout_ms)
252 struct Curl_easy *data = conn->data;
255 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
256 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
260 bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
261 ARES_GETSOCK_MAXNUM);
263 for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
266 if(ARES_GETSOCK_READABLE(bitmask, i)) {
267 pfd[i].fd = socks[i];
268 pfd[i].events |= POLLRDNORM|POLLIN;
270 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
271 pfd[i].fd = socks[i];
272 pfd[i].events |= POLLWRNORM|POLLOUT;
274 if(pfd[i].events != 0)
281 nfds = Curl_poll(pfd, num, timeout_ms);
286 /* Call ares_process() unconditonally here, even if we simply timed out
287 above, as otherwise the ares name resolve won't timeout! */
288 ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
291 /* move through the descriptors and ask for processing on them */
292 for(i=0; i < num; i++)
293 ares_process_fd((ares_channel)data->state.resolver,
294 pfd[i].revents & (POLLRDNORM|POLLIN)?
295 pfd[i].fd:ARES_SOCKET_BAD,
296 pfd[i].revents & (POLLWRNORM|POLLOUT)?
297 pfd[i].fd:ARES_SOCKET_BAD);
303 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
304 * name resolve request has completed. It should also make sure to time-out if
305 * the operation seems to take too long.
307 * Returns normal CURLcode errors.
309 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
310 struct Curl_dns_entry **dns)
312 struct Curl_easy *data = conn->data;
313 struct ResolverResults *res = (struct ResolverResults *)
314 conn->async.os_specific;
315 CURLcode result = CURLE_OK;
319 waitperform(conn, 0);
321 if(res && !res->num_pending) {
322 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
323 /* temp_ai ownership is moved to the connection, so we need not free-up
326 if(!conn->async.dns) {
327 failf(data, "Could not resolve: %s (%s)",
328 conn->async.hostname, ares_strerror(conn->async.status));
329 result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
330 CURLE_COULDNT_RESOLVE_HOST;
333 *dns = conn->async.dns;
335 destroy_async_data(&conn->async);
342 * Curl_resolver_wait_resolv()
344 * waits for a resolve to finish. This function should be avoided since using
345 * this risk getting the multi interface to "hang".
347 * If 'entry' is non-NULL, make it point to the resolved dns entry
349 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
350 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
352 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
353 struct Curl_dns_entry **entry)
355 CURLcode result = CURLE_OK;
356 struct Curl_easy *data = conn->data;
358 struct timeval now = Curl_tvnow();
359 struct Curl_dns_entry *temp_entry;
362 *entry = NULL; /* clear on entry */
364 timeout = Curl_timeleft(data, &now, TRUE);
366 /* already expired! */
367 connclose(conn, "Timed out before name resolve started");
368 return CURLE_OPERATION_TIMEDOUT;
371 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
373 /* Wait for the name resolve query to complete. */
375 struct timeval *tvp, tv, store;
380 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
382 store.tv_sec = itimeout/1000;
383 store.tv_usec = (itimeout%1000)*1000;
385 tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
387 /* use the timeout period ares returned to us above if less than one
388 second is left, otherwise just use 1000ms to make sure the progress
389 callback gets called frequent enough */
391 timeout_ms = (int)(tvp->tv_usec/1000);
395 waitperform(conn, timeout_ms);
396 result = Curl_resolver_is_resolved(conn, &temp_entry);
398 if(result || conn->async.done)
401 if(Curl_pgrsUpdate(conn))
402 result = CURLE_ABORTED_BY_CALLBACK;
404 struct timeval now2 = Curl_tvnow();
405 timediff = Curl_tvdiff(now2, now); /* spent time */
406 timeout -= timediff?timediff:1; /* always deduct at least 1 */
407 now = now2; /* for next loop */
410 result = CURLE_OPERATION_TIMEDOUT;
413 /* failure, so we cancel the ares operation */
414 ares_cancel((ares_channel)data->state.resolver);
416 /* Operation complete, if the lookup was successful we now have the entry
419 *entry = conn->async.dns;
422 /* close the connection, since we can't return failure here without
423 cleaning up this connection properly.
424 TODO: remove this action from here, it is not a name resolver decision.
426 connclose(conn, "c-ares resolve failed");
431 /* Connects results to the list */
432 static void compound_results(struct ResolverResults *res,
435 Curl_addrinfo *ai_tail;
440 while(ai_tail->ai_next)
441 ai_tail = ai_tail->ai_next;
443 /* Add the new results to the list of old results. */
444 ai_tail->ai_next = res->temp_ai;
449 * ares_query_completed_cb() is the callback that ares will call when
450 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
451 * when using ares, is completed either successfully or with failure.
453 static void query_completed_cb(void *arg, /* (struct connectdata *) */
455 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
458 struct hostent *hostent)
460 struct connectdata *conn = (struct connectdata *)arg;
461 struct ResolverResults *res;
463 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
464 (void)timeouts; /* ignored */
467 if(ARES_EDESTRUCTION == status)
468 /* when this ares handle is getting destroyed, the 'arg' pointer may not
469 be valid so only defer it when we know the 'status' says its fine! */
472 res = (struct ResolverResults *)conn->async.os_specific;
475 if(CURL_ASYNC_SUCCESS == status) {
476 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
478 compound_results(res, ai);
481 /* A successful result overwrites any previous error */
482 if(res->last_status != ARES_SUCCESS)
483 res->last_status = status;
487 * Curl_resolver_getaddrinfo() - when using ares
489 * Returns name information about the given hostname and port number. If
490 * successful, the 'hostent' is returned and the forth argument will point to
491 * memory we need to free after use. That memory *MUST* be freed with
492 * Curl_freeaddrinfo(), nothing else.
494 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
495 const char *hostname,
500 struct Curl_easy *data = conn->data;
502 int family = PF_INET;
503 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
505 #endif /* CURLRES_IPV6 */
507 *waitp = 0; /* default to synchronous response */
509 /* First check if this is an IPv4 address string */
510 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
511 /* This is a dotted IP address 123.123.123.123-style */
512 return Curl_ip2addr(AF_INET, &in, hostname, port);
515 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
516 /* Otherwise, check if this is an IPv6 address string */
517 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
518 /* This must be an IPv6 address literal. */
519 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
521 switch(conn->ip_version) {
523 #if ARES_VERSION >= 0x010601
524 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
525 c-ares versions this just falls through and defaults
529 case CURL_IPRESOLVE_V4:
532 case CURL_IPRESOLVE_V6:
536 #endif /* CURLRES_IPV6 */
538 bufp = strdup(hostname);
540 struct ResolverResults *res = NULL;
541 free(conn->async.hostname);
542 conn->async.hostname = bufp;
543 conn->async.port = port;
544 conn->async.done = FALSE; /* not done */
545 conn->async.status = 0; /* clear */
546 conn->async.dns = NULL; /* clear */
547 res = calloc(sizeof(struct ResolverResults), 1);
549 free(conn->async.hostname);
550 conn->async.hostname = NULL;
553 conn->async.os_specific = res;
555 /* initial status - failed */
556 res->last_status = ARES_ENOTFOUND;
557 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
558 if(family == PF_UNSPEC) {
559 if(Curl_ipv6works()) {
560 res->num_pending = 2;
562 /* areschannel is already setup in the Curl_open() function */
563 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
564 PF_INET, query_completed_cb, conn);
565 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
566 PF_INET6, query_completed_cb, conn);
569 res->num_pending = 1;
571 /* areschannel is already setup in the Curl_open() function */
572 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
573 PF_INET, query_completed_cb, conn);
577 #endif /* CURLRES_IPV6 */
579 res->num_pending = 1;
581 /* areschannel is already setup in the Curl_open() function */
582 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
583 query_completed_cb, conn);
586 *waitp = 1; /* expect asynchronous response */
588 return NULL; /* no struct yet */
591 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
594 CURLcode result = CURLE_NOT_BUILT_IN;
597 /* If server is NULL or empty, this would purge all DNS servers
598 * from ares library, which will cause any and all queries to fail.
599 * So, just return OK if none are configured and don't actually make
600 * any changes to c-ares. This lets c-ares use it's defaults, which
601 * it gets from the OS (for instance from /etc/resolv.conf on Linux).
603 if(!(servers && servers[0]))
606 #if (ARES_VERSION >= 0x010704)
607 ares_result = ares_set_servers_csv(data->state.resolver, servers);
608 switch(ares_result) {
613 result = CURLE_OUT_OF_MEMORY;
615 case ARES_ENOTINITIALIZED:
619 result = CURLE_BAD_FUNCTION_ARGUMENT;
622 #else /* too old c-ares version! */
629 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
632 #if (ARES_VERSION >= 0x010704)
636 ares_set_local_dev((ares_channel)data->state.resolver, interf);
639 #else /* c-ares version too old! */
642 return CURLE_NOT_BUILT_IN;
646 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
647 const char *local_ip4)
649 #if (ARES_VERSION >= 0x010704)
652 if((!local_ip4) || (local_ip4[0] == 0)) {
653 a4.s_addr = 0; /* disabled: do not bind to a specific address */
656 if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
657 return CURLE_BAD_FUNCTION_ARGUMENT;
661 ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
664 #else /* c-ares version too old! */
667 return CURLE_NOT_BUILT_IN;
671 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
672 const char *local_ip6)
674 #if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
675 unsigned char a6[INET6_ADDRSTRLEN];
677 if((!local_ip6) || (local_ip6[0] == 0)) {
678 /* disabled: do not bind to a specific address */
679 memset(a6, 0, sizeof(a6));
682 if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
683 return CURLE_BAD_FUNCTION_ARGUMENT;
687 ares_set_local_ip6((ares_channel)data->state.resolver, a6);
690 #else /* c-ares version too old! */
693 return CURLE_NOT_BUILT_IN;
696 #endif /* CURLRES_ARES */