Coverage Report

Created: 2024-05-21 06:52

/src/curl/lib/asyn-thread.c
Line
Count
Source (jump to first uncovered line)
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
0
#  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
73
struct 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
 */
82
int Curl_resolver_global_init(void)
83
15
{
84
15
  return CURLE_OK;
85
15
}
86
87
/*
88
 * Curl_resolver_global_cleanup()
89
 * Called from curl_global_cleanup() to destroy global resolver environment.
90
 * Does nothing here.
91
 */
92
void Curl_resolver_global_cleanup(void)
93
0
{
94
0
}
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
 */
102
CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
103
165k
{
104
165k
  (void)easy;
105
165k
  *resolver = calloc(1, sizeof(struct resdata));
106
165k
  if(!*resolver)
107
0
    return CURLE_OUT_OF_MEMORY;
108
165k
  return CURLE_OK;
109
165k
}
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
 */
117
void Curl_resolver_cleanup(void *resolver)
118
165k
{
119
165k
  free(resolver);
120
165k
}
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
 */
127
CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
128
0
{
129
0
  (void)from;
130
0
  return Curl_resolver_init(easy, to);
131
0
}
132
133
static void destroy_async_data(struct Curl_async *);
134
135
/*
136
 * Cancel all possibly still on-going resolves for this connection.
137
 */
138
void Curl_resolver_cancel(struct Curl_easy *data)
139
301k
{
140
301k
  destroy_async_data(&data->state.async);
141
301k
}
142
143
/* This function is used to init a threaded resolve */
144
static 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+ */
150
struct 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 */
160
struct 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
181
struct 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
191
static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
192
0
{
193
0
  return &(data->state.async.tdata->tsd);
194
0
}
195
196
/* Destroy resolver thread synchronization data */
197
static
198
void destroy_thread_sync_data(struct thread_sync_data *tsd)
199
0
{
200
0
  if(tsd->mtx) {
201
0
    Curl_mutex_destroy(tsd->mtx);
202
0
    free(tsd->mtx);
203
0
  }
204
205
0
  free(tsd->hostname);
206
207
0
  if(tsd->res)
208
0
    Curl_freeaddrinfo(tsd->res);
209
210
0
#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
0
  if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
216
0
    wakeup_close(tsd->sock_pair[1]);
217
0
  }
218
0
#endif
219
0
  memset(tsd, 0, sizeof(*tsd));
220
0
}
221
222
/* Initialize resolver thread synchronization data */
223
static
224
int init_thread_sync_data(struct thread_data *td,
225
                           const char *hostname,
226
                           int port,
227
                           const struct addrinfo *hints)
228
0
{
229
0
  struct thread_sync_data *tsd = &td->tsd;
230
231
0
  memset(tsd, 0, sizeof(*tsd));
232
233
0
  tsd->td = td;
234
0
  tsd->port = port;
235
  /* Treat the request as done until the thread actually starts so any early
236
   * cleanup gets done properly.
237
   */
238
0
  tsd->done = 1;
239
0
#ifdef HAVE_GETADDRINFO
240
0
  DEBUGASSERT(hints);
241
0
  tsd->hints = *hints;
242
#else
243
  (void) hints;
244
#endif
245
246
0
  tsd->mtx = malloc(sizeof(curl_mutex_t));
247
0
  if(!tsd->mtx)
248
0
    goto err_exit;
249
250
0
  Curl_mutex_init(tsd->mtx);
251
252
0
#ifndef CURL_DISABLE_SOCKETPAIR
253
  /* create socket pair or pipe */
254
0
  if(wakeup_create(&tsd->sock_pair[0]) < 0) {
255
0
    tsd->sock_pair[0] = CURL_SOCKET_BAD;
256
0
    tsd->sock_pair[1] = CURL_SOCKET_BAD;
257
0
    goto err_exit;
258
0
  }
259
0
#endif
260
0
  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
0
  tsd->hostname = strdup(hostname);
266
0
  if(!tsd->hostname)
267
0
    goto err_exit;
268
269
0
  return 1;
270
271
0
err_exit:
272
0
#ifndef CURL_DISABLE_SOCKETPAIR
273
0
  if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
274
0
    wakeup_close(tsd->sock_pair[0]);
275
0
    tsd->sock_pair[0] = CURL_SOCKET_BAD;
276
0
  }
277
0
#endif
278
0
  destroy_thread_sync_data(tsd);
