Coverage Report

Created: 2025-08-26 07:08

/src/PROJ/curl/lib/asyn-thrdd.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  /* = WSA_NOT_ENOUGH_MEMORY on Windows */
53
#else
54
#  define RESOLVER_ENOMEM  SOCKENOMEM
55
#endif
56
57
#include "urldata.h"
58
#include "cfilters.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 "curl_threads.h"
66
#include "select.h"
67
#include "strdup.h"
68
69
#ifdef USE_ARES
70
#include <ares.h>
71
#ifdef USE_HTTPSRR
72
#define USE_HTTPSRR_ARES  /* the combo */
73
#endif
74
#endif
75
76
/* The last 3 #include files should be in this order */
77
#include "curl_printf.h"
78
#include "curl_memory.h"
79
#include "memdebug.h"
80
81
82
/*
83
 * Curl_async_global_init()
84
 * Called from curl_global_init() to initialize global resolver environment.
85
 * Does nothing here.
86
 */
87
int Curl_async_global_init(void)
88
0
{
89
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
90
  if(ares_library_init(ARES_LIB_INIT_ALL)) {
91
    return CURLE_FAILED_INIT;
92
  }
93
#endif
94
0
  return CURLE_OK;
95
0
}
96
97
/*
98
 * Curl_async_global_cleanup()
99
 * Called from curl_global_cleanup() to destroy global resolver environment.
100
 * Does nothing here.
101
 */
102
void Curl_async_global_cleanup(void)
103
0
{
104
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
105
  ares_library_cleanup();
106
#endif
107
0
}
108
109
static void async_thrdd_destroy(struct Curl_easy *);
110
static void async_thrdd_shutdown(struct Curl_easy *);
111
112
CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
113
0
{
114
0
  (void)data;
115
0
  *impl = NULL;
116
0
  return CURLE_OK;
117
0
}
118
119
/* Give up reference to add_ctx */
120
static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx,
121
                            struct Curl_easy *data)
122
0
{
123
0
  struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx;
124
0
  bool destroy;
125
126
0
  (void)data;
127
0
  if(!addr_ctx)
128
0
    return;
129
130
0
  Curl_mutex_acquire(&addr_ctx->mutx);
131
0
  if(!data)  /* called by resolving thread */
132
0
    addr_ctx->thrd_done = TRUE;
133
134
0
  DEBUGASSERT(addr_ctx->ref_count);
135
0
  --addr_ctx->ref_count;
136
0
  destroy = !addr_ctx->ref_count;
137
0
  Curl_mutex_release(&addr_ctx->mutx);
138
139
0
  if(destroy) {
140
0
    Curl_mutex_destroy(&addr_ctx->mutx);
141
0
    free(addr_ctx->hostname);
142
0
    if(addr_ctx->res)
143
0
      Curl_freeaddrinfo(addr_ctx->res);
144
0
#ifndef CURL_DISABLE_SOCKETPAIR
145
#ifndef USE_EVENTFD
146
    wakeup_close(addr_ctx->sock_pair[1]);
147
#endif
148
0
    wakeup_close(addr_ctx->sock_pair[0]);
149
0
#endif
150
0
    free(addr_ctx);
151
0
  }
152
0
  *paddr_ctx = NULL;
153
0
}
154
155
/* Initialize context for threaded resolver */
156
static struct async_thrdd_addr_ctx *
157
addr_ctx_create(struct Curl_easy *data,
158
                const char *hostname, int port,
159
                const struct addrinfo *hints)
160
0
{
161
0
  struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx));
162
0
  if(!addr_ctx)
163
0
    return NULL;
164
165
0
  addr_ctx->thread_hnd = curl_thread_t_null;
166
0
  addr_ctx->port = port;
167
0
  addr_ctx->ref_count = 1;
168
169
0
#ifdef HAVE_GETADDRINFO
170
0
  DEBUGASSERT(hints);
171
0
  addr_ctx->hints = *hints;
172
#else
173
  (void)hints;
174
#endif
175
176
0
  Curl_mutex_init(&addr_ctx->mutx);
177
178
0
#ifndef CURL_DISABLE_SOCKETPAIR
179
  /* create socket pair or pipe */
180
0
  if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) {
181
0
    addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
182
0
    addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
183
0
    goto err_exit;
184
0
  }
185
0
#endif
186
0
  addr_ctx->sock_error = 0;
187
188
  /* Copying hostname string because original can be destroyed by parent
189
   * thread during gethostbyname execution.
190
   */
