Revert "Update to 7.40.1"
[platform/upstream/curl.git] / lib / asyn-ares.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, 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 "curl_setup.h"
24
25 #ifdef HAVE_LIMITS_H
26 #include <limits.h>
27 #endif
28 #ifdef HAVE_NETINET_IN_H
29 #include <netinet/in.h>
30 #endif
31 #ifdef HAVE_NETDB_H
32 #include <netdb.h>
33 #endif
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
36 #endif
37 #ifdef __VMS
38 #include <in.h>
39 #include <inet.h>
40 #endif
41
42 #ifdef HAVE_PROCESS_H
43 #include <process.h>
44 #endif
45
46 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
47 #undef in_addr_t
48 #define in_addr_t unsigned long
49 #endif
50
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  **********************************************************************/
56
57 #ifdef CURLRES_ARES
58
59 #include "urldata.h"
60 #include "sendf.h"
61 #include "hostip.h"
62 #include "hash.h"
63 #include "share.h"
64 #include "strerror.h"
65 #include "url.h"
66 #include "multiif.h"
67 #include "inet_pton.h"
68 #include "connect.h"
69 #include "select.h"
70 #include "progress.h"
71
72 #define _MPRINTF_REPLACE /* use our functions only */
73 #include <curl/mprintf.h>
74
75 #  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
76      (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
77 #    define CARES_STATICLIB
78 #  endif
79 #  include <ares.h>
80 #  include <ares_version.h> /* really old c-ares didn't include this by
81                                itself */
82
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
86 #endif
87
88 #include "curl_memory.h"
89 /* The last #include file should be: */
90 #include "memdebug.h"
91
92 struct ResolverResults {
93   int num_pending; /* number of ares_gethostbyname() requests */
94   Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
95   int last_status;
96 };
97
98 /*
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.
102  */
103 int Curl_resolver_global_init(void)
104 {
105 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
106   if(ares_library_init(ARES_LIB_INIT_ALL)) {
107     return CURLE_FAILED_INIT;
108   }
109 #endif
110   return CURLE_OK;
111 }
112
113 /*
114  * Curl_resolver_global_cleanup()
115  *
116  * Called from curl_global_cleanup() to destroy global resolver environment.
117  * Deinitializes ares library.
118  */
119 void Curl_resolver_global_cleanup(void)
120 {
121 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
122   ares_library_cleanup();
123 #endif
124 }
125
126 /*
127  * Curl_resolver_init()
128  *
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.
132  */
133 CURLcode Curl_resolver_init(void **resolver)
134 {
135   int status = ares_init((ares_channel*)resolver);
136   if(status != ARES_SUCCESS) {
137     if(status == ARES_ENOMEM)
138       return CURLE_OUT_OF_MEMORY;
139     else
140       return CURLE_FAILED_INIT;
141   }
142   return CURLE_OK;
143   /* make sure that all other returns from this function should destroy the
144      ares channel before returning error! */
145 }
146
147 /*
148  * Curl_resolver_cleanup()
149  *
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.
153  */
154 void Curl_resolver_cleanup(void *resolver)
155 {
156   ares_destroy((ares_channel)resolver);
157 }
158
159 /*
160  * Curl_resolver_duphandle()
161  *
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.
165  */
166 int Curl_resolver_duphandle(void **to, void *from)
167 {
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;
171   return CURLE_OK;
172 }
173
174 static void destroy_async_data (struct Curl_async *async);
175
176 /*
177  * Cancel all possibly still on-going resolves for this connection.
178  */
179 void Curl_resolver_cancel(struct connectdata *conn)
180 {
181   if(conn && conn->data && conn->data->state.resolver)
182     ares_cancel((ares_channel)conn->data->state.resolver);
183   destroy_async_data(&conn->async);
184 }
185
186 /*
187  * destroy_async_data() cleans up async resolver data.
188  */
189 static void destroy_async_data (struct Curl_async *async)
190 {
191   if(async->hostname)
192     free(async->hostname);
193
194   if(async->os_specific) {
195     struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
196     if(res) {
197       if(res->temp_ai) {
198         Curl_freeaddrinfo(res->temp_ai);
199         res->temp_ai = NULL;
200       }
201       free(res);
202     }
203     async->os_specific = NULL;
204   }
205
206   async->hostname = NULL;
207 }
208
209 /*
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.
214  *
215  * Returns: sockets-in-use-bitmap
216  */
217
218 int Curl_resolver_getsock(struct connectdata *conn,
219                           curl_socket_t *socks,
220                           int numsocks)
221
222 {
223   struct timeval maxtime;
224   struct timeval timebuf;
225   struct timeval *timeout;
226   long milli;
227   int max = ares_getsock((ares_channel)conn->data->state.resolver,
228                          (ares_socket_t *)socks, numsocks);
229
230   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
231   maxtime.tv_usec = 0;
232
233   timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
234                          &timebuf);
235   milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
236   if(milli == 0)
237     milli += 10;
238   Curl_expire(conn->data, milli);
239
240   return max;
241 }
242
243 /*
244  * waitperform()
245  *
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"
249  *
250  * return number of sockets it worked on
251  */
252
253 static int waitperform(struct connectdata *conn, int timeout_ms)
254 {
255   struct SessionHandle *data = conn->data;
256   int nfds;
257   int bitmask;
258   ares_socket_t socks[ARES_GETSOCK_MAXNUM];
259   struct pollfd pfd[ARES_GETSOCK_MAXNUM];
260   int i;
261   int num = 0;
262
263   bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
264                          ARES_GETSOCK_MAXNUM);
265
266   for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
267     pfd[i].events = 0;
268     pfd[i].revents = 0;
269     if(ARES_GETSOCK_READABLE(bitmask, i)) {
270       pfd[i].fd = socks[i];
271       pfd[i].events |= POLLRDNORM|POLLIN;
272     }
273     if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
274       pfd[i].fd = socks[i];
275       pfd[i].events |= POLLWRNORM|POLLOUT;
276     }
277     if(pfd[i].events != 0)
278       num++;
279     else
280       break;
281   }
282
283   if(num)
284     nfds = Curl_poll(pfd, num, timeout_ms);
285   else
286     nfds = 0;
287
288   if(!nfds)
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,
292                     ARES_SOCKET_BAD);
293   else {
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);
301   }
302   return nfds;
303 }
304
305 /*
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.
309  *
310  * Returns normal CURLcode errors.
311  */
312 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
313                                    struct Curl_dns_entry **dns)
314 {
315   struct SessionHandle *data = conn->data;
316   struct ResolverResults *res = (struct ResolverResults *)
317     conn->async.os_specific;
318   CURLcode rc = CURLE_OK;
319
320   *dns = NULL;
321
322   waitperform(conn, 0);
323
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
327        them */
328     res->temp_ai = NULL;
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;
334     }
335     else
336       *dns = conn->async.dns;
337
338     destroy_async_data(&conn->async);
339   }
340
341   return rc;
342 }
343
344 /*
345  * Curl_resolver_wait_resolv()
346  *
347  * waits for a resolve to finish. This function should be avoided since using
348  * this risk getting the multi interface to "hang".
349  *
350  * If 'entry' is non-NULL, make it point to the resolved dns entry
351  *
352  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
353  * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
354  */
355 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
356                                    struct Curl_dns_entry **entry)
357 {
358   CURLcode rc=CURLE_OK;
359   struct SessionHandle *data = conn->data;
360   long timeout;
361   struct timeval now = Curl_tvnow();
362   struct Curl_dns_entry *temp_entry;
363
364   timeout = Curl_timeleft(data, &now, TRUE);
365   if(!timeout)
366     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
367
368   /* Wait for the name resolve query to complete. */
369   for(;;) {
370     struct timeval *tvp, tv, store;
371     long timediff;
372     int itimeout;
373     int timeout_ms;
374
375     itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
376
377     store.tv_sec = itimeout/1000;
378     store.tv_usec = (itimeout%1000)*1000;
379
380     tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
381
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 */
385     if(!tvp->tv_sec)
386       timeout_ms = (int)(tvp->tv_usec/1000);
387     else
388       timeout_ms = 1000;
389
390     waitperform(conn, timeout_ms);
391     Curl_resolver_is_resolved(conn,&temp_entry);
392
393     if(conn->async.done)
394       break;
395
396     if(Curl_pgrsUpdate(conn)) {
397       rc = CURLE_ABORTED_BY_CALLBACK;
398       timeout = -1; /* trigger the cancel below */
399     }
400     else {
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 */
405     }
406     if(timeout < 0) {
407       /* our timeout, so we cancel the ares operation */
408       ares_cancel((ares_channel)data->state.resolver);
409       break;
410     }
411   }
412
413   /* Operation complete, if the lookup was successful we now have the entry
414      in the cache. */
415
416   if(entry)
417     *entry = conn->async.dns;
418
419   if(rc)
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.
423     */
424     connclose(conn, "c-ares resolve failed");
425
426   return rc;
427 }
428
429 /* Connects results to the list */
430 static void compound_results(struct ResolverResults *res,
431                              Curl_addrinfo *ai)
432 {
433   Curl_addrinfo *ai_tail;
434   if(!ai)
435     return;
436   ai_tail = ai;
437
438   while(ai_tail->ai_next)
439     ai_tail = ai_tail->ai_next;
440
441   /* Add the new results to the list of old results. */
442   ai_tail->ai_next = res->temp_ai;
443   res->temp_ai = ai;
444 }
445
446 /*
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.
450  */
451 static void query_completed_cb(void *arg,  /* (struct connectdata *) */
452                                int status,
453 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
454                                int timeouts,
455 #endif
456                                struct hostent *hostent)
457 {
458   struct connectdata *conn = (struct connectdata *)arg;
459   struct ResolverResults *res;
460
461 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
462   (void)timeouts; /* ignored */
463 #endif
464
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! */
468     return;
469
470   res = (struct ResolverResults *)conn->async.os_specific;
471   res->num_pending--;
472
473   if(CURL_ASYNC_SUCCESS == status) {
474     Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
475     if(ai) {
476       compound_results(res, ai);
477     }
478   }
479   /* A successful result overwrites any previous error */
480   if(res->last_status != ARES_SUCCESS)
481     res->last_status = status;
482 }
483
484 /*
485  * Curl_resolver_getaddrinfo() - when using ares
486  *
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.
491  */
492 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
493                                          const char *hostname,
494                                          int port,
495                                          int *waitp)
496 {
497   char *bufp;
498   struct SessionHandle *data = conn->data;
499   struct in_addr in;
500   int family = PF_INET;
501 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
502   struct in6_addr in6;
503 #endif /* CURLRES_IPV6 */
504
505   *waitp = 0; /* default to synchronous response */
506
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);
511   }
512
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);
518
519   switch(conn->ip_version) {
520   default:
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
524                            to PF_INET */
525     break;
526 #endif
527   case CURL_IPRESOLVE_V4:
528     family = PF_INET;
529     break;
530   case CURL_IPRESOLVE_V6:
531     family = PF_INET6;
532     break;
533   }
534 #endif /* CURLRES_IPV6 */
535
536   bufp = strdup(hostname);
537   if(bufp) {
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);
546     if(!res) {
547       Curl_safefree(conn->async.hostname);
548       conn->async.hostname = NULL;
549       return NULL;
550     }
551     conn->async.os_specific = res;
552
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;
559
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);
565       }
566       else {
567         res->num_pending = 1;
568
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);
572       }
573     }
574     else
575 #endif /* CURLRES_IPV6 */
576     {
577       res->num_pending = 1;
578
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);
582     }
583
584     *waitp = 1; /* expect asynchronous response */
585   }
586   return NULL; /* no struct yet */
587 }
588
589 CURLcode Curl_set_dns_servers(struct SessionHandle *data,
590                               char *servers)
591 {
592   CURLcode result = CURLE_NOT_BUILT_IN;
593   int ares_result;
594
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).
600    */
601   if(!(servers && servers[0]))
602     return CURLE_OK;
603
604 #if (ARES_VERSION >= 0x010704)
605   ares_result = ares_set_servers_csv(data->state.resolver, servers);
606   switch(ares_result) {
607   case ARES_SUCCESS:
608     result = CURLE_OK;
609     break;
610   case ARES_ENOMEM:
611     result = CURLE_OUT_OF_MEMORY;
612     break;
613   case ARES_ENOTINITIALIZED:
614   case ARES_ENODATA:
615   case ARES_EBADSTR:
616   default:
617     result = CURLE_BAD_FUNCTION_ARGUMENT;
618     break;
619   }
620 #else /* too old c-ares version! */
621   (void)data;
622   (void)(ares_result);
623 #endif
624   return result;
625 }
626
627 CURLcode Curl_set_dns_interface(struct SessionHandle *data,
628                                 const char *interf)
629 {
630 #if (ARES_VERSION >= 0x010704)
631   if(!interf)
632     interf = "";
633
634   ares_set_local_dev((ares_channel)data->state.resolver, interf);
635
636   return CURLE_OK;
637 #else /* c-ares version too old! */
638   (void)data;
639   (void)interf;
640   return CURLE_NOT_BUILT_IN;
641 #endif
642 }
643
644 CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data,
645                                 const char *local_ip4)
646 {
647 #if (ARES_VERSION >= 0x010704)
648   struct in_addr a4;
649
650   if((!local_ip4) || (local_ip4[0] == 0)) {
651     a4.s_addr = 0; /* disabled: do not bind to a specific address */
652   }
653   else {
654     if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
655       return CURLE_BAD_FUNCTION_ARGUMENT;
656     }
657   }
658
659   ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
660
661   return CURLE_OK;
662 #else /* c-ares version too old! */
663   (void)data;
664   (void)local_ip4;
665   return CURLE_NOT_BUILT_IN;
666 #endif
667 }
668
669 CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data,
670                                 const char *local_ip6)
671 {
672 #if (ARES_VERSION >= 0x010704)
673   unsigned char a6[INET6_ADDRSTRLEN];
674
675   if((!local_ip6) || (local_ip6[0] == 0)) {
676     /* disabled: do not bind to a specific address */
677     memset(a6, 0, sizeof(a6));
678   }
679   else {
680     if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
681       return CURLE_BAD_FUNCTION_ARGUMENT;
682     }
683   }
684
685   ares_set_local_ip6((ares_channel)data->state.resolver, a6);
686
687   return CURLE_OK;
688 #else /* c-ares version too old! */
689   (void)data;
690   (void)local_ip6;
691   return CURLE_NOT_BUILT_IN;
692 #endif
693 }
694 #endif /* CURLRES_ARES */