Coverage Report

Created: 2026-01-09 07:14

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