279
0
  return 0;
280
0
}
281
282
static CURLcode getaddrinfo_complete(struct Curl_easy *data)
283
0
{
284
0
  struct thread_sync_data *tsd = conn_thread_sync_data(data);
285
0
  CURLcode result;
286
287
0
  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
0
  tsd->res = NULL;
292
293
0
  return result;
294
0
}
295
296
#ifdef _WIN32
297
static VOID WINAPI
298
query_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 USE_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 USE_IPV6, create a resolver thread
448
 * and wait on it.
449
 */
450
static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
451
0
{
452
0
  struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
453
0
  struct thread_data *td = tsd->td;
454
0
  char service[12];
455
0
  int rc;
456
0
#ifndef CURL_DISABLE_SOCKETPAIR
457
0
  char buf[1];
458
0
#endif
459
460
0
  msnprintf(service, sizeof(service), "%d", tsd->port);
461
462
0
  rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
463
464
0
  if(rc) {
465
0
    tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
466
0
    if(tsd->sock_error == 0)
467
0
      tsd->sock_error = RESOLVER_ENOMEM;
468
0
  }
469
0
  else {
470
0
    Curl_addrinfo_set_port(tsd->res, tsd->port);
471
0
  }
472
473
0
  Curl_mutex_acquire(tsd->mtx);
474
0
  if(tsd->done) {
475
    /* too late, gotta clean up the mess */
476
0
    Curl_mutex_release(tsd->mtx);
477
0
    destroy_thread_sync_data(tsd);
478
0
    free(td);
479
0
  }
480
0
  else {
481
0
#ifndef CURL_DISABLE_SOCKETPAIR
482
0
    if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
483
      /* DNS has been resolved, signal client task */
484
0
      buf[0] = 1;
485
0
      if(wakeup_write(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
486
        /* update sock_erro to errno */
487
0
        tsd->sock_error = SOCKERRNO;
488
0
      }
489
0
    }
490
0
#endif
491
0
    tsd->done = 1;
492
0
    Curl_mutex_release(tsd->mtx);
493
0
  }
494
495
0
  return 0;
496
0
}
497
498
#else /* HAVE_GETADDRINFO */
499
500
/*
501
 * gethostbyname_thread() resolves a name and then exits.
502
 */
503
static 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
 */
536
static void destroy_async_data(struct Curl_async *async)
537
301k
{
538
301k
  if(async->tdata) {
539
0
    struct thread_data *td = async->tdata;
540
0
    int done;
541
0
#ifndef CURL_DISABLE_SOCKETPAIR
542
0
    curl_socket_t sock_rd = td->tsd.sock_pair[0];
543
0
    struct Curl_easy *data = td->tsd.data;
544
0
#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
0
    Curl_mutex_acquire(td->tsd.mtx);
551
0
    done = td->tsd.done;
552
0
    td->tsd.done = 1;
553
0
    Curl_mutex_release(td->tsd.mtx);
554
555
0
    if(!done) {
556
#ifdef _WIN32
557
      if(td->complete_ev) {
558
        CloseHandle(td->complete_ev);
559
        td->complete_ev = NULL;
560
      }
561
#endif
562
0
      if(td->thread_hnd != curl_thread_t_null) {
563
0
        Curl_thread_destroy(td->thread_hnd);
564
0
        td->thread_hnd = curl_thread_t_null;
565
0
      }
566
0
    }
567
0
    else {
568
#ifdef _WIN32
569
      if(td->complete_ev) {
570
        Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
571
        WaitForSingleObject(td->complete_ev, INFINITE);
572
        CloseHandle(td->complete_ev);
573
        td->complete_ev = NULL;
574
      }
575
#endif
576
0
      if(td->thread_hnd != curl_thread_t_null)
577
0
        Curl_thread_join(&td->thread_hnd);
578
579
0
      destroy_thread_sync_data(&td->tsd);
580
581
0
      free(async->tdata);
582
0
    }
583
0
#ifndef CURL_DISABLE_SOCKETPAIR
584
    /*
585
     * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
586
     * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
587
     */
588
0
    Curl_multi_closed(data, sock_rd);
589
0
    wakeup_close(sock_rd);
590
0
#endif
591
0
  }
592
301k
  async->tdata = NULL;
593
594
301k
  free(async->hostname);
595
301k
  async->hostname = NULL;
596
301k
}
597
598
/*
599
 * init_resolve_thread() starts a new thread that performs the actual
600
 * resolve. This function returns before the resolve is done.
601
 *
602
 * Returns FALSE in case of failure, otherwise TRUE.
603
 */