191
0
  addr_ctx->hostname = strdup(hostname);
192
0
  if(!addr_ctx->hostname)
193
0
    goto err_exit;
194
195
0
  return addr_ctx;
196
197
0
err_exit:
198
0
  addr_ctx_unlink(&addr_ctx, data);
199
0
  return NULL;
200
0
}
201
202
static void async_thrd_cleanup(void *arg)
203
0
{
204
0
  struct async_thrdd_addr_ctx *addr_ctx = arg;
205
206
0
  Curl_thread_disable_cancel();
207
0
  addr_ctx_unlink(&addr_ctx, NULL);
208
0
}
209
210
#ifdef HAVE_GETADDRINFO
211
212
/*
213
 * getaddrinfo_thread() resolves a name and then exits.
214
 *
215
 * For builds without ARES, but with USE_IPV6, create a resolver thread
216
 * and wait on it.
217
 */
218
static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
219
0
{
220
0
  struct async_thrdd_addr_ctx *addr_ctx = arg;
221
0
  bool do_abort;
222
223
/* clang complains about empty statements and the pthread_cleanup* macros
224
 * are pretty ill defined. */
225
0
#if defined(__clang__)
226
0
#pragma clang diagnostic push
227
0
#pragma clang diagnostic ignored "-Wextra-semi-stmt"
228
0
#endif
229
230
0
  Curl_thread_push_cleanup(async_thrd_cleanup, addr_ctx);
231
232
0
  Curl_mutex_acquire(&addr_ctx->mutx);
233
0
  do_abort = addr_ctx->do_abort;
234
0
  Curl_mutex_release(&addr_ctx->mutx);
235
236
0
  if(!do_abort) {
237
0
    char service[12];
238
0
    int rc;
239
240
#ifdef DEBUGBUILD
241
    Curl_resolve_test_delay();
242
#endif
243
0
    msnprintf(service, sizeof(service), "%d", addr_ctx->port);
244
245
0
    rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
246
0
                             &addr_ctx->hints, &addr_ctx->res);
247
248
0
    if(rc) {
249
0
      addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
250
0
      if(addr_ctx->sock_error == 0)
251
0
        addr_ctx->sock_error = RESOLVER_ENOMEM;
252
0
    }
253
0
    else {
254
0
      Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
255
0
    }
256
257
0
    Curl_mutex_acquire(&addr_ctx->mutx);
258
0
    do_abort = addr_ctx->do_abort;
259
0
    Curl_mutex_release(&addr_ctx->mutx);
260
0
#ifndef CURL_DISABLE_SOCKETPAIR
261
0
    if(!do_abort) {
262
0
#ifdef USE_EVENTFD
263
0
      const uint64_t buf[1] = { 1 };
264
#else
265
      const char buf[1] = { 1 };
266
#endif
267
      /* Thread is done, notify transfer */
268
0
      if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
269
        /* update sock_error to errno */
270
0
        addr_ctx->sock_error = SOCKERRNO;
271
0
      }
272
0
    }
273
0
#endif
274
275
0
  }
276
277
0
  Curl_thread_pop_cleanup();
278
0
#if defined(__clang__)
279
0
#pragma clang diagnostic pop
280
0
#endif
281
282
0
  addr_ctx_unlink(&addr_ctx, NULL);
283
0
  return 0;
284
0
}
285
286
#else /* HAVE_GETADDRINFO */
287
288
/*
289
 * gethostbyname_thread() resolves a name and then exits.
290
 */
291
static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
292
{
293
  struct async_thrdd_addr_ctx *addr_ctx = arg;
294
  bool do_abort;
295
296
/* clang complains about empty statements and the pthread_cleanup* macros
297
 * are pretty ill defined. */
298
#if defined(__clang__)
299
#pragma clang diagnostic push
300
#pragma clang diagnostic ignored "-Wextra-semi-stmt"
301
#endif
302
303
  Curl_thread_push_cleanup(async_thrd_cleanup, addr_ctx);
304
305
  Curl_mutex_acquire(&addr_ctx->mutx);
306
  do_abort = addr_ctx->do_abort;
307
  Curl_mutex_release(&addr_ctx->mutx);
308
309
  if(!do_abort) {
310
#ifdef DEBUGBUILD
311
    Curl_resolve_test_delay();
312
#endif
313
314
    addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
315
    if(!addr_ctx->res) {
316
      addr_ctx->sock_error = SOCKERRNO;
317
      if(addr_ctx->sock_error == 0)
318
        addr_ctx->sock_error = RESOLVER_ENOMEM;
319
    }
320
321
    Curl_mutex_acquire(&addr_ctx->mutx);
322
    do_abort = addr_ctx->do_abort;
323
    Curl_mutex_release(&addr_ctx->mutx);
324
#ifndef CURL_DISABLE_SOCKETPAIR
325
    if(!do_abort) {
326
#ifdef USE_EVENTFD
327
      const uint64_t buf[1] = { 1 };
328
#else
329
      const char buf[1] = { 1 };
330
#endif
331
      /* Thread is done, notify transfer */
332
      if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
333
        /* update sock_error to errno */
334
        addr_ctx->sock_error = SOCKERRNO;
335
      }
336
    }
337
#endif
338
  }
