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
92struct 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 */
103int 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 */
119void 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 */
133CURLcode 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 */
154void 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 */
166int 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
174static void destroy_async_data (struct Curl_async *async);
175
176/*
177 * Cancel all possibly still on-going resolves for this connection.
178 */
179void 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 */
189static 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
218int 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
253static 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 */
312CURLcode 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 */
355CURLcode 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 */
430static 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 */
451static 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 */
492Curl_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
589CURLcode 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
627CURLcode 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
644CURLcode 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
669CURLcode 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 */
695