1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2011, 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 ***************************************************************************/
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
43 #include <stdlib.h> /* required for free() prototypes */
46 #include <unistd.h> /* for the close() proto */
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
60 #define in_addr_t unsigned long
63 /***********************************************************************
64 * Only for ares-enabled builds
65 * And only for functions that fulfill the asynch resolver backend API
66 * as defined in asyn.h, nothing else belongs in this file!
67 **********************************************************************/
79 #include "inet_pton.h"
84 #define _MPRINTF_REPLACE /* use our functions only */
85 #include <curl/mprintf.h>
87 # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
88 (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
89 # define CARES_STATICLIB
93 #if ARES_VERSION >= 0x010500
94 /* c-ares 1.5.0 or later, the callback proto is modified */
95 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
98 #include "curl_memory.h"
99 /* The last #include file should be: */
100 #include "memdebug.h"
102 struct ResolverResults {
103 int num_pending; /* number of ares_gethostbyname() requests */
104 Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
109 * Curl_resolver_global_init() - the generic low-level asynchronous name
110 * resolve API. Called from curl_global_init() to initialize global resolver
111 * environment. Initializes ares library.
113 int Curl_resolver_global_init(void)
115 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
116 if(ares_library_init(ARES_LIB_INIT_ALL)) {
117 return CURLE_FAILED_INIT;
124 * Curl_resolver_global_cleanup()
126 * Called from curl_global_cleanup() to destroy global resolver environment.
127 * Deinitializes ares library.
129 void Curl_resolver_global_cleanup(void)
131 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
132 ares_library_cleanup();
137 * Curl_resolver_init()
139 * Called from curl_easy_init() -> Curl_open() to initialize resolver
140 * URL-state specific environment ('resolver' member of the UrlState
141 * structure). Fills the passed pointer by the initialized ares_channel.
143 CURLcode Curl_resolver_init(void **resolver)
145 int status = ares_init((ares_channel*)resolver);
146 if(status != ARES_SUCCESS) {
147 if(status == ARES_ENOMEM)
148 return CURLE_OUT_OF_MEMORY;
150 return CURLE_FAILED_INIT;
153 /* make sure that all other returns from this function should destroy the
154 ares channel before returning error! */
158 * Curl_resolver_cleanup()
160 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
161 * URL-state specific environment ('resolver' member of the UrlState
162 * structure). Destroys the ares channel.
164 void Curl_resolver_cleanup(void *resolver)
166 ares_destroy((ares_channel)resolver);
170 * Curl_resolver_duphandle()
172 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
173 * environment ('resolver' member of the UrlState structure). Duplicates the
174 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
176 int Curl_resolver_duphandle(void **to, void *from)
178 /* Clone the ares channel for the new handle */
179 if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from))
180 return CURLE_FAILED_INIT;
184 static void destroy_async_data (struct Curl_async *async);
187 * Cancel all possibly still on-going resolves for this connection.
189 void Curl_resolver_cancel(struct connectdata *conn)
191 if(conn && conn->data && conn->data->state.resolver)
192 ares_cancel((ares_channel)conn->data->state.resolver);
193 destroy_async_data(&conn->async);
197 * destroy_async_data() cleans up async resolver data.
199 static void destroy_async_data (struct Curl_async *async)
202 free(async->hostname);
204 if(async->os_specific) {
205 struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
208 Curl_freeaddrinfo(res->temp_ai);
213 async->os_specific = NULL;
216 async->hostname = NULL;
220 * Curl_resolver_fdset() is called when someone from the outside world (using
221 * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
222 * ares. The caller must make sure that this function is only called when we
223 * have a working ares channel.
225 * Returns: CURLE_OK always!
228 int Curl_resolver_getsock(struct connectdata *conn,
229 curl_socket_t *socks,
233 struct timeval maxtime;
234 struct timeval timebuf;
235 struct timeval *timeout;
236 int max = ares_getsock((ares_channel)conn->data->state.resolver,
237 (ares_socket_t *)socks, numsocks);
240 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
243 timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
246 Curl_expire(conn->data,
247 (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
255 * 1) Ask ares what sockets it currently plays with, then
256 * 2) wait for the timeout period to check for action on ares' sockets.
257 * 3) tell ares to act on all the sockets marked as "with action"
259 * return number of sockets it worked on
262 static int waitperform(struct connectdata *conn, int timeout_ms)
264 struct SessionHandle *data = conn->data;
267 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
268 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
272 bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
273 ARES_GETSOCK_MAXNUM);
275 for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
278 if(ARES_GETSOCK_READABLE(bitmask, i)) {
279 pfd[i].fd = socks[i];
280 pfd[i].events |= POLLRDNORM|POLLIN;
282 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
283 pfd[i].fd = socks[i];
284 pfd[i].events |= POLLWRNORM|POLLOUT;
286 if(pfd[i].events != 0)
293 nfds = Curl_poll(pfd, num, timeout_ms);
298 /* Call ares_process() unconditonally here, even if we simply timed out
299 above, as otherwise the ares name resolve won't timeout! */
300 ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
303 /* move through the descriptors and ask for processing on them */
304 for(i=0; i < num; i++)
305 ares_process_fd((ares_channel)data->state.resolver,
306 pfd[i].revents & (POLLRDNORM|POLLIN)?
307 pfd[i].fd:ARES_SOCKET_BAD,
308 pfd[i].revents & (POLLWRNORM|POLLOUT)?
309 pfd[i].fd:ARES_SOCKET_BAD);
315 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
316 * name resolve request has completed. It should also make sure to time-out if
317 * the operation seems to take too long.
319 * Returns normal CURLcode errors.
321 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
322 struct Curl_dns_entry **dns)
324 struct SessionHandle *data = conn->data;
325 struct ResolverResults *res = (struct ResolverResults *)
326 conn->async.os_specific;
330 waitperform(conn, 0);
332 if(res && !res->num_pending) {
333 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
334 /* temp_ai ownership is moved to the connection, so we need not free-up
337 destroy_async_data(&conn->async);
338 if(!conn->async.dns) {
339 failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
340 ares_strerror(conn->async.status));
341 return CURLE_COULDNT_RESOLVE_HOST;
343 *dns = conn->async.dns;
350 * Curl_resolver_wait_resolv()
352 * waits for a resolve to finish. This function should be avoided since using
353 * this risk getting the multi interface to "hang".
355 * If 'entry' is non-NULL, make it point to the resolved dns entry
357 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
358 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
360 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
361 struct Curl_dns_entry **entry)
363 CURLcode rc=CURLE_OK;
364 struct SessionHandle *data = conn->data;
366 struct timeval now = Curl_tvnow();
367 struct Curl_dns_entry *temp_entry;
369 timeout = Curl_timeleft(data, &now, TRUE);
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 Curl_resolver_is_resolved(conn,&temp_entry);
401 if(Curl_pgrsUpdate(conn)) {
402 rc = CURLE_ABORTED_BY_CALLBACK;
403 timeout = -1; /* trigger the cancel below */
406 struct timeval now2 = Curl_tvnow();
407 timediff = Curl_tvdiff(now2, now); /* spent time */
408 timeout -= timediff?timediff:1; /* always deduct at least 1 */
409 now = now2; /* for next loop */
412 /* our timeout, so we cancel the ares operation */
413 ares_cancel((ares_channel)data->state.resolver);
418 /* Operation complete, if the lookup was successful we now have the entry
422 *entry = conn->async.dns;
424 if(!conn->async.dns) {
425 /* a name was not resolved */
426 if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
427 if(conn->bits.httpproxy) {
428 failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname);
429 rc = CURLE_COULDNT_RESOLVE_PROXY;
432 failf(data, "Resolving host timed out: %s", conn->host.dispname);
433 rc = CURLE_COULDNT_RESOLVE_HOST;
436 else if(conn->async.done) {
437 if(conn->bits.httpproxy) {
438 failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname,
439 ares_strerror(conn->async.status));
440 rc = CURLE_COULDNT_RESOLVE_PROXY;
443 failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
444 ares_strerror(conn->async.status));
445 rc = CURLE_COULDNT_RESOLVE_HOST;
449 rc = CURLE_OPERATION_TIMEDOUT;
451 /* close the connection, since we can't return failure here without
452 cleaning up this connection properly */
453 conn->bits.close = TRUE;
459 /* Connects results to the list */
460 static void compound_results(struct ResolverResults *res,
463 Curl_addrinfo *ai_tail;
468 while(ai_tail->ai_next)
469 ai_tail = ai_tail->ai_next;
471 /* Add the new results to the list of old results. */
472 ai_tail->ai_next = res->temp_ai;
477 * ares_query_completed_cb() is the callback that ares will call when
478 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
479 * when using ares, is completed either successfully or with failure.
481 static void query_completed_cb(void *arg, /* (struct connectdata *) */
483 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
486 struct hostent *hostent)
488 struct connectdata *conn = (struct connectdata *)arg;
489 struct ResolverResults *res;
491 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
492 (void)timeouts; /* ignored */
495 if(ARES_EDESTRUCTION == status)
496 /* when this ares handle is getting destroyed, the 'arg' pointer may not
497 be valid so only defer it when we know the 'status' says its fine! */
500 res = (struct ResolverResults *)conn->async.os_specific;
503 if(CURL_ASYNC_SUCCESS == status) {
504 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
506 compound_results(res, ai);
509 /* A successful result overwrites any previous error */
510 if(res->last_status != ARES_SUCCESS)
511 res->last_status = status;
515 * Curl_resolver_getaddrinfo() - when using ares
517 * Returns name information about the given hostname and port number. If
518 * successful, the 'hostent' is returned and the forth argument will point to
519 * memory we need to free after use. That memory *MUST* be freed with
520 * Curl_freeaddrinfo(), nothing else.
522 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
523 const char *hostname,
528 struct SessionHandle *data = conn->data;
530 int family = PF_INET;
531 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
533 #endif /* CURLRES_IPV6 */
535 *waitp = 0; /* default to synchronous response */
537 /* First check if this is an IPv4 address string */
538 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
539 /* This is a dotted IP address 123.123.123.123-style */
540 return Curl_ip2addr(AF_INET, &in, hostname, port);
543 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
544 /* Otherwise, check if this is an IPv6 address string */
545 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
546 /* This must be an IPv6 address literal. */
547 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
549 switch(conn->ip_version) {
551 #if ARES_VERSION >= 0x010601
552 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
553 c-ares versions this just falls through and defaults
557 case CURL_IPRESOLVE_V4:
560 case CURL_IPRESOLVE_V6:
564 #endif /* CURLRES_IPV6 */
566 bufp = strdup(hostname);
568 struct ResolverResults *res = NULL;
569 Curl_safefree(conn->async.hostname);
570 conn->async.hostname = bufp;
571 conn->async.port = port;
572 conn->async.done = FALSE; /* not done */
573 conn->async.status = 0; /* clear */
574 conn->async.dns = NULL; /* clear */
575 res = (struct ResolverResults *)calloc(sizeof(struct ResolverResults),1);
577 Curl_safefree(conn->async.hostname);
578 conn->async.hostname = NULL;
581 conn->async.os_specific = res;
583 /* initial status - failed */
584 res->last_status = ARES_ENOTFOUND;
585 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
586 if(family == PF_UNSPEC) {
587 res->num_pending = 2;
589 /* areschannel is already setup in the Curl_open() function */
590 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
591 PF_INET, query_completed_cb, conn);
592 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
593 PF_INET6, query_completed_cb, conn);
596 #endif /* CURLRES_IPV6 */
598 res->num_pending = 1;
600 /* areschannel is already setup in the Curl_open() function */
601 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
602 query_completed_cb, conn);
605 *waitp = 1; /* expect asynchronous response */
607 return NULL; /* no struct yet */
609 #endif /* CURLRES_ARES */