604
static bool init_resolve_thread(struct Curl_easy *data,
605
                                const char *hostname, int port,
606
                                const struct addrinfo *hints)
607
0
{
608
0
  struct thread_data *td = calloc(1, sizeof(struct thread_data));
609
0
  int err = ENOMEM;
610
0
  struct Curl_async *asp = &data->state.async;
611
612
0
  data->state.async.tdata = td;
613
0
  if(!td)
614
0
    goto errno_exit;
615
616
0
  asp->port = port;
617
0
  asp->done = FALSE;
618
0
  asp->status = 0;
619
0
  asp->dns = NULL;
620
0
  td->thread_hnd = curl_thread_t_null;
621
#ifdef _WIN32
622
  td->complete_ev = NULL;
623
#endif
624
625
0
  if(!init_thread_sync_data(td, hostname, port, hints)) {
626
0
    asp->tdata = NULL;
627
0
    free(td);
628
0
    goto errno_exit;
629
0
  }
630
631
0
  free(asp->hostname);
632
0
  asp->hostname = strdup(hostname);
633
0
  if(!asp->hostname)
634
0
    goto err_exit;
635
636
  /* The thread will set this to 1 when complete. */
637
0
  td->tsd.done = 0;
638
639
#ifdef _WIN32
640
  if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW &&
641
     Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) {
642
#define MAX_NAME_LEN 256 /* max domain name is 253 chars */
643
#define MAX_PORT_LEN 8
644
    WCHAR namebuf[MAX_NAME_LEN];
645
    WCHAR portbuf[MAX_PORT_LEN];
646
    /* calculate required length */
647
    int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname,
648
                                    -1, NULL, 0);
649
    if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
650
      /* do utf8 conversion */
651
      w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len);
652
      if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
653
        swprintf(portbuf, MAX_PORT_LEN, L"%d", port);
654
        td->tsd.w8.hints.ai_family = hints->ai_family;
655
        td->tsd.w8.hints.ai_socktype = hints->ai_socktype;
656
        td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL);
657
        if(!td->complete_ev) {
658
          /* failed to start, mark it as done here for proper cleanup. */
659
          td->tsd.done = 1;
660
          goto err_exit;
661
        }
662
        err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS,
663
                                  NULL, &td->tsd.w8.hints, &td->tsd.w8.res,
664
                                  NULL, &td->tsd.w8.overlapped,
665
                                  &query_complete, &td->tsd.w8.cancel_ev);
666
        if(err != WSA_IO_PENDING)
667
          query_complete(err, 0, &td->tsd.w8.overlapped);
668
        return TRUE;
669
      }
670
    }
671
  }
672
#endif
673
674
0
#ifdef HAVE_GETADDRINFO
675
0
  td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
676
#else
677
  td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
678
#endif
679
680
0
  if(td->thread_hnd == curl_thread_t_null) {
681
    /* The thread never started, so mark it as done here for proper cleanup. */
682
0
    td->tsd.done = 1;
683
0
    err = errno;
684
0
    goto err_exit;
685
0
  }
686
687
0
  return TRUE;
688
689
0
err_exit:
690
0
  destroy_async_data(asp);
691
692
0
errno_exit:
693
0
  errno = err;
694
0
  return FALSE;
695
0
}
696
697
/*
698
 * 'entry' may be NULL and then no data is returned
699
 */
700
static CURLcode thread_wait_resolv(struct Curl_easy *data,
701
                                   struct Curl_dns_entry **entry,
702
                                   bool report)
