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