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
102struct 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 */
113int 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 */
129void 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 */
143CURLcode 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 */
164void 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 */
176int 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
184static void destroy_async_data (struct Curl_async *async);
185
186/*
187 * Cancel all possibly still on-going resolves for this connection.
188 */
189void 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 */
199static 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
228int 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
262static 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 */
321CURLcode 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 */
360CURLcode 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 */
460static 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 */
481static 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 */
522Curl_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 = 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 */
610