703
0
{
704
0
  struct thread_data *td;
705
0
  CURLcode result = CURLE_OK;
706
707
0
  DEBUGASSERT(data);
708
0
  td = data->state.async.tdata;
709
0
  DEBUGASSERT(td);
710
#ifdef _WIN32
711
  DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
712
#else
713
0
  DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
714
0
#endif
715
716
  /* wait for the thread to resolve the name */
717
#ifdef _WIN32
718
  if(td->complete_ev) {
719
    WaitForSingleObject(td->complete_ev, INFINITE);
720
    CloseHandle(td->complete_ev);
721
    td->complete_ev = NULL;
722
    if(entry)
723
      result = getaddrinfo_complete(data);
724
  }
725
  else
726
#endif
727
0
  if(Curl_thread_join(&td->thread_hnd)) {
728
0
    if(entry)
729
0
      result = getaddrinfo_complete(data);
730
0
  }
731
0
  else
732
0
    DEBUGASSERT(0);
733
734
0
  data->state.async.done = TRUE;
735
736
0
  if(entry)
737
0
    *entry = data->state.async.dns;
738
739
0
  if(!data->state.async.dns && report)
740
    /* a name was not resolved, report error */
741
0
    result = Curl_resolver_error(data);
742
743
0
  destroy_async_data(&data->state.async);
744
745
0
  if(!data->state.async.dns && report)
746
0
    connclose(data->conn, "asynch resolve failed");
747
748
0
  return result;
749
0
}
750
751
752
/*
753
 * Until we gain a way to signal the resolver threads to stop early, we must
754
 * simply wait for them and ignore their results.
755
 */
756
void Curl_resolver_kill(struct Curl_easy *data)
757
59.5k
{
758
59.5k
  struct thread_data *td = data->state.async.tdata;
759
760
  /* If we're still resolving, we must wait for the threads to fully clean up,
761
     unfortunately.  Otherwise, we can simply cancel to clean up any resolver
762
     data. */
763
#ifdef _WIN32
764
  if(td && td->complete_ev) {
765
    Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
766
    (void)thread_wait_resolv(data, NULL, FALSE);
767
  }
768
  else
769
#endif
770
59.5k
  if(td && td->thread_hnd != curl_thread_t_null
771
59.5k
     && (data->set.quick_exit != 1L))
772
0
    (void)thread_wait_resolv(data, NULL, FALSE);
773
59.5k
  else
774
59.5k
    Curl_resolver_cancel(data);
775
59.5k
}
776
777
/*
778
 * Curl_resolver_wait_resolv()
779
 *
780
 * Waits for a resolve to finish. This function should be avoided since using
781
 * this risk getting the multi interface to "hang".
782
 *
783
 * If 'entry' is non-NULL, make it point to the resolved dns entry
784
 *
785
 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
786
 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
787
 *
788
 * This is the version for resolves-in-a-thread.
789
 */
790
CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
791
                                   struct Curl_dns_entry **entry)
792
0
{
793
0
  return thread_wait_resolv(data, entry, TRUE);
794
0
}
795
796
/*
797
 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
798
 * name resolve request has completed. It should also make sure to time-out if
799
 * the operation seems to take too long.
800
 */
801
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
802
                                   struct Curl_dns_entry **entry)
803
0
{
804
0
  struct thread_data *td = data->state.async.tdata;
805
0
  int done = 0;
806
807
0
  DEBUGASSERT(entry);
808
0
  *entry = NULL;
809
810
0
  if(!td) {
811
0
    DEBUGASSERT(td);
812
0
    return CURLE_COULDNT_RESOLVE_HOST;
813
0
  }
814
815
0
  Curl_mutex_acquire(td->tsd.mtx);
816
0
  done = td->tsd.done;
817
0
  Curl_mutex_release(td->tsd.mtx);
818
819
0
  if(done) {
820
0
    getaddrinfo_complete(data);
821
822
0
    if(!data->state.async.dns) {
823
0
      CURLcode result = Curl_resolver_error(data);
824
0
      destroy_async_data(&data->state.async);
825
0
      return result;
826
0
    }
827
0
    destroy_async_data(&data->state.async);
828
0
    *entry = data->state.async.dns;
829
0
  }
830
0
  else {
831
    /* poll for name lookup done with exponential backoff up to 250ms */
832
    /* should be fine even if this converts to 32 bit */
833
0
    timediff_t elapsed = Curl_timediff(Curl_now(),
834
0
                                       data->progress.t_startsingle);
835
0
    if(elapsed < 0)
836
0
      elapsed = 0;
837
838
0
    if(td->poll_interval == 0)
839
      /* Start at 1ms poll interval */
840
0
      td->poll_interval = 1;
841
0
    else if(elapsed >= td->interval_end)
842
      /* Back-off exponentially if last interval expired  */
843
0
      td->poll_interval *= 2;
844
845
0
    if(td->poll_interval > 250)
846
0
      td->poll_interval = 250;
847
848
0
    td->interval_end = elapsed + td->poll_interval;
849
0
    Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
850
0
  }
851
852
0
  return CURLE_OK;
853
0
}
854
855
int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
856
0
{
857
0
  int ret_val = 0;
858
0
  timediff_t milli;
859
0
  timediff_t ms;
860
0
  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
861
0
#ifndef CURL_DISABLE_SOCKETPAIR
862
0
  struct thread_data *td = data->state.async.tdata;
863
#else
864
  (void)socks;
865
#endif
866
867
0
#ifndef CURL_DISABLE_SOCKETPAIR
868
0
  if(td) {
869
    /* return read fd to client for polling the DNS resolution status */
870
0
    socks[0] = td->tsd.sock_pair[0];
871
0
    td->tsd.data = data;
872
0
    ret_val = GETSOCK_READSOCK(0);
873
0
  }
874
0
  else {
875
0
#endif
876
0
    ms = Curl_timediff(Curl_now(), reslv->start);
877
0
    if(ms < 3)
878
0
      milli = 0;
879
0
    else if(ms <= 50)
880
0
      milli = ms/3;
881
0
    else if(ms <= 250)
882
0
      milli = 50;
883
0
    else
884
0
      milli = 200;
885
0
    Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
886
0
#ifndef CURL_DISABLE_SOCKETPAIR
887
0
  }
888
0
#endif
889
890
891
0
  return ret_val;
892
0
}
893
894
#ifndef HAVE_GETADDRINFO
895
/*
896
 * Curl_getaddrinfo() - for platforms without getaddrinfo
897
 */