339
340
  Curl_thread_pop_cleanup();
341
#if defined(__clang__)
342
#pragma clang diagnostic pop
343
#endif
344
345
  async_thrd_cleanup(addr_ctx);
346
  return 0;
347
}
348
349
#endif /* HAVE_GETADDRINFO */
350
351
/*
352
 * async_thrdd_destroy() cleans up async resolver data and thread handle.
353
 */
354
static void async_thrdd_destroy(struct Curl_easy *data)
355
0
{
356
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
357
0
  struct async_thrdd_addr_ctx *addr = thrdd->addr;
358
359
#ifdef USE_HTTPSRR_ARES
360
  if(thrdd->rr.channel) {
361
    ares_destroy(thrdd->rr.channel);
362
    thrdd->rr.channel = NULL;
363
  }
364
  Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
365
#endif
366
367
0
  if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) {
368
0
    bool done;
369
370
0
    Curl_mutex_acquire(&addr->mutx);
371
0
#ifndef CURL_DISABLE_SOCKETPAIR
372
0
    if(!addr->do_abort)
373
0
      Curl_multi_will_close(data, addr->sock_pair[0]);
374
0
#endif
375
0
    addr->do_abort = TRUE;
376
0
    done = addr->thrd_done;
377
0
    Curl_mutex_release(&addr->mutx);
378
379
0
    if(done) {
380
0
      Curl_thread_join(&addr->thread_hnd);
381
0
      CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined");
382
0
    }
383
0
    else {
384
      /* thread is still running. Detach the thread while mutexed, it will
385
       * trigger the cleanup when it releases its reference. */
386
0
      Curl_thread_destroy(&addr->thread_hnd);
387
0
      CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached");
388
0
    }
389
0
  }
390
0
  addr_ctx_unlink(&thrdd->addr, data);
391
0
}
392
393
#ifdef USE_HTTPSRR_ARES
394
395
static void async_thrdd_rr_done(void *user_data, ares_status_t status,
396
                                size_t timeouts,
397
                                const ares_dns_record_t *dnsrec)
398
{
399
  struct Curl_easy *data = user_data;
400
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
401
402
  (void)timeouts;
403
  thrdd->rr.done = TRUE;
404
  if((ARES_SUCCESS != status) || !dnsrec)
405
    return;
406
  thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
407
}
408
409
static CURLcode async_rr_start(struct Curl_easy *data)
410
{
411
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
412
  int status;
413
414
  DEBUGASSERT(!thrdd->rr.channel);
415
  status = ares_init_options(&thrdd->rr.channel, NULL, 0);
416
  if(status != ARES_SUCCESS) {
417
    thrdd->rr.channel = NULL;
418
    return CURLE_FAILED_INIT;
419
  }
420
#ifdef CURLDEBUG
421
  if(getenv("CURL_DNS_SERVER")) {
422
    const char *servers = getenv("CURL_DNS_SERVER");
423
    status = ares_set_servers_ports_csv(thrdd->rr.channel, servers);
424
    if(status)
425
      return CURLE_FAILED_INIT;
426
  }
427
#endif
428
429
  memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
430
  thrdd->rr.hinfo.port = -1;
431
  ares_query_dnsrec(thrdd->rr.channel,
432
                    data->conn->host.name, ARES_CLASS_IN,
433
                    ARES_REC_TYPE_HTTPS,
434
                    async_thrdd_rr_done, data, NULL);
435
  CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name);
436
  return CURLE_OK;
437
}
438
#endif
439
440
/*
441
 * async_thrdd_init() starts a new thread that performs the actual
442
 * resolve. This function returns before the resolve is done.
443
 *
444
 * Returns FALSE in case of failure, otherwise TRUE.
445
 */
