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