compiler warning: fix
[platform/upstream/curl.git] / lib / asyn-ares.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
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.
13  *
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.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 #include "setup.h"
24
25 #include <string.h>
26
27 #ifdef HAVE_LIMITS_H
28 #include <limits.h>
29 #endif
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
35 #endif
36 #ifdef HAVE_NETDB_H
37 #include <netdb.h>
38 #endif
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
41 #endif
42 #ifdef HAVE_STDLIB_H
43 #include <stdlib.h>     /* required for free() prototypes */
44 #endif
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>     /* for the close() proto */
47 #endif
48 #ifdef __VMS
49 #include <in.h>
50 #include <inet.h>
51 #include <stdlib.h>
52 #endif
53
54 #ifdef HAVE_PROCESS_H
55 #include <process.h>
56 #endif
57
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #undef in_addr_t
60 #define in_addr_t unsigned long
61 #endif
62
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  **********************************************************************/
68
69 #ifdef CURLRES_ARES
70
71 #include "urldata.h"
72 #include "sendf.h"
73 #include "hostip.h"
74 #include "hash.h"
75 #include "share.h"
76 #include "strerror.h"
77 #include "url.h"
78 #include "multiif.h"
79 #include "inet_pton.h"
80 #include "connect.h"
81 #include "select.h"
82 #include "progress.h"
83
84 #define _MPRINTF_REPLACE /* use our functions only */
85 #include <curl/mprintf.h>
86
87 #  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
88      (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
89 #    define CARES_STATICLIB
90 #  endif
91 #  include <ares.h>
92
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
96 #endif
97
98 #include "curl_memory.h"
99 /* The last #include file should be: */
100 #include "memdebug.h"
101
102 struct ResolverResults {
103   int num_pending; /* number of ares_gethostbyname() requests */
104   Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
105   int last_status;
106 };
107
108 /*
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.
112  */
113 int Curl_resolver_global_init(void)
114 {
115 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
116   if(ares_library_init(ARES_LIB_INIT_ALL)) {
117     return CURLE_FAILED_INIT;
118   }
119 #endif
120   return CURLE_OK;
121 }
122
123 /*
124  * Curl_resolver_global_cleanup()
125  *
126  * Called from curl_global_cleanup() to destroy global resolver environment.
127  * Deinitializes ares library.
128  */
129 void Curl_resolver_global_cleanup(void)
130 {
131 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
132   ares_library_cleanup();
133 #endif
134 }
135
136 /*
137  * Curl_resolver_init()
138  *
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.
142  */
143 CURLcode Curl_resolver_init(void **resolver)
144 {
145   int status = ares_init((ares_channel*)resolver);
146   if(status != ARES_SUCCESS) {
147     if(status == ARES_ENOMEM)
148       return CURLE_OUT_OF_MEMORY;
149     else
150       return CURLE_FAILED_INIT;
151   }
152   return CURLE_OK;
153   /* make sure that all other returns from this function should destroy the
154      ares channel before returning error! */
155 }
156
157 /*
158  * Curl_resolver_cleanup()
159  *
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.
163  */
164 void Curl_resolver_cleanup(void *resolver)
165 {
166   ares_destroy((ares_channel)resolver);
167 }
168
169 /*
170  * Curl_resolver_duphandle()
171  *
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.
175  */
176 int Curl_resolver_duphandle(void **to, void *from)
177 {
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;
181   return CURLE_OK;
182 }
183
184 static void destroy_async_data (struct Curl_async *async);
185
186 /*
187  * Cancel all possibly still on-going resolves for this connection.
188  */
189 void Curl_resolver_cancel(struct connectdata *conn)
190 {
191   if(conn && conn->data && conn->data->state.resolver)
192     ares_cancel((ares_channel)conn->data->state.resolver);
193   destroy_async_data(&conn->async);
194 }
195
196 /*
197  * destroy_async_data() cleans up async resolver data.
198  */
199 static void destroy_async_data (struct Curl_async *async)
200 {
201   if(async->hostname)
202     free(async->hostname);
203
204   if(async->os_specific) {
205     struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
206     if(res) {
207       if(res->temp_ai) {
208         Curl_freeaddrinfo(res->temp_ai);
209         res->temp_ai = NULL;
210       }
211       free(res);
212     }
213     async->os_specific = NULL;
214   }
215
216   async->hostname = NULL;
217 }
218
219 /*
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.
224  *
225  * Returns: CURLE_OK always!
226  */
227
228 int Curl_resolver_getsock(struct connectdata *conn,
229                           curl_socket_t *socks,
230                           int numsocks)
231
232 {
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);
238
239
240   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
241   maxtime.tv_usec = 0;
242
243   timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
244                          &timebuf);
245
246   Curl_expire(conn->data,
247               (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
248
249   return max;
250 }
251
252 /*
253  * waitperform()
254  *
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"
258  *
259  * return number of sockets it worked on
260  */
261
262 static int waitperform(struct connectdata *conn, int timeout_ms)
263 {
264   struct SessionHandle *data = conn->data;
265   int nfds;
266   int bitmask;
267   ares_socket_t socks[ARES_GETSOCK_MAXNUM];
268   struct pollfd pfd[ARES_GETSOCK_MAXNUM];
269   int i;
270   int num = 0;
271
272   bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
273                          ARES_GETSOCK_MAXNUM);
274
275   for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
276     pfd[i].events = 0;
277     pfd[i].revents = 0;
278     if(ARES_GETSOCK_READABLE(bitmask, i)) {
279       pfd[i].fd = socks[i];
280       pfd[i].events |= POLLRDNORM|POLLIN;
281     }
282     if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
283       pfd[i].fd = socks[i];
284       pfd[i].events |= POLLWRNORM|POLLOUT;
285     }
286     if(pfd[i].events != 0)
287       num++;
288     else
289       break;
290   }
291
292   if(num)
293     nfds = Curl_poll(pfd, num, timeout_ms);
294   else
295     nfds = 0;
296
297   if(!nfds)
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,
301                     ARES_SOCKET_BAD);
302   else {
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);
310   }
311   return nfds;
312 }
313
314 /*
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.
318  *
319  * Returns normal CURLcode errors.
320  */
321 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
322                                    struct Curl_dns_entry **dns)
323 {
324   struct SessionHandle *data = conn->data;
325   struct ResolverResults *res = (struct ResolverResults *)
326     conn->async.os_specific;
327
328   *dns = NULL;
329
330   waitperform(conn, 0);
331
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
335        them */
336     res->temp_ai = NULL;
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;
342     }
343     *dns = conn->async.dns;
344   }
345
346   return CURLE_OK;
347 }
348
349 /*
350  * Curl_resolver_wait_resolv()
351  *
352  * waits for a resolve to finish. This function should be avoided since using
353  * this risk getting the multi interface to "hang".
354  *
355  * If 'entry' is non-NULL, make it point to the resolved dns entry
356  *
357  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
358  * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
359  */
360 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
361                                    struct Curl_dns_entry **entry)
362 {
363   CURLcode rc=CURLE_OK;
364   struct SessionHandle *data = conn->data;
365   long timeout;
366   struct timeval now = Curl_tvnow();
367   struct Curl_dns_entry *temp_entry;
368
369   timeout = Curl_timeleft(data, &now, TRUE);
370   if(!timeout)
371     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
372
373   /* Wait for the name resolve query to complete. */
374   for(;;) {
375     struct timeval *tvp, tv, store;
376     long timediff;
377     int itimeout;
378     int timeout_ms;
379
380     itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
381
382     store.tv_sec = itimeout/1000;
383     store.tv_usec = (itimeout%1000)*1000;
384
385     tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
386
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 */
390     if(!tvp->tv_sec)
391       timeout_ms = (int)(tvp->tv_usec/1000);
392     else
393       timeout_ms = 1000;
394
395     waitperform(conn, timeout_ms);
396     Curl_resolver_is_resolved(conn,&temp_entry);
397
398     if(conn->async.done)
399       break;
400
401     if(Curl_pgrsUpdate(conn)) {
402       rc = CURLE_ABORTED_BY_CALLBACK;
403       timeout = -1; /* trigger the cancel below */
404     }
405     else {
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 */
410     }
411     if(timeout < 0) {
412       /* our timeout, so we cancel the ares operation */
413       ares_cancel((ares_channel)data->state.resolver);
414       break;
415     }
416   }
417
418   /* Operation complete, if the lookup was successful we now have the entry
419      in the cache. */
420
421   if(entry)
422     *entry = conn->async.dns;
423
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;
430       }
431       else {
432         failf(data, "Resolving host timed out: %s", conn->host.dispname);
433         rc = CURLE_COULDNT_RESOLVE_HOST;
434       }
435     }
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;
441       }
442       else {
443         failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
444               ares_strerror(conn->async.status));
445         rc = CURLE_COULDNT_RESOLVE_HOST;
446       }
447     }
448     else
449       rc = CURLE_OPERATION_TIMEDOUT;
450
451     /* close the connection, since we can't return failure here without
452        cleaning up this connection properly */
453     conn->bits.close = TRUE;
454   }
455
456   return rc;
457 }
458
459 /* Connects results to the list */
460 static void compound_results(struct ResolverResults *res,
461                              Curl_addrinfo *ai)
462 {
463   Curl_addrinfo *ai_tail;
464   if(!ai)
465     return;
466   ai_tail = ai;
467
468   while(ai_tail->ai_next)
469     ai_tail = ai_tail->ai_next;
470
471   /* Add the new results to the list of old results. */
472   ai_tail->ai_next = res->temp_ai;
473   res->temp_ai = ai;
474 }
475
476 /*
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.
480  */
481 static void query_completed_cb(void *arg,  /* (struct connectdata *) */
482                                int status,
483 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
484                                int timeouts,
485 #endif
486                                struct hostent *hostent)
487 {
488   struct connectdata *conn = (struct connectdata *)arg;
489   struct ResolverResults *res;
490
491 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
492   (void)timeouts; /* ignored */
493 #endif
494
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! */
498     return;
499
500   res = (struct ResolverResults *)conn->async.os_specific;
501   res->num_pending--;
502
503   if(CURL_ASYNC_SUCCESS == status) {
504     Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
505     if(ai) {
506       compound_results(res, ai);
507     }
508   }
509   /* A successful result overwrites any previous error */
510   if(res->last_status != ARES_SUCCESS)
511     res->last_status = status;
512 }
513
514 /*
515  * Curl_resolver_getaddrinfo() - when using ares
516  *
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.
521  */
522 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
523                                          const char *hostname,
524                                          int port,
525                                          int *waitp)
526 {
527   char *bufp;
528   struct SessionHandle *data = conn->data;
529   struct in_addr in;
530   int family = PF_INET;
531 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
532   struct in6_addr in6;
533 #endif /* CURLRES_IPV6 */
534
535   *waitp = 0; /* default to synchronous response */
536
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);
541   }
542
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);
548
549   switch(conn->ip_version) {
550   default:
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
554                            to PF_INET */
555     break;
556 #endif
557   case CURL_IPRESOLVE_V4:
558     family = PF_INET;
559     break;
560   case CURL_IPRESOLVE_V6:
561     family = PF_INET6;
562     break;
563   }
564 #endif /* CURLRES_IPV6 */
565
566   bufp = strdup(hostname);
567   if(bufp) {
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);
576     if(!res) {
577       Curl_safefree(conn->async.hostname);
578       conn->async.hostname = NULL;
579       return NULL;
580     }
581     conn->async.os_specific = res;
582
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;
588
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);
594     }
595     else
596 #endif /* CURLRES_IPV6 */
597     {
598       res->num_pending = 1;
599
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);
603     }
604
605     *waitp = 1; /* expect asynchronous response */
606   }
607   return NULL; /* no struct yet */
608 }
609 #endif /* CURLRES_ARES */