446
static bool async_thrdd_init(struct Curl_easy *data,
447
                             const char *hostname, int port, int ip_version,
448
                             const struct addrinfo *hints)
449
0
{
450
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
451
0
  struct async_thrdd_addr_ctx *addr_ctx;
452
453
  /* !checksrc! disable ERRNOVAR 1 */
454
0
  int err = ENOMEM;
455
456
0
  if(thrdd->addr
457
#ifdef USE_HTTPSRR_ARES
458
     || thrdd->rr.channel
459
#endif
460
0
     ) {
461
0
    CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
462
0
    async_thrdd_destroy(data);
463
0
    DEBUGASSERT(!thrdd->addr);
464
#ifdef USE_HTTPSRR_ARES
465
    DEBUGASSERT(!thrdd->rr.channel);
466
#endif
467
0
  }
468
469
0
  data->state.async.dns = NULL;
470
0
  data->state.async.done = FALSE;
471
0
  data->state.async.port = port;
472
0
  data->state.async.ip_version = ip_version;
473
0
  free(data->state.async.hostname);
474
0
  data->state.async.hostname = strdup(hostname);
475
0
  if(!data->state.async.hostname)
476
0
    goto err_exit;
477
478
0
  addr_ctx = addr_ctx_create(data, hostname, port, hints);
479
0
  if(!addr_ctx)
480
0
    goto err_exit;
481
0
  thrdd->addr = addr_ctx;
482
483
  /* passing addr_ctx to the thread adds a reference */
484
0
  addr_ctx->ref_count = 2;
485
0
  addr_ctx->start = curlx_now();
486
487
0
#ifdef HAVE_GETADDRINFO
488
0
  addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
489
#else
490
  addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
491
#endif
492
493
0
  if(addr_ctx->thread_hnd == curl_thread_t_null) {
494
    /* The thread never started */
495
0
    addr_ctx->ref_count = 1;
496
0
    addr_ctx->thrd_done = TRUE;
497
0
    err = errno;
498
0
    goto err_exit;
499
0
  }
500
501
#ifdef USE_HTTPSRR_ARES
502
  if(async_rr_start(data))
503
    infof(data, "Failed HTTPS RR operation");
504
#endif
505
0
  CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
506
0
  return TRUE;
507
508
0
err_exit:
509
0
  CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
510
0
  async_thrdd_destroy(data);
511
0
  CURL_SETERRNO(err);
512
0
  return FALSE;
513
0
}
514
515
static void async_thrdd_shutdown(struct Curl_easy *data)
516
0
{
517
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
518
0
  struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr;
519
0
  bool done;
520
521
0
  if(!addr_ctx)
522
0
    return;
523
0
  if(addr_ctx->thread_hnd == curl_thread_t_null)
524
0
    return;
525
526
0
  Curl_mutex_acquire(&addr_ctx->mutx);
527
0
#ifndef CURL_DISABLE_SOCKETPAIR
528
0
    if(!addr_ctx->do_abort)
529
0
      Curl_multi_will_close(data, addr_ctx->sock_pair[0]);
530
0
#endif
531
0
  addr_ctx->do_abort = TRUE;
532
0
  done = addr_ctx->thrd_done;
533
0
  Curl_mutex_release(&addr_ctx->mutx);
534
535
0
  DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null);
536
0
  if(!done && (addr_ctx->thread_hnd != curl_thread_t_null)) {
537
0
    CURL_TRC_DNS(data, "cancelling resolve thread");
538
0
    (void)Curl_thread_cancel(&addr_ctx->thread_hnd);
539
0
  }
540
0
}
541
542
/*
543
 * 'entry' may be NULL and then no data is returned
544
 */
545
static CURLcode asyn_thrdd_await(struct Curl_easy *data,
546
                                 struct async_thrdd_addr_ctx *addr_ctx,
547
                                 struct Curl_dns_entry **entry)
548
0
{
549
0
  CURLcode result = CURLE_OK;
550
551
0
  if(addr_ctx->thread_hnd != curl_thread_t_null) {
552
    /* not interested in result? cancel, if still running... */
553
0
    if(!entry)
554
0
      async_thrdd_shutdown(data);
555
556
0
    CURL_TRC_DNS(data, "resolve, wait for thread to finish");
557
0
    if(!Curl_thread_join(&addr_ctx->thread_hnd)) {
558
0
      DEBUGASSERT(0);
559
0
    }
560
561
0
    if(entry)
562
0
      result = Curl_async_is_resolved(data, entry);
563
0
  }
564
565
0
  data->state.async.done = TRUE;
566
0
  if(entry)
567
0
    *entry = data->state.async.dns;
568
569
0
  return result;
570
0
}
571
572
/*
573
 * Until we gain a way to signal the resolver threads to stop early, we must
574
 * simply wait for them and ignore their results.
575
 */
