Coverage Report

Created: 2025-12-05 06:38

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