xref: /third_party/curl/lib/asyn-thread.c (revision 13498266)
1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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.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 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26#include "socketpair.h"
27
28/***********************************************************************
29 * Only for threaded name resolves builds
30 **********************************************************************/
31#ifdef CURLRES_THREADED
32
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 __VMS
43#include <in.h>
44#include <inet.h>
45#endif
46
47#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
48#  include <pthread.h>
49#endif
50
51#ifdef HAVE_GETADDRINFO
52#  define RESOLVER_ENOMEM  EAI_MEMORY
53#else
54#  define RESOLVER_ENOMEM  ENOMEM
55#endif
56
57#include "system_win32.h"
58#include "urldata.h"
59#include "sendf.h"
60#include "hostip.h"
61#include "hash.h"
62#include "share.h"
63#include "url.h"
64#include "multiif.h"
65#include "inet_ntop.h"
66#include "curl_threads.h"
67#include "connect.h"
68/* The last 3 #include files should be in this order */
69#include "curl_printf.h"
70#include "curl_memory.h"
71#include "memdebug.h"
72
73struct resdata {
74  struct curltime start;
75};
76
77/*
78 * Curl_resolver_global_init()
79 * Called from curl_global_init() to initialize global resolver environment.
80 * Does nothing here.
81 */
82int Curl_resolver_global_init(void)
83{
84  return CURLE_OK;
85}
86
87/*
88 * Curl_resolver_global_cleanup()
89 * Called from curl_global_cleanup() to destroy global resolver environment.
90 * Does nothing here.
91 */
92void Curl_resolver_global_cleanup(void)
93{
94}
95
96/*
97 * Curl_resolver_init()
98 * Called from curl_easy_init() -> Curl_open() to initialize resolver
99 * URL-state specific environment ('resolver' member of the UrlState
100 * structure).
101 */
102CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
103{
104  (void)easy;
105  *resolver = calloc(1, sizeof(struct resdata));
106  if(!*resolver)
107    return CURLE_OUT_OF_MEMORY;
108  return CURLE_OK;
109}
110
111/*
112 * Curl_resolver_cleanup()
113 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
114 * URL-state specific environment ('resolver' member of the UrlState
115 * structure).
116 */
117void Curl_resolver_cleanup(void *resolver)
118{
119  free(resolver);
120}
121
122/*
123 * Curl_resolver_duphandle()
124 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
125 * environment ('resolver' member of the UrlState structure).
126 */
127CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
128{
129  (void)from;
130  return Curl_resolver_init(easy, to);
131}
132
133static void destroy_async_data(struct Curl_async *);
134
135/*
136 * Cancel all possibly still on-going resolves for this connection.
137 */
138void Curl_resolver_cancel(struct Curl_easy *data)
139{
140  destroy_async_data(&data->state.async);
141}
142
143/* This function is used to init a threaded resolve */
144static bool init_resolve_thread(struct Curl_easy *data,
145                                const char *hostname, int port,
146                                const struct addrinfo *hints);
147
148#ifdef _WIN32
149/* Thread sync data used by GetAddrInfoExW for win8+ */
150struct thread_sync_data_w8
151{
152  OVERLAPPED overlapped;
153  ADDRINFOEXW_ *res;
154  HANDLE cancel_ev;
155  ADDRINFOEXW_ hints;
156};
157#endif
158
159/* Data for synchronization between resolver thread and its parent */
160struct thread_sync_data {
161#ifdef _WIN32
162  struct thread_sync_data_w8 w8;
163#endif
164  curl_mutex_t *mtx;
165  int done;
166  int port;
167  char *hostname;        /* hostname to resolve, Curl_async.hostname
168                            duplicate */
169#ifndef CURL_DISABLE_SOCKETPAIR
170  struct Curl_easy *data;
171  curl_socket_t sock_pair[2]; /* socket pair */
172#endif
173  int sock_error;
174  struct Curl_addrinfo *res;
175#ifdef HAVE_GETADDRINFO
176  struct addrinfo hints;
177#endif
178  struct thread_data *td; /* for thread-self cleanup */
179};
180
181struct thread_data {
182#ifdef _WIN32
183  HANDLE complete_ev;
184#endif
185  curl_thread_t thread_hnd;
186  unsigned int poll_interval;
187  timediff_t interval_end;
188  struct thread_sync_data tsd;
189};
190
191static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
192{
193  return &(data->state.async.tdata->tsd);
194}
195
196/* Destroy resolver thread synchronization data */
197static
198void destroy_thread_sync_data(struct thread_sync_data *tsd)
199{
200  if(tsd->mtx) {
201    Curl_mutex_destroy(tsd->mtx);
202    free(tsd->mtx);
203  }
204
205  free(tsd->hostname);
206
207  if(tsd->res)
208    Curl_freeaddrinfo(tsd->res);
209
210#ifndef CURL_DISABLE_SOCKETPAIR
211  /*
212   * close one end of the socket pair (may be done in resolver thread);
213   * the other end (for reading) is always closed in the parent thread.
214   */
215  if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
216    wakeup_close(tsd->sock_pair[1]);
217  }
218#endif
219  memset(tsd, 0, sizeof(*tsd));
220}
221
222/* Initialize resolver thread synchronization data */
223static
224int init_thread_sync_data(struct thread_data *td,
225                           const char *hostname,
226                           int port,
227                           const struct addrinfo *hints)
228{
229  struct thread_sync_data *tsd = &td->tsd;
230
231  memset(tsd, 0, sizeof(*tsd));
232
233  tsd->td = td;
234  tsd->port = port;
235  /* Treat the request as done until the thread actually starts so any early
236   * cleanup gets done properly.
237   */
238  tsd->done = 1;
239#ifdef HAVE_GETADDRINFO
240  DEBUGASSERT(hints);
241  tsd->hints = *hints;
242#else
243  (void) hints;
244#endif
245
246  tsd->mtx = malloc(sizeof(curl_mutex_t));
247  if(!tsd->mtx)
248    goto err_exit;
249
250  Curl_mutex_init(tsd->mtx);
251
252#ifndef CURL_DISABLE_SOCKETPAIR
253  /* create socket pair or pipe */
254  if(wakeup_create(&tsd->sock_pair[0]) < 0) {
255    tsd->sock_pair[0] = CURL_SOCKET_BAD;
256    tsd->sock_pair[1] = CURL_SOCKET_BAD;
257    goto err_exit;
258  }
259#endif
260  tsd->sock_error = CURL_ASYNC_SUCCESS;
261
262  /* Copying hostname string because original can be destroyed by parent
263   * thread during gethostbyname execution.
264   */
265  tsd->hostname = strdup(hostname);
266  if(!tsd->hostname)
267    goto err_exit;
268
269  return 1;
270
271err_exit:
272#ifndef CURL_DISABLE_SOCKETPAIR
273  if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
274    wakeup_close(tsd->sock_pair[0]);
275    tsd->sock_pair[0] = CURL_SOCKET_BAD;
276  }
277#endif
278  destroy_thread_sync_data(tsd);
279  return 0;
280}
281
282static CURLcode getaddrinfo_complete(struct Curl_easy *data)
283{
284  struct thread_sync_data *tsd = conn_thread_sync_data(data);
285  CURLcode result;
286
287  result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
288  /* The tsd->res structure has been copied to async.dns and perhaps the DNS
289     cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
290  */
291  tsd->res = NULL;
292
293  return result;
294}
295
296#ifdef _WIN32
297static VOID WINAPI
298query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped)
299{
300  size_t ss_size;
301  const ADDRINFOEXW_ *ai;
302  struct Curl_addrinfo *ca;
303  struct Curl_addrinfo *cafirst = NULL;
304  struct Curl_addrinfo *calast = NULL;
305#ifdef __clang__
306#pragma clang diagnostic push
307#pragma clang diagnostic ignored "-Wcast-align"
308#endif
309  struct thread_sync_data *tsd =
310    CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped);
311#ifdef __clang__
312#pragma clang diagnostic pop
313#endif
314  struct thread_data *td = tsd->td;
315  const ADDRINFOEXW_ *res = tsd->w8.res;
316  int error = (int)err;
317  (void)bytes;
318
319  if(error == ERROR_SUCCESS) {
320    /* traverse the addrinfo list */
321
322    for(ai = res; ai != NULL; ai = ai->ai_next) {
323      size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0;
324      /* ignore elements with unsupported address family, */
325      /* settle family-specific sockaddr structure size.  */
326      if(ai->ai_family == AF_INET)
327        ss_size = sizeof(struct sockaddr_in);
328#ifdef ENABLE_IPV6
329      else if(ai->ai_family == AF_INET6)
330        ss_size = sizeof(struct sockaddr_in6);
331#endif
332      else
333        continue;
334
335      /* ignore elements without required address info */
336      if(!ai->ai_addr || !(ai->ai_addrlen > 0))
337        continue;
338
339      /* ignore elements with bogus address size */
340      if((size_t)ai->ai_addrlen < ss_size)
341        continue;
342
343      ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen);
344      if(!ca) {
345        error = EAI_MEMORY;
346        break;
347      }
348
349      /* copy each structure member individually, member ordering, */
350      /* size, or padding might be different for each platform.    */
351      ca->ai_flags     = ai->ai_flags;
352      ca->ai_family    = ai->ai_family;
353      ca->ai_socktype  = ai->ai_socktype;
354      ca->ai_protocol  = ai->ai_protocol;
355      ca->ai_addrlen   = (curl_socklen_t)ss_size;
356      ca->ai_addr      = NULL;
357      ca->ai_canonname = NULL;
358      ca->ai_next      = NULL;
359
360      ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
361      memcpy(ca->ai_addr, ai->ai_addr, ss_size);
362
363      if(namelen) {
364        size_t i;
365        ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
366        for(i = 0; i < namelen; ++i) /* convert wide string to ascii */
367          ca->ai_canonname[i] = (char)ai->ai_canonname[i];
368        ca->ai_canonname[namelen] = '\0';
369      }
370
371      /* if the return list is empty, this becomes the first element */
372      if(!cafirst)
373        cafirst = ca;
374
375      /* add this element last in the return list */
376      if(calast)
377        calast->ai_next = ca;
378      calast = ca;
379    }
380
381    /* if we failed, also destroy the Curl_addrinfo list */
382    if(error) {
383      Curl_freeaddrinfo(cafirst);
384      cafirst = NULL;
385    }
386    else if(!cafirst) {
387#ifdef EAI_NONAME
388      /* rfc3493 conformant */
389      error = EAI_NONAME;
390#else
391      /* rfc3493 obsoleted */
392      error = EAI_NODATA;
393#endif
394#ifdef USE_WINSOCK
395      SET_SOCKERRNO(error);
396#endif
397    }
398    tsd->res = cafirst;
399  }
400
401  if(tsd->w8.res) {
402    Curl_FreeAddrInfoExW(tsd->w8.res);
403    tsd->w8.res = NULL;
404  }
405
406  if(error) {
407    tsd->sock_error = SOCKERRNO?SOCKERRNO:error;
408    if(tsd->sock_error == 0)
409      tsd->sock_error = RESOLVER_ENOMEM;
410  }
411  else {
412    Curl_addrinfo_set_port(tsd->res, tsd->port);
413  }
414
415  Curl_mutex_acquire(tsd->mtx);
416  if(tsd->done) {
417    /* too late, gotta clean up the mess */
418    Curl_mutex_release(tsd->mtx);
419    destroy_thread_sync_data(tsd);
420    free(td);
421  }
422  else {
423#ifndef CURL_DISABLE_SOCKETPAIR
424    char buf[1];
425    if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
426      /* DNS has been resolved, signal client task */
427      buf[0] = 1;
428      if(swrite(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
429        /* update sock_erro to errno */
430        tsd->sock_error = SOCKERRNO;
431      }
432    }
433#endif
434    tsd->done = 1;
435    Curl_mutex_release(tsd->mtx);
436    if(td->complete_ev)
437      SetEvent(td->complete_ev); /* Notify caller that the query completed */
438  }
439}
440#endif
441
442#ifdef HAVE_GETADDRINFO
443
444/*
445 * getaddrinfo_thread() resolves a name and then exits.
446 *
447 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
448 * and wait on it.
449 */
450static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
451{
452  struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
453  struct thread_data *td = tsd->td;
454  char service[12];
455  int rc;
456#ifndef CURL_DISABLE_SOCKETPAIR
457  char buf[1];
458#endif
459
460  msnprintf(service, sizeof(service), "%d", tsd->port);
461
462  rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
463
464  if(rc) {
465    tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
466    if(tsd->sock_error == 0)
467      tsd->sock_error = RESOLVER_ENOMEM;
468  }
469  else {
470    Curl_addrinfo_set_port(tsd->res, tsd->port);
471  }
472
473  Curl_mutex_acquire(tsd->mtx);
474  if(tsd->done) {
475    /* too late, gotta clean up the mess */
476    Curl_mutex_release(tsd->mtx);
477    destroy_thread_sync_data(tsd);
478    free(td);
479  }
480  else {
481#ifndef CURL_DISABLE_SOCKETPAIR
482    if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
483      /* DNS has been resolved, signal client task */
484      buf[0] = 1;
485      if(wakeup_write(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
486        /* update sock_erro to errno */
487        tsd->sock_error = SOCKERRNO;
488      }
489    }
490#endif
491    tsd->done = 1;
492    Curl_mutex_release(tsd->mtx);
493  }
494
495  return 0;
496}
497
498#else /* HAVE_GETADDRINFO */
499
500/*
501 * gethostbyname_thread() resolves a name and then exits.
502 */
503static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
504{
505  struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
506  struct thread_data *td = tsd->td;
507
508  tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
509
510  if(!tsd->res) {
511    tsd->sock_error = SOCKERRNO;
512    if(tsd->sock_error == 0)
513      tsd->sock_error = RESOLVER_ENOMEM;
514  }
515
516  Curl_mutex_acquire(tsd->mtx);
517  if(tsd->done) {
518    /* too late, gotta clean up the mess */
519    Curl_mutex_release(tsd->mtx);
520    destroy_thread_sync_data(tsd);
521    free(td);
522  }
523  else {
524    tsd->done = 1;
525    Curl_mutex_release(tsd->mtx);
526  }
527
528  return 0;
529}
530
531#endif /* HAVE_GETADDRINFO */
532
533/*
534 * destroy_async_data() cleans up async resolver data and thread handle.
535 */
536static void destroy_async_data(struct Curl_async *async)
537{
538  if(async->tdata) {
539    struct thread_data *td = async->tdata;
540    int done;
541#ifndef CURL_DISABLE_SOCKETPAIR
542    curl_socket_t sock_rd = td->tsd.sock_pair[0];
543    struct Curl_easy *data = td->tsd.data;
544#endif
545
546    /*
547     * if the thread is still blocking in the resolve syscall, detach it and
548     * let the thread do the cleanup...
549     */
550    Curl_mutex_acquire(td->tsd.mtx);
551    done = td->tsd.done;
552    td->tsd.done = 1;
553    Curl_mutex_release(td->tsd.mtx);
554
555    if(!done) {
556#ifdef _WIN32
557      if(td->complete_ev)
558        CloseHandle(td->complete_ev);
559      else
560#endif
561      Curl_thread_destroy(td->thread_hnd);
562    }
563    else {
564#ifdef _WIN32
565      if(td->complete_ev) {
566        Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
567        WaitForSingleObject(td->complete_ev, INFINITE);
568        CloseHandle(td->complete_ev);
569      }
570#endif
571      if(td->thread_hnd != curl_thread_t_null)
572        Curl_thread_join(&td->thread_hnd);
573
574      destroy_thread_sync_data(&td->tsd);
575
576      free(async->tdata);
577    }
578#ifndef CURL_DISABLE_SOCKETPAIR
579    /*
580     * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
581     * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
582     */
583    Curl_multi_closed(data, sock_rd);
584    sclose(sock_rd);
585#endif
586  }
587  async->tdata = NULL;
588
589  free(async->hostname);
590  async->hostname = NULL;
591}
592
593/*
594 * init_resolve_thread() starts a new thread that performs the actual
595 * resolve. This function returns before the resolve is done.
596 *
597 * Returns FALSE in case of failure, otherwise TRUE.
598 */
599static bool init_resolve_thread(struct Curl_easy *data,
600                                const char *hostname, int port,
601                                const struct addrinfo *hints)
602{
603  struct thread_data *td = calloc(1, sizeof(struct thread_data));
604  int err = ENOMEM;
605  struct Curl_async *asp = &data->state.async;
606
607  data->state.async.tdata = td;
608  if(!td)
609    goto errno_exit;
610
611  asp->port = port;
612  asp->done = FALSE;
613  asp->status = 0;
614  asp->dns = NULL;
615  td->thread_hnd = curl_thread_t_null;
616#ifdef _WIN32
617  td->complete_ev = NULL;
618#endif
619
620  if(!init_thread_sync_data(td, hostname, port, hints)) {
621    asp->tdata = NULL;
622    free(td);
623    goto errno_exit;
624  }
625
626  free(asp->hostname);
627  asp->hostname = strdup(hostname);
628  if(!asp->hostname)
629    goto err_exit;
630
631  /* The thread will set this to 1 when complete. */
632  td->tsd.done = 0;
633
634#ifdef _WIN32
635  if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW &&
636     Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) {
637#define MAX_NAME_LEN 256 /* max domain name is 253 chars */
638#define MAX_PORT_LEN 8
639    WCHAR namebuf[MAX_NAME_LEN];
640    WCHAR portbuf[MAX_PORT_LEN];
641    /* calculate required length */
642    int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname,
643                                    -1, NULL, 0);
644    if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
645      /* do utf8 conversion */
646      w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len);
647      if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
648        swprintf(portbuf, MAX_PORT_LEN, L"%d", port);
649        td->tsd.w8.hints.ai_family = hints->ai_family;
650        td->tsd.w8.hints.ai_socktype = hints->ai_socktype;
651        td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL);
652        if(!td->complete_ev) {
653          /* failed to start, mark it as done here for proper cleanup. */
654          td->tsd.done = 1;
655          goto err_exit;
656        }
657        err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS,
658                                  NULL, &td->tsd.w8.hints, &td->tsd.w8.res,
659                                  NULL, &td->tsd.w8.overlapped,
660                                  &query_complete, &td->tsd.w8.cancel_ev);
661        if(err != WSA_IO_PENDING)
662          query_complete(err, 0, &td->tsd.w8.overlapped);
663        return TRUE;
664      }
665    }
666  }
667#endif
668
669#ifdef HAVE_GETADDRINFO
670  td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
671#else
672  td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
673#endif
674
675  if(!td->thread_hnd) {
676    /* The thread never started, so mark it as done here for proper cleanup. */
677    td->tsd.done = 1;
678    err = errno;
679    goto err_exit;
680  }
681
682  return TRUE;
683
684err_exit:
685  destroy_async_data(asp);
686
687errno_exit:
688  errno = err;
689  return FALSE;
690}
691
692/*
693 * 'entry' may be NULL and then no data is returned
694 */
695static CURLcode thread_wait_resolv(struct Curl_easy *data,
696                                   struct Curl_dns_entry **entry,
697                                   bool report)
698{
699  struct thread_data *td;
700  CURLcode result = CURLE_OK;
701
702  DEBUGASSERT(data);
703  td = data->state.async.tdata;
704  DEBUGASSERT(td);
705#ifdef _WIN32
706  DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
707#else
708  DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
709#endif
710
711  /* wait for the thread to resolve the name */
712#ifdef _WIN32
713  if(td->complete_ev) {
714    WaitForSingleObject(td->complete_ev, INFINITE);
715    CloseHandle(td->complete_ev);
716    if(entry)
717      result = getaddrinfo_complete(data);
718  }
719  else
720#endif
721  if(Curl_thread_join(&td->thread_hnd)) {
722    if(entry)
723      result = getaddrinfo_complete(data);
724  }
725  else
726    DEBUGASSERT(0);
727
728  data->state.async.done = TRUE;
729
730  if(entry)
731    *entry = data->state.async.dns;
732
733  if(!data->state.async.dns && report)
734    /* a name was not resolved, report error */
735    result = Curl_resolver_error(data);
736
737  destroy_async_data(&data->state.async);
738
739  if(!data->state.async.dns && report)
740    connclose(data->conn, "asynch resolve failed");
741
742  return result;
743}
744
745
746/*
747 * Until we gain a way to signal the resolver threads to stop early, we must
748 * simply wait for them and ignore their results.
749 */
750void Curl_resolver_kill(struct Curl_easy *data)
751{
752  struct thread_data *td = data->state.async.tdata;
753
754  /* If we're still resolving, we must wait for the threads to fully clean up,
755     unfortunately.  Otherwise, we can simply cancel to clean up any resolver
756     data. */
757  if(td && td->thread_hnd != curl_thread_t_null
758     && (data->set.quick_exit != 1L))
759    (void)thread_wait_resolv(data, NULL, FALSE);
760  else
761    Curl_resolver_cancel(data);
762}
763
764/*
765 * Curl_resolver_wait_resolv()
766 *
767 * Waits for a resolve to finish. This function should be avoided since using
768 * this risk getting the multi interface to "hang".
769 *
770 * If 'entry' is non-NULL, make it point to the resolved dns entry
771 *
772 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
773 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
774 *
775 * This is the version for resolves-in-a-thread.
776 */
777CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
778                                   struct Curl_dns_entry **entry)
779{
780  return thread_wait_resolv(data, entry, TRUE);
781}
782
783/*
784 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
785 * name resolve request has completed. It should also make sure to time-out if
786 * the operation seems to take too long.
787 */
788CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
789                                   struct Curl_dns_entry **entry)
790{
791  struct thread_data *td = data->state.async.tdata;
792  int done = 0;
793
794  DEBUGASSERT(entry);
795  *entry = NULL;
796
797  if(!td) {
798    DEBUGASSERT(td);
799    return CURLE_COULDNT_RESOLVE_HOST;
800  }
801
802  Curl_mutex_acquire(td->tsd.mtx);
803  done = td->tsd.done;
804  Curl_mutex_release(td->tsd.mtx);
805
806  if(done) {
807    getaddrinfo_complete(data);
808
809    if(!data->state.async.dns) {
810      CURLcode result = Curl_resolver_error(data);
811      destroy_async_data(&data->state.async);
812      return result;
813    }
814    destroy_async_data(&data->state.async);
815    *entry = data->state.async.dns;
816  }
817  else {
818    /* poll for name lookup done with exponential backoff up to 250ms */
819    /* should be fine even if this converts to 32 bit */
820    timediff_t elapsed = Curl_timediff(Curl_now(),
821                                       data->progress.t_startsingle);
822    if(elapsed < 0)
823      elapsed = 0;
824
825    if(td->poll_interval == 0)
826      /* Start at 1ms poll interval */
827      td->poll_interval = 1;
828    else if(elapsed >= td->interval_end)
829      /* Back-off exponentially if last interval expired  */
830      td->poll_interval *= 2;
831
832    if(td->poll_interval > 250)
833      td->poll_interval = 250;
834
835    td->interval_end = elapsed + td->poll_interval;
836    Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
837  }
838
839  return CURLE_OK;
840}
841
842int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
843{
844  int ret_val = 0;
845  timediff_t milli;
846  timediff_t ms;
847  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
848#ifndef CURL_DISABLE_SOCKETPAIR
849  struct thread_data *td = data->state.async.tdata;
850#else
851  (void)socks;
852#endif
853
854#ifndef CURL_DISABLE_SOCKETPAIR
855  if(td) {
856    /* return read fd to client for polling the DNS resolution status */
857    socks[0] = td->tsd.sock_pair[0];
858    td->tsd.data = data;
859    ret_val = GETSOCK_READSOCK(0);
860  }
861  else {
862#endif
863    ms = Curl_timediff(Curl_now(), reslv->start);
864    if(ms < 3)
865      milli = 0;
866    else if(ms <= 50)
867      milli = ms/3;
868    else if(ms <= 250)
869      milli = 50;
870    else
871      milli = 200;
872    Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
873#ifndef CURL_DISABLE_SOCKETPAIR
874  }
875#endif
876
877
878  return ret_val;
879}
880
881#ifndef HAVE_GETADDRINFO
882/*
883 * Curl_getaddrinfo() - for platforms without getaddrinfo
884 */
885struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
886                                                const char *hostname,
887                                                int port,
888                                                int *waitp)
889{
890  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
891
892  *waitp = 0; /* default to synchronous response */
893
894  reslv->start = Curl_now();
895
896  /* fire up a new resolver thread! */
897  if(init_resolve_thread(data, hostname, port, NULL)) {
898    *waitp = 1; /* expect asynchronous response */
899    return NULL;
900  }
901
902  failf(data, "getaddrinfo() thread failed");
903
904  return NULL;
905}
906
907#else /* !HAVE_GETADDRINFO */
908
909/*
910 * Curl_resolver_getaddrinfo() - for getaddrinfo
911 */
912struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
913                                                const char *hostname,
914                                                int port,
915                                                int *waitp)
916{
917  struct addrinfo hints;
918  int pf = PF_INET;
919  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
920
921  *waitp = 0; /* default to synchronous response */
922
923#ifdef CURLRES_IPV6
924  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
925    /* The stack seems to be IPv6-enabled */
926    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
927      pf = PF_INET6;
928    else
929      pf = PF_UNSPEC;
930  }
931#endif /* CURLRES_IPV6 */
932
933  memset(&hints, 0, sizeof(hints));
934  hints.ai_family = pf;
935  hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
936    SOCK_STREAM : SOCK_DGRAM;
937
938  reslv->start = Curl_now();
939  /* fire up a new resolver thread! */
940  if(init_resolve_thread(data, hostname, port, &hints)) {
941    *waitp = 1; /* expect asynchronous response */
942    return NULL;
943  }
944
945  failf(data, "getaddrinfo() thread failed to start");
946  return NULL;
947
948}
949
950#endif /* !HAVE_GETADDRINFO */
951
952CURLcode Curl_set_dns_servers(struct Curl_easy *data,
953                              char *servers)
954{
955  (void)data;
956  (void)servers;
957  return CURLE_NOT_BUILT_IN;
958
959}
960
961CURLcode Curl_set_dns_interface(struct Curl_easy *data,
962                                const char *interf)
963{
964  (void)data;
965  (void)interf;
966  return CURLE_NOT_BUILT_IN;
967}
968
969CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
970                                const char *local_ip4)
971{
972  (void)data;
973  (void)local_ip4;
974  return CURLE_NOT_BUILT_IN;
975}
976
977CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
978                                const char *local_ip6)
979{
980  (void)data;
981  (void)local_ip6;
982  return CURLE_NOT_BUILT_IN;
983}
984
985#endif /* CURLRES_THREADED */
986