576
void Curl_async_thrdd_shutdown(struct Curl_easy *data)
577
0
{
578
0
  async_thrdd_shutdown(data);
579
0
}
580
581
void Curl_async_thrdd_destroy(struct Curl_easy *data)
582
0
{
583
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
584
585
0
  if(thrdd->addr && !data->set.quick_exit) {
586
0
    (void)asyn_thrdd_await(data, thrdd->addr, NULL);
587
0
  }
588
0
  async_thrdd_destroy(data);
589
0
}
590
591
/*
592
 * Curl_async_await()
593
 *
594
 * Waits for a resolve to finish. This function should be avoided since using
595
 * this risk getting the multi interface to "hang".
596
 *
597
 * If 'entry' is non-NULL, make it point to the resolved dns entry
598
 *
599
 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
600
 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
601
 *
602
 * This is the version for resolves-in-a-thread.
603
 */
604
CURLcode Curl_async_await(struct Curl_easy *data,
605
                          struct Curl_dns_entry **entry)
606
0
{
607
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
608
0
  if(thrdd->addr)
609
0
    return asyn_thrdd_await(data, thrdd->addr, entry);
610
0
  return CURLE_FAILED_INIT;
611
0
}
612
613
/*
614
 * Curl_async_is_resolved() is called repeatedly to check if a previous
615
 * name resolve request has completed. It should also make sure to time-out if
616
 * the operation seems to take too long.
617
 */
618
CURLcode Curl_async_is_resolved(struct Curl_easy *data,
619
                                struct Curl_dns_entry **dns)
620
0
{
621
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
622
0
  bool done = FALSE;
623
624
0
  DEBUGASSERT(dns);
625
0
  *dns = NULL;
626
627
0
  if(data->state.async.done) {
628
0
    *dns = data->state.async.dns;
629
0
    CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
630
0
                 *dns ? "" : "not ");
631
0
    return CURLE_OK;
632
0
  }
633
634
#ifdef USE_HTTPSRR_ARES
635
  /* best effort, ignore errors */
636
  if(thrdd->rr.channel)
637
    (void)Curl_ares_perform(thrdd->rr.channel, 0);
638
#endif
639
640
0
  DEBUGASSERT(thrdd->addr);
641
0
  if(!thrdd->addr)
642
0
    return CURLE_FAILED_INIT;
643
644
0
  Curl_mutex_acquire(&thrdd->addr->mutx);
645
0
  done = thrdd->addr->thrd_done;
646
0
  Curl_mutex_release(&thrdd->addr->mutx);
647
648
0
  if(done) {
649
0
    CURLcode result = CURLE_OK;
650
651
0
    data->state.async.done = TRUE;
652
0
    Curl_resolv_unlink(data, &data->state.async.dns);
653
654
0
    if(thrdd->addr->res) {
655
0
      data->state.async.dns =
656
0
        Curl_dnscache_mk_entry(data, thrdd->addr->res,
657
0
                               data->state.async.hostname, 0,
658
0
                               data->state.async.port, FALSE);
659
0
      thrdd->addr->res = NULL;
660
0
      if(!data->state.async.dns)
661
0
        result = CURLE_OUT_OF_MEMORY;
662
663
#ifdef USE_HTTPSRR_ARES
664
      if(thrdd->rr.channel) {
665
        result = thrdd->rr.result;
666
        if(!result) {
667
          struct Curl_https_rrinfo *lhrr;
668
          lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
669
          if(!lhrr)
670
            result = CURLE_OUT_OF_MEMORY;
671
          else
672
            data->state.async.dns->hinfo = lhrr;
673
        }
674
      }
675
#endif
676
0
      if(!result && data->state.async.dns)
677
0
        result = Curl_dnscache_add(data, data->state.async.dns);
678
0
    }
679
680
0
    if(!result && !data->state.async.dns)
681
0
      result = Curl_resolver_error(data, NULL);
682
0
    if(result)
683
0
      Curl_resolv_unlink(data, &data->state.async.dns);
684
0
    *dns = data->state.async.dns;
685
0
    CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
686
0
                 result, *dns ? "" : "not ");
687
0
    async_thrdd_shutdown(data);
688
0
    return result;
689
0
  }