898
struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
899
                                                const char *hostname,
900
                                                int port,
901
                                                int *waitp)
902
{
903
  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
904
905
  *waitp = 0; /* default to synchronous response */
906
907
  reslv->start = Curl_now();
908
909
  /* fire up a new resolver thread! */
910
  if(init_resolve_thread(data, hostname, port, NULL)) {
911
    *waitp = 1; /* expect asynchronous response */
912
    return NULL;
913
  }
914
915
  failf(data, "getaddrinfo() thread failed");
916
917
  return NULL;
918
}
919
920
#else /* !HAVE_GETADDRINFO */
921
922
/*
923
 * Curl_resolver_getaddrinfo() - for getaddrinfo
924
 */
925
struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
926
                                                const char *hostname,
927
                                                int port,
928
                                                int *waitp)
929
0
{
930
0
  struct addrinfo hints;
931
0
  int pf = PF_INET;
932
0
  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
933
934
0
  *waitp = 0; /* default to synchronous response */
935
936
0
#ifdef CURLRES_IPV6
937
0
  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
938
    /* The stack seems to be IPv6-enabled */
939
0
    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
940
0
      pf = PF_INET6;
941
0
    else
942
0
      pf = PF_UNSPEC;
943
0
  }
944
0
#endif /* CURLRES_IPV6 */
945
946
0
  memset(&hints, 0, sizeof(hints));
947
0
  hints.ai_family = pf;
948
0
  hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
949
0
    SOCK_STREAM : SOCK_DGRAM;
950
951
0
  reslv->start = Curl_now();
952
  /* fire up a new resolver thread! */
953
0
  if(init_resolve_thread(data, hostname, port, &hints)) {
954
0
    *waitp = 1; /* expect asynchronous response */
955
0
    return NULL;
956
0
  }
957
958
0
  failf(data, "getaddrinfo() thread failed to start");
959
0
  return NULL;
960
961
0
}
962
963
#endif /* !HAVE_GETADDRINFO */
964
965
CURLcode Curl_set_dns_servers(struct Curl_easy *data,
966
                              char *servers)
967
0
{
968
0
  (void)data;
969
0
  (void)servers;
970
0
  return CURLE_NOT_BUILT_IN;
971
972
0
}
973
974
CURLcode Curl_set_dns_interface(struct Curl_easy *data,
975
                                const char *interf)
976
0
{
977
0
  (void)data;
978
0
  (void)interf;
979
0
  return CURLE_NOT_BUILT_IN;
980
0
}
981
982
CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
983
                                const char *local_ip4)
984
0
{
985
0
  (void)data;
986
0
  (void)local_ip4;
987
0
  return CURLE_NOT_BUILT_IN;
988
0
}
989
990
CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
991
                                const char *local_ip6)
992
0
{
993
0
  (void)data;
994
0
  (void)local_ip6;
995
0
  return CURLE_NOT_BUILT_IN;
996
0
}
997
998
#endif /* CURLRES_THREADED */