1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, 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;
321 waitperform(conn, 0);
323 if(res && !res->num_pending) {
324 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
325 /* temp_ai ownership is moved to the connection, so we need not free-up
328 destroy_async_data(&conn->async);
329 if(!conn->async.dns) {
330 failf(data, "Could not resolve %s: %s (%s)",
331 conn->bits.proxy?"proxy":"host",
333 ares_strerror(conn->async.status));
334 return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
335 CURLE_COULDNT_RESOLVE_HOST;
337 *dns = conn->async.dns;
344 * Curl_resolver_wait_resolv()
346 * waits for a resolve to finish. This function should be avoided since using
347 * this risk getting the multi interface to "hang".
349 * If 'entry' is non-NULL, make it point to the resolved dns entry
351 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
352 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
354 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
355 struct Curl_dns_entry **entry)
357 CURLcode rc=CURLE_OK;
358 struct SessionHandle *data = conn->data;
360 struct timeval now = Curl_tvnow();
361 struct Curl_dns_entry *temp_entry;
363 timeout = Curl_timeleft(data, &now, TRUE);
365 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
367 /* Wait for the name resolve query to complete. */
369 struct timeval *tvp, tv, store;
374 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
376 store.tv_sec = itimeout/1000;
377 store.tv_usec = (itimeout%1000)*1000;
379 tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
381 /* use the timeout period ares returned to us above if less than one
382 second is left, otherwise just use 1000ms to make sure the progress
383 callback gets called frequent enough */
385 timeout_ms = (int)(tvp->tv_usec/1000);
389 waitperform(conn, timeout_ms);
390 Curl_resolver_is_resolved(conn,&temp_entry);
395 if(Curl_pgrsUpdate(conn)) {
396 rc = CURLE_ABORTED_BY_CALLBACK;
397 timeout = -1; /* trigger the cancel below */
400 struct timeval now2 = Curl_tvnow();
401 timediff = Curl_tvdiff(now2, now); /* spent time */
402 timeout -= timediff?timediff:1; /* always deduct at least 1 */
403 now = now2; /* for next loop */
406 /* our timeout, so we cancel the ares operation */
407 ares_cancel((ares_channel)data->state.resolver);
412 /* Operation complete, if the lookup was successful we now have the entry
416 *entry = conn->async.dns;
418 if(!conn->async.dns) {
419 /* a name was not resolved */
420 if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
421 if(conn->bits.proxy) {
422 failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname);
423 rc = CURLE_COULDNT_RESOLVE_PROXY;
426 failf(data, "Resolving host timed out: %s", conn->host.dispname);
427 rc = CURLE_COULDNT_RESOLVE_HOST;
430 else if(conn->async.done) {
431 if(conn->bits.proxy) {
432 failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname,
433 ares_strerror(conn->async.status));
434 rc = CURLE_COULDNT_RESOLVE_PROXY;
437 failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
438 ares_strerror(conn->async.status));
439 rc = CURLE_COULDNT_RESOLVE_HOST;
443 rc = CURLE_OPERATION_TIMEDOUT;
445 /* close the connection, since we can't return failure here without
446 cleaning up this connection properly */
447 conn->bits.close = TRUE;
453 /* Connects results to the list */
454 static void compound_results(struct ResolverResults *res,
457 Curl_addrinfo *ai_tail;
462 while(ai_tail->ai_next)
463 ai_tail = ai_tail->ai_next;
465 /* Add the new results to the list of old results. */
466 ai_tail->ai_next = res->temp_ai;
471 * ares_query_completed_cb() is the callback that ares will call when
472 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
473 * when using ares, is completed either successfully or with failure.
475 static void query_completed_cb(void *arg, /* (struct connectdata *) */
477 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
480 struct hostent *hostent)
482 struct connectdata *conn = (struct connectdata *)arg;
483 struct ResolverResults *res;
485 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
486 (void)timeouts; /* ignored */
489 if(ARES_EDESTRUCTION == status)
490 /* when this ares handle is getting destroyed, the 'arg' pointer may not
491 be valid so only defer it when we know the 'status' says its fine! */
494 res = (struct ResolverResults *)conn->async.os_specific;
497 if(CURL_ASYNC_SUCCESS == status) {
498 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
500 compound_results(res, ai);
503 /* A successful result overwrites any previous error */
504 if(res->last_status != ARES_SUCCESS)
505 res->last_status = status;
509 * Curl_resolver_getaddrinfo() - when using ares
511 * Returns name information about the given hostname and port number. If
512 * successful, the 'hostent' is returned and the forth argument will point to
513 * memory we need to free after use. That memory *MUST* be freed with
514 * Curl_freeaddrinfo(), nothing else.
516 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
517 const char *hostname,
522 struct SessionHandle *data = conn->data;
524 int family = PF_INET;
525 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
527 #endif /* CURLRES_IPV6 */
529 *waitp = 0; /* default to synchronous response */
531 /* First check if this is an IPv4 address string */
532 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
533 /* This is a dotted IP address 123.123.123.123-style */
534 return Curl_ip2addr(AF_INET, &in, hostname, port);
537 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
538 /* Otherwise, check if this is an IPv6 address string */
539 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
540 /* This must be an IPv6 address literal. */
541 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
543 switch(conn->ip_version) {
545 #if ARES_VERSION >= 0x010601
546 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
547 c-ares versions this just falls through and defaults
551 case CURL_IPRESOLVE_V4:
554 case CURL_IPRESOLVE_V6:
558 #endif /* CURLRES_IPV6 */
560 bufp = strdup(hostname);
562 struct ResolverResults *res = NULL;
563 Curl_safefree(conn->async.hostname);
564 conn->async.hostname = bufp;
565 conn->async.port = port;
566 conn->async.done = FALSE; /* not done */
567 conn->async.status = 0; /* clear */
568 conn->async.dns = NULL; /* clear */
569 res = calloc(sizeof(struct ResolverResults),1);
571 Curl_safefree(conn->async.hostname);
572 conn->async.hostname = NULL;
575 conn->async.os_specific = res;
577 /* initial status - failed */
578 res->last_status = ARES_ENOTFOUND;
579 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
580 if(family == PF_UNSPEC) {
581 if(Curl_ipv6works()) {
582 res->num_pending = 2;
584 /* areschannel is already setup in the Curl_open() function */
585 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
586 PF_INET, query_completed_cb, conn);
587 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
588 PF_INET6, query_completed_cb, conn);
591 res->num_pending = 1;
593 /* areschannel is already setup in the Curl_open() function */
594 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
595 PF_INET, query_completed_cb, conn);
599 #endif /* CURLRES_IPV6 */
601 res->num_pending = 1;
603 /* areschannel is already setup in the Curl_open() function */
604 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
605 query_completed_cb, conn);
608 *waitp = 1; /* expect asynchronous response */
610 return NULL; /* no struct yet */
613 CURLcode Curl_set_dns_servers(struct SessionHandle *data,
616 CURLcode result = CURLE_NOT_BUILT_IN;
617 #if (ARES_VERSION >= 0x010704)
618 int ares_result = ares_set_servers_csv(data->state.resolver, servers);
619 switch(ares_result) {
624 result = CURLE_OUT_OF_MEMORY;
626 case ARES_ENOTINITIALIZED:
630 result = CURLE_BAD_FUNCTION_ARGUMENT;
633 #else /* too old c-ares version! */
639 #endif /* CURLRES_ARES */