1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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 http://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 #define _MPRINTF_REPLACE /* use our functions only */
73 #include <curl/mprintf.h>
75 # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
76 (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
77 # define CARES_STATICLIB
80 # include <ares_version.h> /* really old c-ares didn't include this by
83 #if ARES_VERSION >= 0x010500
84 /* c-ares 1.5.0 or later, the callback proto is modified */
85 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
88 #include "curl_memory.h"
89 /* The last #include file should be: */
92 struct ResolverResults {
93 int num_pending; /* number of ares_gethostbyname() requests */
94 Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
99 * Curl_resolver_global_init() - the generic low-level asynchronous name
100 * resolve API. Called from curl_global_init() to initialize global resolver
101 * environment. Initializes ares library.
103 int Curl_resolver_global_init(void)
105 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
106 if(ares_library_init(ARES_LIB_INIT_ALL)) {
107 return CURLE_FAILED_INIT;
114 * Curl_resolver_global_cleanup()
116 * Called from curl_global_cleanup() to destroy global resolver environment.
117 * Deinitializes ares library.
119 void Curl_resolver_global_cleanup(void)
121 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
122 ares_library_cleanup();
127 * Curl_resolver_init()
129 * Called from curl_easy_init() -> Curl_open() to initialize resolver
130 * URL-state specific environment ('resolver' member of the UrlState
131 * structure). Fills the passed pointer by the initialized ares_channel.
133 CURLcode Curl_resolver_init(void **resolver)
135 int status = ares_init((ares_channel*)resolver);
136 if(status != ARES_SUCCESS) {
137 if(status == ARES_ENOMEM)
138 return CURLE_OUT_OF_MEMORY;
140 return CURLE_FAILED_INIT;
143 /* make sure that all other returns from this function should destroy the
144 ares channel before returning error! */
148 * Curl_resolver_cleanup()
150 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
151 * URL-state specific environment ('resolver' member of the UrlState
152 * structure). Destroys the ares channel.
154 void Curl_resolver_cleanup(void *resolver)
156 ares_destroy((ares_channel)resolver);
160 * Curl_resolver_duphandle()
162 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
163 * environment ('resolver' member of the UrlState structure). Duplicates the
164 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
166 int Curl_resolver_duphandle(void **to, void *from)
168 /* Clone the ares channel for the new handle */
169 if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from))
170 return CURLE_FAILED_INIT;
174 static void destroy_async_data (struct Curl_async *async);
177 * Cancel all possibly still on-going resolves for this connection.
179 void Curl_resolver_cancel(struct connectdata *conn)
181 if(conn && conn->data && conn->data->state.resolver)
182 ares_cancel((ares_channel)conn->data->state.resolver);
183 destroy_async_data(&conn->async);
187 * destroy_async_data() cleans up async resolver data.
189 static void destroy_async_data (struct Curl_async *async)
192 free(async->hostname);
194 if(async->os_specific) {
195 struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
198 Curl_freeaddrinfo(res->temp_ai);
203 async->os_specific = NULL;
206 async->hostname = NULL;
210 * Curl_resolver_getsock() is called when someone from the outside world
211 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
212 * with ares. The caller must make sure that this function is only called when
213 * we have a working ares channel.
215 * Returns: sockets-in-use-bitmap
218 int Curl_resolver_getsock(struct connectdata *conn,
219 curl_socket_t *socks,
223 struct timeval maxtime;
224 struct timeval timebuf;
225 struct timeval *timeout;
227 int max = ares_getsock((ares_channel)conn->data->state.resolver,
228 (ares_socket_t *)socks, numsocks);
230 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
233 timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
235 milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
238 Curl_expire(conn->data, milli);
246 * 1) Ask ares what sockets it currently plays with, then
247 * 2) wait for the timeout period to check for action on ares' sockets.
248 * 3) tell ares to act on all the sockets marked as "with action"
250 * return number of sockets it worked on
253 static int waitperform(struct connectdata *conn, int timeout_ms)
255 struct SessionHandle *data = conn->data;
258 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
259 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
263 bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
264 ARES_GETSOCK_MAXNUM);
266 for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
269 if(ARES_GETSOCK_READABLE(bitmask, i)) {
270 pfd[i].fd = socks[i];
271 pfd[i].events |= POLLRDNORM|POLLIN;
273 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
274 pfd[i].fd = socks[i];
275 pfd[i].events |= POLLWRNORM|POLLOUT;
277 if(pfd[i].events != 0)
284 nfds = Curl_poll(pfd, num, timeout_ms);
289 /* Call ares_process() unconditonally here, even if we simply timed out
290 above, as otherwise the ares name resolve won't timeout! */
291 ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
294 /* move through the descriptors and ask for processing on them */
295 for(i=0; i < num; i++)
296 ares_process_fd((ares_channel)data->state.resolver,
297 pfd[i].revents & (POLLRDNORM|POLLIN)?
298 pfd[i].fd:ARES_SOCKET_BAD,
299 pfd[i].revents & (POLLWRNORM|POLLOUT)?
300 pfd[i].fd:ARES_SOCKET_BAD);
306 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
307 * name resolve request has completed. It should also make sure to time-out if
308 * the operation seems to take too long.
310 * Returns normal CURLcode errors.
312 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
313 struct Curl_dns_entry **dns)
315 struct SessionHandle *data = conn->data;
316 struct ResolverResults *res = (struct ResolverResults *)
317 conn->async.os_specific;
318 CURLcode rc = CURLE_OK;
322 waitperform(conn, 0);
324 if(res && !res->num_pending) {
325 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
326 /* temp_ai ownership is moved to the connection, so we need not free-up
329 if(!conn->async.dns) {
330 failf(data, "Could not resolve: %s (%s)",
331 conn->async.hostname, ares_strerror(conn->async.status));
332 rc = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
333 CURLE_COULDNT_RESOLVE_HOST;
336 *dns = conn->async.dns;
338 destroy_async_data(&conn->async);
345 * Curl_resolver_wait_resolv()
347 * waits for a resolve to finish. This function should be avoided since using
348 * this risk getting the multi interface to "hang".
350 * If 'entry' is non-NULL, make it point to the resolved dns entry
352 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
353 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
355 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
356 struct Curl_dns_entry **entry)
358 CURLcode rc=CURLE_OK;
359 struct SessionHandle *data = conn->data;
361 struct timeval now = Curl_tvnow();
362 struct Curl_dns_entry *temp_entry;
364 timeout = Curl_timeleft(data, &now, TRUE);
366 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
368 /* Wait for the name resolve query to complete. */
370 struct timeval *tvp, tv, store;
375 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
377 store.tv_sec = itimeout/1000;
378 store.tv_usec = (itimeout%1000)*1000;
380 tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
382 /* use the timeout period ares returned to us above if less than one
383 second is left, otherwise just use 1000ms to make sure the progress
384 callback gets called frequent enough */
386 timeout_ms = (int)(tvp->tv_usec/1000);
390 waitperform(conn, timeout_ms);
391 Curl_resolver_is_resolved(conn,&temp_entry);
396 if(Curl_pgrsUpdate(conn)) {
397 rc = CURLE_ABORTED_BY_CALLBACK;
398 timeout = -1; /* trigger the cancel below */
401 struct timeval now2 = Curl_tvnow();
402 timediff = Curl_tvdiff(now2, now); /* spent time */
403 timeout -= timediff?timediff:1; /* always deduct at least 1 */
404 now = now2; /* for next loop */
407 /* our timeout, so we cancel the ares operation */
408 ares_cancel((ares_channel)data->state.resolver);
413 /* Operation complete, if the lookup was successful we now have the entry
417 *entry = conn->async.dns;
420 /* close the connection, since we can't return failure here without
421 cleaning up this connection properly.
422 TODO: remove this action from here, it is not a name resolver decision.
424 connclose(conn, "c-ares resolve failed");
429 /* Connects results to the list */
430 static void compound_results(struct ResolverResults *res,
433 Curl_addrinfo *ai_tail;
438 while(ai_tail->ai_next)
439 ai_tail = ai_tail->ai_next;
441 /* Add the new results to the list of old results. */
442 ai_tail->ai_next = res->temp_ai;
447 * ares_query_completed_cb() is the callback that ares will call when
448 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
449 * when using ares, is completed either successfully or with failure.
451 static void query_completed_cb(void *arg, /* (struct connectdata *) */
453 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
456 struct hostent *hostent)
458 struct connectdata *conn = (struct connectdata *)arg;
459 struct ResolverResults *res;
461 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
462 (void)timeouts; /* ignored */
465 if(ARES_EDESTRUCTION == status)
466 /* when this ares handle is getting destroyed, the 'arg' pointer may not
467 be valid so only defer it when we know the 'status' says its fine! */
470 res = (struct ResolverResults *)conn->async.os_specific;
473 if(CURL_ASYNC_SUCCESS == status) {
474 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
476 compound_results(res, ai);
479 /* A successful result overwrites any previous error */
480 if(res->last_status != ARES_SUCCESS)
481 res->last_status = status;
485 * Curl_resolver_getaddrinfo() - when using ares
487 * Returns name information about the given hostname and port number. If
488 * successful, the 'hostent' is returned and the forth argument will point to
489 * memory we need to free after use. That memory *MUST* be freed with
490 * Curl_freeaddrinfo(), nothing else.
492 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
493 const char *hostname,
498 struct SessionHandle *data = conn->data;
500 int family = PF_INET;
501 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
503 #endif /* CURLRES_IPV6 */
505 *waitp = 0; /* default to synchronous response */
507 /* First check if this is an IPv4 address string */
508 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
509 /* This is a dotted IP address 123.123.123.123-style */
510 return Curl_ip2addr(AF_INET, &in, hostname, port);
513 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
514 /* Otherwise, check if this is an IPv6 address string */
515 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
516 /* This must be an IPv6 address literal. */
517 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
519 switch(conn->ip_version) {
521 #if ARES_VERSION >= 0x010601
522 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
523 c-ares versions this just falls through and defaults
527 case CURL_IPRESOLVE_V4:
530 case CURL_IPRESOLVE_V6:
534 #endif /* CURLRES_IPV6 */
536 bufp = strdup(hostname);
538 struct ResolverResults *res = NULL;
539 Curl_safefree(conn->async.hostname);
540 conn->async.hostname = bufp;
541 conn->async.port = port;
542 conn->async.done = FALSE; /* not done */
543 conn->async.status = 0; /* clear */
544 conn->async.dns = NULL; /* clear */
545 res = calloc(sizeof(struct ResolverResults),1);
547 Curl_safefree(conn->async.hostname);
548 conn->async.hostname = NULL;
551 conn->async.os_specific = res;
553 /* initial status - failed */
554 res->last_status = ARES_ENOTFOUND;
555 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
556 if(family == PF_UNSPEC) {
557 if(Curl_ipv6works()) {
558 res->num_pending = 2;
560 /* areschannel is already setup in the Curl_open() function */
561 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
562 PF_INET, query_completed_cb, conn);
563 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
564 PF_INET6, query_completed_cb, conn);
567 res->num_pending = 1;
569 /* areschannel is already setup in the Curl_open() function */
570 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
571 PF_INET, query_completed_cb, conn);
575 #endif /* CURLRES_IPV6 */
577 res->num_pending = 1;
579 /* areschannel is already setup in the Curl_open() function */
580 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
581 query_completed_cb, conn);
584 *waitp = 1; /* expect asynchronous response */
586 return NULL; /* no struct yet */
589 CURLcode Curl_set_dns_servers(struct SessionHandle *data,
592 CURLcode result = CURLE_NOT_BUILT_IN;
595 /* If server is NULL or empty, this would purge all DNS servers
596 * from ares library, which will cause any and all queries to fail.
597 * So, just return OK if none are configured and don't actually make
598 * any changes to c-ares. This lets c-ares use it's defaults, which
599 * it gets from the OS (for instance from /etc/resolv.conf on Linux).
601 if(!(servers && servers[0]))
604 #if (ARES_VERSION >= 0x010704)
605 ares_result = ares_set_servers_csv(data->state.resolver, servers);
606 switch(ares_result) {
611 result = CURLE_OUT_OF_MEMORY;
613 case ARES_ENOTINITIALIZED:
617 result = CURLE_BAD_FUNCTION_ARGUMENT;
620 #else /* too old c-ares version! */
627 CURLcode Curl_set_dns_interface(struct SessionHandle *data,
630 #if (ARES_VERSION >= 0x010704)
634 ares_set_local_dev((ares_channel)data->state.resolver, interf);
637 #else /* c-ares version too old! */
640 return CURLE_NOT_BUILT_IN;
644 CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data,
645 const char *local_ip4)
647 #if (ARES_VERSION >= 0x010704)
650 if((!local_ip4) || (local_ip4[0] == 0)) {
651 a4.s_addr = 0; /* disabled: do not bind to a specific address */
654 if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
655 return CURLE_BAD_FUNCTION_ARGUMENT;
659 ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
662 #else /* c-ares version too old! */
665 return CURLE_NOT_BUILT_IN;
669 CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data,
670 const char *local_ip6)
672 #if (ARES_VERSION >= 0x010704)
673 unsigned char a6[INET6_ADDRSTRLEN];
675 if((!local_ip6) || (local_ip6[0] == 0)) {
676 /* disabled: do not bind to a specific address */
677 memset(a6, 0, sizeof(a6));
680 if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
681 return CURLE_BAD_FUNCTION_ARGUMENT;
685 ares_set_local_ip6((ares_channel)data->state.resolver, a6);
688 #else /* c-ares version too old! */
691 return CURLE_NOT_BUILT_IN;
694 #endif /* CURLRES_ARES */