1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2013, 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
319  *dns = NULL;
320
321  waitperform(conn, 0);
322
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
326       them */
327    res->temp_ai = NULL;
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",
332            conn->host.dispname,
333            ares_strerror(conn->async.status));
334      return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
335        CURLE_COULDNT_RESOLVE_HOST;
336    }
337    *dns = conn->async.dns;
338  }
339
340  return CURLE_OK;
341}
342
343/*
344 * Curl_resolver_wait_resolv()
345 *
346 * waits for a resolve to finish. This function should be avoided since using
347 * this risk getting the multi interface to "hang".
348 *
349 * If 'entry' is non-NULL, make it point to the resolved dns entry
350 *
351 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
352 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
353 */
354CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
355                                   struct Curl_dns_entry **entry)
356{
357  CURLcode rc=CURLE_OK;
358  struct SessionHandle *data = conn->data;
359  long timeout;
360  struct timeval now = Curl_tvnow();
361  struct Curl_dns_entry *temp_entry;
362
363  timeout = Curl_timeleft(data, &now, TRUE);
364  if(!timeout)
365    timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
366
367  /* Wait for the name resolve query to complete. */
368  for(;;) {
369    struct timeval *tvp, tv, store;
370    long timediff;
371    int itimeout;
372    int timeout_ms;
373
374    itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
375
376    store.tv_sec = itimeout/1000;
377    store.tv_usec = (itimeout%1000)*1000;
378
379    tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
380
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 */
384    if(!tvp->tv_sec)
385      timeout_ms = (int)(tvp->tv_usec/1000);
386    else
387      timeout_ms = 1000;
388
389    waitperform(conn, timeout_ms);
390    Curl_resolver_is_resolved(conn,&temp_entry);
391
392    if(conn->async.done)
393      break;
394
395    if(Curl_pgrsUpdate(conn)) {
396      rc = CURLE_ABORTED_BY_CALLBACK;
397      timeout = -1; /* trigger the cancel below */
398    }
399    else {
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 */
404    }
405    if(timeout < 0) {
406      /* our timeout, so we cancel the ares operation */
407      ares_cancel((ares_channel)data->state.resolver);
408      break;
409    }
410  }
411
412  /* Operation complete, if the lookup was successful we now have the entry
413     in the cache. */
414
415  if(entry)
416    *entry = conn->async.dns;
417
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;
424      }
425      else {
426        failf(data, "Resolving host timed out: %s", conn->host.dispname);
427        rc = CURLE_COULDNT_RESOLVE_HOST;
428      }
429    }
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;
435      }
436      else {
437        failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
438              ares_strerror(conn->async.status));
439        rc = CURLE_COULDNT_RESOLVE_HOST;
440      }
441    }
442    else
443      rc = CURLE_OPERATION_TIMEDOUT;
444
445    /* close the connection, since we can't return failure here without
446       cleaning up this connection properly */
447    conn->bits.close = TRUE;
448  }
449
450  return rc;
451}
452
453/* Connects results to the list */
454static void compound_results(struct ResolverResults *res,
455                             Curl_addrinfo *ai)
456{
457  Curl_addrinfo *ai_tail;
458  if(!ai)
459    return;
460  ai_tail = ai;
461
462  while(ai_tail->ai_next)
463    ai_tail = ai_tail->ai_next;
464
465  /* Add the new results to the list of old results. */
466  ai_tail->ai_next = res->temp_ai;
467  res->temp_ai = ai;
468}
469
470/*
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.
474 */
475static void query_completed_cb(void *arg,  /* (struct connectdata *) */
476                               int status,
477#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
478                               int timeouts,
479#endif
480                               struct hostent *hostent)
481{
482  struct connectdata *conn = (struct connectdata *)arg;
483  struct ResolverResults *res;
484
485#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
486  (void)timeouts; /* ignored */
487#endif
488
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! */
492    return;
493
494  res = (struct ResolverResults *)conn->async.os_specific;
495  res->num_pending--;
496
497  if(CURL_ASYNC_SUCCESS == status) {
498    Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
499    if(ai) {
500      compound_results(res, ai);
501    }
502  }
503  /* A successful result overwrites any previous error */
504  if(res->last_status != ARES_SUCCESS)
505    res->last_status = status;
506}
507
508/*
509 * Curl_resolver_getaddrinfo() - when using ares
510 *
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.
515 */
516Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
517                                         const char *hostname,
518                                         int port,
519                                         int *waitp)
520{
521  char *bufp;
522  struct SessionHandle *data = conn->data;
523  struct in_addr in;
524  int family = PF_INET;
525#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
526  struct in6_addr in6;
527#endif /* CURLRES_IPV6 */
528
529  *waitp = 0; /* default to synchronous response */
530
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);
535  }
536
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);
542
543  switch(conn->ip_version) {
544  default:
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
548                           to PF_INET */
549    break;
550#endif
551  case CURL_IPRESOLVE_V4:
552    family = PF_INET;
553    break;
554  case CURL_IPRESOLVE_V6:
555    family = PF_INET6;
556    break;
557  }
558#endif /* CURLRES_IPV6 */
559
560  bufp = strdup(hostname);
561  if(bufp) {
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);
570    if(!res) {
571      Curl_safefree(conn->async.hostname);
572      conn->async.hostname = NULL;
573      return NULL;
574    }
575    conn->async.os_specific = res;
576
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;
583
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);
589      }
590      else {
591        res->num_pending = 1;
592
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);
596      }
597    }
598    else
599#endif /* CURLRES_IPV6 */
600    {
601      res->num_pending = 1;
602
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);
606    }
607
608    *waitp = 1; /* expect asynchronous response */
609  }
610  return NULL; /* no struct yet */
611}
612
613CURLcode Curl_set_dns_servers(struct SessionHandle *data,
614                              char *servers)
615{
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) {
620  case ARES_SUCCESS:
621    result = CURLE_OK;
622    break;
623  case ARES_ENOMEM:
624    result = CURLE_OUT_OF_MEMORY;
625    break;
626  case ARES_ENOTINITIALIZED:
627  case ARES_ENODATA:
628  case ARES_EBADSTR:
629  default:
630    result = CURLE_BAD_FUNCTION_ARGUMENT;
631    break;
632  }
633#else /* too old c-ares version! */
634  (void)data;
635  (void)servers;
636#endif
637  return result;
638}
639#endif /* CURLRES_ARES */
640