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