690
0
  else {
691
    /* poll for name lookup done with exponential backoff up to 250ms */
692
    /* should be fine even if this converts to 32-bit */
693
0
    timediff_t elapsed = curlx_timediff(curlx_now(),
694
0
                                       data->progress.t_startsingle);
695
0
    if(elapsed < 0)
696
0
      elapsed = 0;
697
698
0
    if(thrdd->addr->poll_interval == 0)
699
      /* Start at 1ms poll interval */
700
0
      thrdd->addr->poll_interval = 1;
701
0
    else if(elapsed >= thrdd->addr->interval_end)
702
      /* Back-off exponentially if last interval expired  */
703
0
      thrdd->addr->poll_interval *= 2;
704
705
0
    if(thrdd->addr->poll_interval > 250)
706
0
      thrdd->addr->poll_interval = 250;
707
708
0
    thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
709
0
    Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
710
0
    return CURLE_OK;
711
0
  }
712
0
}
713
714
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
715
0
{
716
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
717
0
  CURLcode result = CURLE_OK;
718
0
  bool thrd_done;
719
720
#if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR)
721
  (void)ps;
722
#endif
723
724
#ifdef USE_HTTPSRR_ARES
725
  if(thrdd->rr.channel) {
726
    result = Curl_ares_pollset(data, thrdd->rr.channel, ps);
727
    if(result)
728
      return result;
729
  }
730
#endif
731
0
  if(!thrdd->addr)
732
0
    return result;
733
734
0
  Curl_mutex_acquire(&thrdd->addr->mutx);
735
0
  thrd_done = thrdd->addr->thrd_done;
736
0
  Curl_mutex_release(&thrdd->addr->mutx);
737
738
0
  if(!thrd_done) {
739
0
#ifndef CURL_DISABLE_SOCKETPAIR
740
  /* return read fd to client for polling the DNS resolution status */
741
0
    result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]);
742
#else
743
    timediff_t milli;
744
    timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start);
745
    if(ms < 3)
746
      milli = 0;
747
    else if(ms <= 50)
748
      milli = ms/3;
749
    else if(ms <= 250)
750
      milli = 50;
751
    else
752
      milli = 200;
753
    Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
754
#endif
755
0
  }
756
0
  return result;
757
0
}
758
759
#ifndef HAVE_GETADDRINFO
760
/*
761
 * Curl_async_getaddrinfo() - for platforms without getaddrinfo
762
 */
763
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
764
                                             const char *hostname,
765
                                             int port,
766
                                             int ip_version,
767
                                             int *waitp)
768
{
769
  (void)ip_version;
770
  *waitp = 0; /* default to synchronous response */
771
772
  /* fire up a new resolver thread! */
773
  if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
774
    *waitp = 1; /* expect asynchronous response */
775
    return NULL;
776
  }
777
778
  failf(data, "getaddrinfo() thread failed");
779
780
  return NULL;
781
}
782
783
#else /* !HAVE_GETADDRINFO */
784
785
/*
786
 * Curl_async_getaddrinfo() - for getaddrinfo
787
 */
788
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
789
                                             const char *hostname,
790
                                             int port,
791
                                             int ip_version,
792
                                             int *waitp)
793
0
{
794
0
  struct addrinfo hints;
795
0
  int pf = PF_INET;
796
0
  *waitp = 0; /* default to synchronous response */
797
798
0
  CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
799
0
#ifdef CURLRES_IPV6
800
0
  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
801
    /* The stack seems to be IPv6-enabled */
802
0
    if(ip_version == CURL_IPRESOLVE_V6)
803
0
      pf = PF_INET6;
804
0
    else
805
0
      pf = PF_UNSPEC;
806
0
  }
807
#else
808
  (void)ip_version;
809
#endif /* CURLRES_IPV6 */
810
811
0
  memset(&hints, 0, sizeof(hints));
812
0
  hints.ai_family = pf;
813
0
  hints.ai_socktype =
814
0
    (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
815
0
    SOCK_STREAM : SOCK_DGRAM;
816
817
  /* fire up a new resolver thread! */
818
0
  if(async_thrdd_init(data, hostname, port, ip_version, &hints)) {
819
0
    *waitp = 1; /* expect asynchronous response */
820
0
    return NULL;
821
0
  }
822
823
0
  failf(data, "getaddrinfo() thread failed to start");
824
0
  return NULL;
825
826
0
}
827
828
#endif /* !HAVE_GETADDRINFO */
829
830
#endif /* CURLRES_THREADED */