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_SYS_SOCKET_H
26#include <sys/socket.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 HAVE_UNISTD_H
38#include <unistd.h>     /* for the close() proto */
39#endif
40#ifdef __VMS
41#include <in.h>
42#include <inet.h>
43#endif
44
45#if defined(USE_THREADS_POSIX)
46#  ifdef HAVE_PTHREAD_H
47#    include <pthread.h>
48#  endif
49#elif defined(USE_THREADS_WIN32)
50#  ifdef HAVE_PROCESS_H
51#    include <process.h>
52#  endif
53#endif
54
55#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
56#undef in_addr_t
57#define in_addr_t unsigned long
58#endif
59
60#ifdef HAVE_GETADDRINFO
61#  define RESOLVER_ENOMEM  EAI_MEMORY
62#else
63#  define RESOLVER_ENOMEM  ENOMEM
64#endif
65
66#include "urldata.h"
67#include "sendf.h"
68#include "hostip.h"
69#include "hash.h"
70#include "share.h"
71#include "strerror.h"
72#include "url.h"
73#include "multiif.h"
74#include "inet_pton.h"
75#include "inet_ntop.h"
76#include "curl_threads.h"
77
78#define _MPRINTF_REPLACE /* use our functions only */
79#include <curl/mprintf.h>
80
81#include "curl_memory.h"
82/* The last #include file should be: */
83#include "memdebug.h"
84
85/***********************************************************************
86 * Only for threaded name resolves builds
87 **********************************************************************/
88#ifdef CURLRES_THREADED
89
90/*
91 * Curl_resolver_global_init()
92 * Called from curl_global_init() to initialize global resolver environment.
93 * Does nothing here.
94 */
95int Curl_resolver_global_init(void)
96{
97  return CURLE_OK;
98}
99
100/*
101 * Curl_resolver_global_cleanup()
102 * Called from curl_global_cleanup() to destroy global resolver environment.
103 * Does nothing here.
104 */
105void Curl_resolver_global_cleanup(void)
106{
107}
108
109/*
110 * Curl_resolver_init()
111 * Called from curl_easy_init() -> Curl_open() to initialize resolver
112 * URL-state specific environment ('resolver' member of the UrlState
113 * structure).  Does nothing here.
114 */
115CURLcode Curl_resolver_init(void **resolver)
116{
117  (void)resolver;
118  return CURLE_OK;
119}
120
121/*
122 * Curl_resolver_cleanup()
123 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
124 * URL-state specific environment ('resolver' member of the UrlState
125 * structure).  Does nothing here.
126 */
127void Curl_resolver_cleanup(void *resolver)
128{
129  (void)resolver;
130}
131
132/*
133 * Curl_resolver_duphandle()
134 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
135 * environment ('resolver' member of the UrlState structure).  Does nothing
136 * here.
137 */
138int Curl_resolver_duphandle(void **to, void *from)
139{
140  (void)to;
141  (void)from;
142  return CURLE_OK;
143}
144
145static void destroy_async_data(struct Curl_async *);
146
147/*
148 * Cancel all possibly still on-going resolves for this connection.
149 */
150void Curl_resolver_cancel(struct connectdata *conn)
151{
152  destroy_async_data(&conn->async);
153}
154
155/* This function is used to init a threaded resolve */
156static bool init_resolve_thread(struct connectdata *conn,
157                                const char *hostname, int port,
158                                const struct addrinfo *hints);
159
160
161/* Data for synchronization between resolver thread and its parent */
162struct thread_sync_data {
163  curl_mutex_t * mtx;
164  int done;
165
166  char * hostname;        /* hostname to resolve, Curl_async.hostname
167                             duplicate */
168  int port;
169  int sock_error;
170  Curl_addrinfo *res;
171#ifdef HAVE_GETADDRINFO
172  struct addrinfo hints;
173#endif
174};
175
176struct thread_data {
177  curl_thread_t thread_hnd;
178  curl_socket_t dummy_sock;
179  unsigned int poll_interval;
180  int interval_end;
181  struct thread_sync_data tsd;
182};
183
184static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
185{
186  return &(((struct thread_data *)conn->async.os_specific)->tsd);
187}
188
189#define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
190
191/* Destroy resolver thread synchronization data */
192static
193void destroy_thread_sync_data(struct thread_sync_data * tsd)
194{
195  if(tsd->mtx) {
196    Curl_mutex_destroy(tsd->mtx);
197    free(tsd->mtx);
198  }
199
200  if(tsd->hostname)
201    free(tsd->hostname);
202
203  if(tsd->res)
204    Curl_freeaddrinfo(tsd->res);
205
206  memset(tsd,0,sizeof(*tsd));
207}
208
209/* Initialize resolver thread synchronization data */
210static
211int init_thread_sync_data(struct thread_sync_data * tsd,
212                           const char * hostname,
213                           int port,
214                           const struct addrinfo *hints)
215{
216  memset(tsd, 0, sizeof(*tsd));
217
218  tsd->port = port;
219#ifdef CURLRES_IPV6
220  DEBUGASSERT(hints);
221  tsd->hints = *hints;
222#else
223  (void) hints;
224#endif
225
226  tsd->mtx = malloc(sizeof(curl_mutex_t));
227  if(tsd->mtx == NULL)
228    goto err_exit;
229
230  Curl_mutex_init(tsd->mtx);
231
232  tsd->sock_error = CURL_ASYNC_SUCCESS;
233
234  /* Copying hostname string because original can be destroyed by parent
235   * thread during gethostbyname execution.
236   */
237  tsd->hostname = strdup(hostname);
238  if(!tsd->hostname)
239    goto err_exit;
240
241  return 1;
242
243 err_exit:
244  /* Memory allocation failed */
245  destroy_thread_sync_data(tsd);
246  return 0;
247}
248
249static int getaddrinfo_complete(struct connectdata *conn)
250{
251  struct thread_sync_data *tsd = conn_thread_sync_data(conn);
252  int rc;
253
254  rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
255  /* The tsd->res structure has been copied to async.dns and perhaps the DNS
256     cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
257  */
258  tsd->res = NULL;
259
260  return rc;
261}
262
263
264#ifdef HAVE_GETADDRINFO
265
266/*
267 * getaddrinfo_thread() resolves a name and then exits.
268 *
269 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
270 * and wait on it.
271 */
272static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
273{
274  struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
275  char   service [NI_MAXSERV];
276  int rc;
277
278  snprintf(service, sizeof(service), "%d", tsd->port);
279
280  rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
281
282  if(rc != 0) {
283    tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
284    if(tsd->sock_error == 0)
285      tsd->sock_error = RESOLVER_ENOMEM;
286  }
287
288  Curl_mutex_acquire(tsd->mtx);
289  tsd->done = 1;
290  Curl_mutex_release(tsd->mtx);
291
292  return 0;
293}
294
295#else /* HAVE_GETADDRINFO */
296
297/*
298 * gethostbyname_thread() resolves a name and then exits.
299 */
300static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
301{
302  struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
303
304  tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
305
306  if(!tsd->res) {
307    tsd->sock_error = SOCKERRNO;
308    if(tsd->sock_error == 0)
309      tsd->sock_error = RESOLVER_ENOMEM;
310  }
311
312  Curl_mutex_acquire(tsd->mtx);
313  tsd->done = 1;
314  Curl_mutex_release(tsd->mtx);
315
316  return 0;
317}
318
319#endif /* HAVE_GETADDRINFO */
320
321/*
322 * destroy_async_data() cleans up async resolver data and thread handle.
323 */
324static void destroy_async_data (struct Curl_async *async)
325{
326  if(async->hostname)
327    free(async->hostname);
328
329  if(async->os_specific) {
330    struct thread_data *td = (struct thread_data*) async->os_specific;
331
332    if(td->dummy_sock != CURL_SOCKET_BAD)
333      sclose(td->dummy_sock);
334
335    if(td->thread_hnd != curl_thread_t_null)
336      Curl_thread_join(&td->thread_hnd);
337
338    destroy_thread_sync_data(&td->tsd);
339
340    free(async->os_specific);
341  }
342  async->hostname = NULL;
343  async->os_specific = NULL;
344}
345
346/*
347 * init_resolve_thread() starts a new thread that performs the actual
348 * resolve. This function returns before the resolve is done.
349 *
350 * Returns FALSE in case of failure, otherwise TRUE.
351 */
352static bool init_resolve_thread (struct connectdata *conn,
353                                 const char *hostname, int port,
354                                 const struct addrinfo *hints)
355{
356  struct thread_data *td = calloc(1, sizeof(struct thread_data));
357  int err = RESOLVER_ENOMEM;
358
359  conn->async.os_specific = (void*) td;
360  if(!td)
361    goto err_exit;
362
363  conn->async.port = port;
364  conn->async.done = FALSE;
365  conn->async.status = 0;
366  conn->async.dns = NULL;
367  td->dummy_sock = CURL_SOCKET_BAD;
368  td->thread_hnd = curl_thread_t_null;
369
370  if(!init_thread_sync_data(&td->tsd, hostname, port, hints))
371    goto err_exit;
372
373  Curl_safefree(conn->async.hostname);
374  conn->async.hostname = strdup(hostname);
375  if(!conn->async.hostname)
376    goto err_exit;
377
378#ifdef WIN32
379  /* This socket is only to keep Curl_resolver_fdset() and select() happy;
380   * should never become signalled for read since it's unbound but
381   * Windows needs at least 1 socket in select().
382   */
383  td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
384  if(td->dummy_sock == CURL_SOCKET_BAD)
385    goto err_exit;
386#endif
387
388#ifdef HAVE_GETADDRINFO
389  td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
390#else
391  td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
392#endif
393
394  if(!td->thread_hnd) {
395#ifndef _WIN32_WCE
396    err = errno;
397#endif
398    goto err_exit;
399  }
400
401  return TRUE;
402
403 err_exit:
404  destroy_async_data(&conn->async);
405
406  SET_ERRNO(err);
407
408  return FALSE;
409}
410
411#if defined(HAVE_GETADDRINFO) && !defined(HAVE_GAI_STRERROR) && !defined(WIN32)
412/* NetWare has getaddrinfo but lacks gai_strerror.
413   Windows has a gai_strerror but it is bad (not thread-safe) and the generic
414   socket error string function can be used for this pupose. */
415static const char *gai_strerror(int ecode)
416{
417  switch (ecode) {
418  case EAI_AGAIN:
419    return "The name could not be resolved at this time";
420  case EAI_BADFLAGS:
421    return "The flags parameter had an invalid value";
422  case EAI_FAIL:
423    return "A non-recoverable error occurred when attempting to "
424      "resolve the name";
425  case EAI_FAMILY:
426    return "The address family was not recognized";
427  case EAI_MEMORY:
428    return "Out of memory";
429  case EAI_NONAME:
430    return "The name does not resolve for the supplied parameters";
431  case EAI_SERVICE:
432    return "The service passed was not recognized for the "
433      "specified socket type"
434  case EAI_SOCKTYPE:
435    return "The intended socket type was not recognized"
436  case EAI_SYSTEM:
437    return "A system error occurred";
438  case EAI_OVERFLOW:
439    return "An argument buffer overflowed";
440  default:
441    return "Unknown error";
442
443/* define this now as this is a private implementation of said function */
444#define HAVE_GAI_STRERROR
445}
446#endif
447
448
449/*
450 * resolver_error() calls failf() with the appropriate message after a resolve
451 * error
452 */
453
454static void resolver_error(struct connectdata *conn, const char *host_or_proxy)
455{
456  failf(conn->data, "Could not resolve %s: %s; %s", host_or_proxy,
457        conn->async.hostname,
458#ifdef HAVE_GAI_STRERROR
459        /* NetWare doesn't have gai_strerror and on Windows it isn't deemed
460           thread-safe */
461        gai_strerror(conn->async.status)
462#else
463        Curl_strerror(conn, conn->async.status)
464#endif
465    );
466}
467
468/*
469 * Curl_resolver_wait_resolv()
470 *
471 * waits for a resolve to finish. This function should be avoided since using
472 * this risk getting the multi interface to "hang".
473 *
474 * If 'entry' is non-NULL, make it point to the resolved dns entry
475 *
476 * This is the version for resolves-in-a-thread.
477 */
478CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
479                                   struct Curl_dns_entry **entry)
480{
481  struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
482  CURLcode rc = CURLE_OK;
483
484  DEBUGASSERT(conn && td);
485
486  /* wait for the thread to resolve the name */
487  if(Curl_thread_join(&td->thread_hnd))
488    rc = getaddrinfo_complete(conn);
489  else
490    DEBUGASSERT(0);
491
492  conn->async.done = TRUE;
493
494  if(entry)
495    *entry = conn->async.dns;
496
497  if(!conn->async.dns) {
498    /* a name was not resolved */
499    if(conn->bits.httpproxy) {
500      resolver_error(conn, "proxy");
501      rc = CURLE_COULDNT_RESOLVE_PROXY;
502    }
503    else {
504      resolver_error(conn, "host");
505      rc = CURLE_COULDNT_RESOLVE_HOST;
506    }
507  }
508
509  destroy_async_data(&conn->async);
510
511  if(!conn->async.dns)
512    conn->bits.close = TRUE;
513
514  return (rc);
515}
516
517/*
518 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
519 * name resolve request has completed. It should also make sure to time-out if
520 * the operation seems to take too long.
521 */
522CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
523                                   struct Curl_dns_entry **entry)
524{
525  struct SessionHandle *data = conn->data;
526  struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
527  int done = 0;
528
529  *entry = NULL;
530
531  if(!td) {
532    DEBUGASSERT(td);
533    return CURLE_COULDNT_RESOLVE_HOST;
534  }
535
536  Curl_mutex_acquire(td->tsd.mtx);
537  done = td->tsd.done;
538  Curl_mutex_release(td->tsd.mtx);
539
540  if(done) {
541    getaddrinfo_complete(conn);
542    destroy_async_data(&conn->async);
543
544    if(!conn->async.dns) {
545      resolver_error(conn, "host");
546      return CURLE_COULDNT_RESOLVE_HOST;
547    }
548    *entry = conn->async.dns;
549  }
550  else {
551    /* poll for name lookup done with exponential backoff up to 250ms */
552    int elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
553    if(elapsed < 0)
554      elapsed = 0;
555
556    if(td->poll_interval == 0)
557      /* Start at 1ms poll interval */
558      td->poll_interval = 1;
559    else if(elapsed >= td->interval_end)
560      /* Back-off exponentially if last interval expired  */
561      td->poll_interval *= 2;
562
563    if(td->poll_interval > 250)
564      td->poll_interval = 250;
565
566    td->interval_end = elapsed + td->poll_interval;
567    Curl_expire(conn->data, td->poll_interval);
568  }
569
570  return CURLE_OK;
571}
572
573int Curl_resolver_getsock(struct connectdata *conn,
574                          curl_socket_t *socks,
575                          int numsocks)
576{
577  const struct thread_data *td =
578    (const struct thread_data *) conn->async.os_specific;
579
580  if(td && td->dummy_sock != CURL_SOCKET_BAD) {
581    if(numsocks) {
582      /* return one socket waiting for readable, even though this is just
583         a dummy */
584      socks[0] = td->dummy_sock;
585      return GETSOCK_READSOCK(0);
586    }
587  }
588  return 0;
589}
590
591#ifndef HAVE_GETADDRINFO
592/*
593 * Curl_getaddrinfo() - for platforms without getaddrinfo
594 */
595Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
596                                         const char *hostname,
597                                         int port,
598                                         int *waitp)
599{
600  struct in_addr in;
601
602  *waitp = 0; /* default to synchronous response */
603
604  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
605    /* This is a dotted IP address 123.123.123.123-style */
606    return Curl_ip2addr(AF_INET, &in, hostname, port);
607
608  /* fire up a new resolver thread! */
609  if(init_resolve_thread(conn, hostname, port, NULL)) {
610    *waitp = 1; /* expect asynchronous response */
611    return NULL;
612  }
613
614  /* fall-back to blocking version */
615  return Curl_ipv4_resolve_r(hostname, port);
616}
617
618#else /* !HAVE_GETADDRINFO */
619
620/*
621 * Curl_resolver_getaddrinfo() - for getaddrinfo
622 */
623Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
624                                         const char *hostname,
625                                         int port,
626                                         int *waitp)
627{
628  struct addrinfo hints;
629  struct in_addr in;
630  Curl_addrinfo *res;
631  int error;
632  char sbuf[NI_MAXSERV];
633  int pf = PF_INET;
634#ifdef CURLRES_IPV6
635  struct in6_addr in6;
636#endif /* CURLRES_IPV6 */
637
638  *waitp = 0; /* default to synchronous response */
639
640  /* First check if this is an IPv4 address string */
641  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
642    /* This is a dotted IP address 123.123.123.123-style */
643    return Curl_ip2addr(AF_INET, &in, hostname, port);
644
645#ifdef CURLRES_IPV6
646  /* check if this is an IPv6 address string */
647  if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
648    /* This is an IPv6 address literal */
649    return Curl_ip2addr(AF_INET6, &in6, hostname, port);
650
651  /*
652   * Check if a limited name resolve has been requested.
653   */
654  switch(conn->ip_version) {
655  case CURL_IPRESOLVE_V4:
656    pf = PF_INET;
657    break;
658  case CURL_IPRESOLVE_V6:
659    pf = PF_INET6;
660    break;
661  default:
662    pf = PF_UNSPEC;
663    break;
664  }
665
666  if((pf != PF_INET) && !Curl_ipv6works())
667    /* the stack seems to be a non-ipv6 one */
668    pf = PF_INET;
669
670#endif /* CURLRES_IPV6 */
671
672  memset(&hints, 0, sizeof(hints));
673  hints.ai_family = pf;
674  hints.ai_socktype = conn->socktype;
675
676  snprintf(sbuf, sizeof(sbuf), "%d", port);
677
678  /* fire up a new resolver thread! */
679  if(init_resolve_thread(conn, hostname, port, &hints)) {
680    *waitp = 1; /* expect asynchronous response */
681    return NULL;
682  }
683
684  /* fall-back to blocking version */
685  infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
686        hostname, Curl_strerror(conn, ERRNO));
687
688  error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
689  if(error) {
690    infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
691          hostname, port, Curl_strerror(conn, SOCKERRNO));
692    return NULL;
693  }
694  return res;
695}
696
697#endif /* !HAVE_GETADDRINFO */
698
699#endif /* CURLRES_THREADED */
700