Coverage Report

Created: 2024-02-25 06:14

/src/PROJ/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
0
{
84
0
  return CURLE_OK;
85
0
}
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
0
{
104
0
  (void)easy;
105
0
  *resolver = calloc(1, sizeof(struct resdata));
106
0
  if(!*resolver)
107
0
    return CURLE_OUT_OF_MEMORY;
108
0
  return CURLE_OK;
109
0
}
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
0
{
119
0
  free(resolver);
120
0
}
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
0
{
140
0
  destroy_async_data(&data->state.async);
141
0
}
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 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
 */
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
0
{
538
0
  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
      else
560
#endif
561
0
      Curl_thread_destroy(td->thread_hnd);
562
0
    }
563
0
    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
0
      if(td->thread_hnd != curl_thread_t_null)
572
0
        Curl_thread_join(&td->thread_hnd);
573
574
0
      destroy_thread_sync_data(&td->tsd);
575
576
0
      free(async->tdata);
577
0
    }
578
0
#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
0
    Curl_multi_closed(data, sock_rd);
584
0
    wakeup_close(sock_rd);
585
0
#endif
586
0
  }
587
0
  async->tdata = NULL;
588
589
0
  free(async->hostname);
590
0
  async->hostname = NULL;
591
0
}
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
 */
599
static bool init_resolve_thread(struct Curl_easy *data,
600
                                const char *hostname, int port,
601
                                const struct addrinfo *hints)
602
0
{
603
0
  struct thread_data *td = calloc(1, sizeof(struct thread_data));
604
0
  int err = ENOMEM;
605
0
  struct Curl_async *asp = &data->state.async;
606
607
0
  data->state.async.tdata = td;
608
0
  if(!td)
609
0
    goto errno_exit;
610
611
0
  asp->port = port;
612
0
  asp->done = FALSE;
613
0
  asp->status = 0;
614
0
  asp->dns = NULL;
615
0
  td->thread_hnd = curl_thread_t_null;
616
#ifdef _WIN32
617
  td->complete_ev = NULL;
618
#endif
619
620
0
  if(!init_thread_sync_data(td, hostname, port, hints)) {
621
0
    asp->tdata = NULL;
622
0
    free(td);
623
0
    goto errno_exit;
624
0
  }
625
626
0
  free(asp->hostname);
627
0
  asp->hostname = strdup(hostname);
628
0
  if(!asp->hostname)
629
0
    goto err_exit;
630
631
  /* The thread will set this to 1 when complete. */
632
0
  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
0
#ifdef HAVE_GETADDRINFO
670
0
  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
0
  if(!td->thread_hnd) {
676
    /* The thread never started, so mark it as done here for proper cleanup. */
677
0
    td->tsd.done = 1;
678
0
    err = errno;
679
0
    goto err_exit;
680
0
  }
681
682
0
  return TRUE;
683
684
0
err_exit:
685
0
  destroy_async_data(asp);
686
687
0
errno_exit:
688
0
  errno = err;
689
0
  return FALSE;
690
0
}
691
692
/*
693
 * 'entry' may be NULL and then no data is returned
694
 */
695
static CURLcode thread_wait_resolv(struct Curl_easy *data,
696
                                   struct Curl_dns_entry **entry,
697
                                   bool report)
698
0
{
699
0
  struct thread_data *td;
700
0
  CURLcode result = CURLE_OK;
701
702
0
  DEBUGASSERT(data);
703
0
  td = data->state.async.tdata;
704
0
  DEBUGASSERT(td);
705
#ifdef _WIN32
706
  DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
707
#else
708
0
  DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
709
0
#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
0
  if(Curl_thread_join(&td->thread_hnd)) {
722
0
    if(entry)
723
0
      result = getaddrinfo_complete(data);
724
0
  }
725
0
  else
726
0
    DEBUGASSERT(0);
727
728
0
  data->state.async.done = TRUE;
729
730
0
  if(entry)
731
0
    *entry = data->state.async.dns;
732
733
0
  if(!data->state.async.dns && report)
734
    /* a name was not resolved, report error */
735
0
    result = Curl_resolver_error(data);
736
737
0
  destroy_async_data(&data->state.async);
738
739
0
  if(!data->state.async.dns && report)
740
0
    connclose(data->conn, "asynch resolve failed");
741
742
0
  return result;
743
0
}
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
 */
750
void Curl_resolver_kill(struct Curl_easy *data)
751
0
{
752
0
  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
0
  if(td && td->thread_hnd != curl_thread_t_null
758
0
     && (data->set.quick_exit != 1L))
759
0
    (void)thread_wait_resolv(data, NULL, FALSE);
760
0
  else
761
0
    Curl_resolver_cancel(data);
762
0
}
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
 */
777
CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
778
                                   struct Curl_dns_entry **entry)
779
0
{
780
0
  return thread_wait_resolv(data, entry, TRUE);
781
0
}
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
 */
788
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
789
                                   struct Curl_dns_entry **entry)
