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#ifdef HAVE_LIMITS_H
26#include <limits.h>
27#endif
28#ifdef HAVE_SYS_SOCKET_H
29#include <sys/socket.h>
30#endif
31#ifdef HAVE_NETINET_IN_H
32#include <netinet/in.h>
33#endif
34#ifdef HAVE_NETDB_H
35#include <netdb.h>
36#endif
37#ifdef HAVE_ARPA_INET_H
38#include <arpa/inet.h>
39#endif
40#ifdef HAVE_UNISTD_H
41#include <unistd.h>     /* for the close() proto */
42#endif
43#ifdef __VMS
44#include <in.h>
45#include <inet.h>
46#endif
47
48#ifdef HAVE_PROCESS_H
49#include <process.h>
50#endif
51
52#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
53#undef in_addr_t
54#define in_addr_t unsigned long
55#endif
56
57/***********************************************************************
58 * Only for ares-enabled builds
59 * And only for functions that fulfill the asynch resolver backend API
60 * as defined in asyn.h, nothing else belongs in this file!
61 **********************************************************************/
62
63#ifdef CURLRES_ARES
64
65#include "urldata.h"
66#include "sendf.h"
67#include "hostip.h"
68#include "hash.h"
69#include "share.h"
70#include "strerror.h"
71#include "url.h"
72#include "multiif.h"
73#include "inet_pton.h"
74#include "connect.h"
75#include "select.h"
76#include "progress.h"
77
78#define _MPRINTF_REPLACE /* use our functions only */
79#include <curl/mprintf.h>
80
81#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
82     (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
83#    define CARES_STATICLIB
84#  endif
85#  include <ares.h>
86
87#if ARES_VERSION >= 0x010500
88/* c-ares 1.5.0 or later, the callback proto is modified */
89#define HAVE_CARES_CALLBACK_TIMEOUTS 1
90#endif
91
92#include "curl_memory.h"
93/* The last #include file should be: */
94#include "memdebug.h"
95
96struct ResolverResults {
97  int num_pending; /* number of ares_gethostbyname() requests */
98  Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
99  int last_status;
100};
101
102/*
103 * Curl_resolver_global_init() - the generic low-level asynchronous name
104 * resolve API.  Called from curl_global_init() to initialize global resolver
105 * environment.  Initializes ares library.
106 */
107int Curl_resolver_global_init(void)
108{
109#ifdef CARES_HAVE_ARES_LIBRARY_INIT
110  if(ares_library_init(ARES_LIB_INIT_ALL)) {
111    return CURLE_FAILED_INIT;
112  }
113#endif
114  return CURLE_OK;
115}
116
117/*
118 * Curl_resolver_global_cleanup()
119 *
120 * Called from curl_global_cleanup() to destroy global resolver environment.
121 * Deinitializes ares library.
122 */
123void Curl_resolver_global_cleanup(void)
124{
125#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
126  ares_library_cleanup();
127#endif
128}
129
130/*
131 * Curl_resolver_init()
132 *
133 * Called from curl_easy_init() -> Curl_open() to initialize resolver
134 * URL-state specific environment ('resolver' member of the UrlState
135 * structure).  Fills the passed pointer by the initialized ares_channel.
136 */
137CURLcode Curl_resolver_init(void **resolver)
138{
139  int status = ares_init((ares_channel*)resolver);
140  if(status != ARES_SUCCESS) {
141    if(status == ARES_ENOMEM)
142      return CURLE_OUT_OF_MEMORY;
143    else
144      return CURLE_FAILED_INIT;
145  }
146  return CURLE_OK;
147  /* make sure that all other returns from this function should destroy the
148     ares channel before returning error! */
149}
150
151/*
152 * Curl_resolver_cleanup()
153 *
154 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
155 * URL-state specific environment ('resolver' member of the UrlState
156 * structure).  Destroys the ares channel.
157 */
158void Curl_resolver_cleanup(void *resolver)
159{
160  ares_destroy((ares_channel)resolver);
161}
162
163/*
164 * Curl_resolver_duphandle()
165 *
166 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
167 * environment ('resolver' member of the UrlState structure).  Duplicates the
168 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
169 */
170int Curl_resolver_duphandle(void **to, void *from)
171{
172  /* Clone the ares channel for the new handle */
173  if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from))
174    return CURLE_FAILED_INIT;
175  return CURLE_OK;
176}
177
178static void destroy_async_data (struct Curl_async *async);
179
180/*
181 * Cancel all possibly still on-going resolves for this connection.
182 */
183void Curl_resolver_cancel(struct connectdata *conn)
184{
185  if(conn && conn->data && conn->data->state.resolver)
186    ares_cancel((ares_channel)conn->data->state.resolver);
187  destroy_async_data(&conn->async);
188}
189
190/*
191 * destroy_async_data() cleans up async resolver data.
192 */
193static void destroy_async_data (struct Curl_async *async)
194{
195  if(async->hostname)
196    free(async->hostname);
197
198  if(async->os_specific) {
199    struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
200    if(res) {
201      if(res->temp_ai) {
202        Curl_freeaddrinfo(res->temp_ai);
203        res->temp_ai = NULL;
204      }
205      free(res);
206    }
207    async->os_specific = NULL;
208  }
209
210  async->hostname = NULL;
211}
212
213/*
214 * Curl_resolver_fdset() is called when someone from the outside world (using
215 * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
216 * ares. The caller must make sure that this function is only called when we
217 * have a working ares channel.
218 *
219 * Returns: CURLE_OK always!
220 */
221
222int Curl_resolver_getsock(struct connectdata *conn,
223                          curl_socket_t *socks,
224                          int numsocks)
225
226{
227  struct timeval maxtime;
228  struct timeval timebuf;
229  struct timeval *timeout;
230  int max = ares_getsock((ares_channel)conn->data->state.resolver,
231                         (ares_socket_t *)socks, numsocks);
232
233
234  maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
235  maxtime.tv_usec = 0;
236
237  timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
238                         &timebuf);
239
240  Curl_expire(conn->data,
241              (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
242
243  return max;
244}
245
246/*
247 * waitperform()
248 *
249 * 1) Ask ares what sockets it currently plays with, then
250 * 2) wait for the timeout period to check for action on ares' sockets.
251 * 3) tell ares to act on all the sockets marked as "with action"
252 *
253 * return number of sockets it worked on
254 */
255
256static int waitperform(struct connectdata *conn, int timeout_ms)
257{
258  struct SessionHandle *data = conn->data;
259  int nfds;
260  int bitmask;
261  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
262  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
263  int i;
264  int num = 0;
265
266  bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
267                         ARES_GETSOCK_MAXNUM);
268
269  for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
270    pfd[i].events = 0;
271    pfd[i].revents = 0;
272    if(ARES_GETSOCK_READABLE(bitmask, i)) {
273      pfd[i].fd = socks[i];
274      pfd[i].events |= POLLRDNORM|POLLIN;
275    }
276    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
277      pfd[i].fd = socks[i];
278      pfd[i].events |= POLLWRNORM|POLLOUT;
279    }
280    if(pfd[i].events != 0)
281      num++;
282    else
283      break;
284  }
285
286  if(num)
287    nfds = Curl_poll(pfd, num, timeout_ms);
288  else
289    nfds = 0;
290
291  if(!nfds)
292    /* Call ares_process() unconditonally here, even if we simply timed out
293       above, as otherwise the ares name resolve won't timeout! */
294    ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
295                    ARES_SOCKET_BAD);
296  else {
297    /* move through the descriptors and ask for processing on them */
298    for(i=0; i < num; i++)
299      ares_process_fd((ares_channel)data->state.resolver,
300                      pfd[i].revents & (POLLRDNORM|POLLIN)?
301                      pfd[i].fd:ARES_SOCKET_BAD,
302                      pfd[i].revents & (POLLWRNORM|POLLOUT)?
303                      pfd[i].fd:ARES_SOCKET_BAD);
304  }
305  return nfds;
306}
307
308/*
309 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
310 * name resolve request has completed. It should also make sure to time-out if
311 * the operation seems to take too long.
312 *
313 * Returns normal CURLcode errors.
314 */
315CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
316                                   struct Curl_dns_entry **dns)
317{
318  struct SessionHandle *data = conn->data;
319  struct ResolverResults *res = (struct ResolverResults *)
320    conn->async.os_specific;
321
322  *dns = NULL;
323
324  waitperform(conn, 0);
325
326  if(res && !res->num_pending) {
327    (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
328    /* temp_ai ownership is moved to the connection, so we need not free-up
329       them */
330    res->temp_ai = NULL;
331    destroy_async_data(&conn->async);
332    if(!conn->async.dns) {
333      failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
334            ares_strerror(conn->async.status));
335      return 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.httpproxy) {
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.httpproxy) {
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      res->num_pending = 2;
582
583      /* areschannel is already setup in the Curl_open() function */
584      ares_gethostbyname((ares_channel)data->state.resolver, hostname,
585                         PF_INET, query_completed_cb, conn);
586      ares_gethostbyname((ares_channel)data->state.resolver, hostname,
587                         PF_INET6, query_completed_cb, conn);
588    }
589    else
590#endif /* CURLRES_IPV6 */
591    {
592      res->num_pending = 1;
593
594      /* areschannel is already setup in the Curl_open() function */
595      ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
596                         query_completed_cb, conn);
597    }
598
599    *waitp = 1; /* expect asynchronous response */
600  }
601  return NULL; /* no struct yet */
602}
603#endif /* CURLRES_ARES */
604