Coverage Report

Created: 2026-01-25 06:18

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