790
0
{
791
0
  struct thread_data *td = data->state.async.tdata;
792
0
  int done = 0;
793
794
0
  DEBUGASSERT(entry);
795
0
  *entry = NULL;
796
797
0
  if(!td) {
798
0
    DEBUGASSERT(td);
799
0
    return CURLE_COULDNT_RESOLVE_HOST;
800
0
  }
801
802
0
  Curl_mutex_acquire(td->tsd.mtx);
803
0
  done = td->tsd.done;
804
0
  Curl_mutex_release(td->tsd.mtx);
805
806
0
  if(done) {
807
0
    getaddrinfo_complete(data);
808
809
0
    if(!data->state.async.dns) {
810
0
      CURLcode result = Curl_resolver_error(data);
811
0
      destroy_async_data(&data->state.async);
812
0
      return result;
813
0
    }
814
0
    destroy_async_data(&data->state.async);
815
0
    *entry = data->state.async.dns;
816
0
  }
817
0
  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
0
    timediff_t elapsed = Curl_timediff(Curl_now(),
821
0
                                       data->progress.t_startsingle);
822
0
    if(elapsed < 0)
823
0
      elapsed = 0;
824
825
0
    if(td->poll_interval == 0)
826
      /* Start at 1ms poll interval */
827
0
      td->poll_interval = 1;
828
0
    else if(elapsed >= td->interval_end)
829
      /* Back-off exponentially if last interval expired  */
830
0
      td->poll_interval *= 2;
831
832
0
    if(td->poll_interval > 250)
833
0
      td->poll_interval = 250;
834
835
0
    td->interval_end = elapsed + td->poll_interval;
836
0
    Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
837
0
  }
838
839
0
  return CURLE_OK;
840
0
}
841
842
int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
843
0
{
844
0
  int ret_val = 0;
845
0
  timediff_t milli;
846
0
  timediff_t ms;
847
0
  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
848
0
#ifndef CURL_DISABLE_SOCKETPAIR
849
0
  struct thread_data *td = data->state.async.tdata;
850
#else
851
  (void)socks;
852
#endif
853
854
0
#ifndef CURL_DISABLE_SOCKETPAIR
855
0
  if(td) {
856
    /* return read fd to client for polling the DNS resolution status */
857
0
    socks[0] = td->tsd.sock_pair[0];
858
0
    td->tsd.data = data;
859
0
    ret_val = GETSOCK_READSOCK(0);
860
0
  }
861
0
  else {
862
0
#endif
863
0
    ms = Curl_timediff(Curl_now(), reslv->start);
864
0
    if(ms < 3)
865
0
      milli = 0;
866
0
    else if(ms <= 50)
867
0
      milli = ms/3;
868
0
    else if(ms <= 250)
869
0
      milli = 50;
870
0
    else
871
0
      milli = 200;
872
0
    Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
873
0
#ifndef CURL_DISABLE_SOCKETPAIR
874
0
  }
875
0
#endif
876
877
878
0
  return ret_val;
879
0
}
880
881
#ifndef HAVE_GETADDRINFO
882
/*
883
 * Curl_getaddrinfo() - for platforms without getaddrinfo
884
 */
885
struct 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
 */
912
struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
913
                                                const char *hostname,
914
                                                int port,
915
                                                int *waitp)
916
0
{
917
0
  struct addrinfo hints;
918
0
  int pf = PF_INET;
919
0
  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
920
921
0
  *waitp = 0; /* default to synchronous response */
922
923
0
#ifdef CURLRES_IPV6
924
0
  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
925
    /* The stack seems to be IPv6-enabled */
926
0
    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
927
0
      pf = PF_INET6;
928
0
    else
929
0
      pf = PF_UNSPEC;
930
0
  }
931
0
#endif /* CURLRES_IPV6 */
932
933
0
  memset(&hints, 0, sizeof(hints));
934
0
  hints.ai_family = pf;
935
0
  hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
936
0
    SOCK_STREAM : SOCK_DGRAM;
937
938
0
  reslv->start = Curl_now();
939
  /* fire up a new resolver thread! */
940
0
  if(init_resolve_thread(data, hostname, port, &hints)) {
941
0
    *waitp = 1; /* expect asynchronous response */
942
0
    return NULL;
943
0
  }
944
945
0
  failf(data, "getaddrinfo() thread failed to start");
946
0
  return NULL;
947
948
0
}
949
950
#endif /* !HAVE_GETADDRINFO */
951
952
CURLcode Curl_set_dns_servers(struct Curl_easy *data,
953
                              char *servers)
954
0
{
955
0
  (void)data;
956
0
  (void)servers;
957
0
  return CURLE_NOT_BUILT_IN;
958
959
0
}
960
961
CURLcode Curl_set_dns_interface(struct Curl_easy *data,
962
                                const char *interf)
963
0
{
964
0
  (void)data;
965
0
  (void)interf;
966
0
  return CURLE_NOT_BUILT_IN;
967
0
}
968
969
CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
970
                                const char *local_ip4)
971
0
{
972
0
  (void)data;
973
0
  (void)local_ip4;
974
0
  return CURLE_NOT_BUILT_IN;
975
0
}
976
977
CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
978
                                const char *local_ip6)
979
0
{
980
0
  (void)data;
981
0
  (void)local_ip6;
982
0
  return CURLE_NOT_BUILT_IN;
983
0
}
984
985
#endif /* CURLRES_THREADED */