Coverage Report

Created: 2025-12-04 06:52

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
1
{
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
1
  return CURLE_OK;
89
1
}
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
6.13k
{
117
6.13k
  struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx;
118
6.13k
  bool destroy;
119
120
6.13k
  (void)data;
121
6.13k
  if(!addr_ctx)
122
6.13k
    return;
123
124
0
  Curl_mutex_acquire(&addr_ctx->mutx);
125
0
  if(!data)  /* called by resolving thread */
126
0
    addr_ctx->thrd_done = TRUE;
127
128
0
  DEBUGASSERT(addr_ctx->ref_count);
129
0
  --addr_ctx->ref_count;
130
0
  destroy = !addr_ctx->ref_count;
131
0
  Curl_mutex_release(&addr_ctx->mutx);
132
133
0
  if(destroy) {
134
0
    Curl_mutex_destroy(&addr_ctx->mutx);
135
0
    curlx_free(addr_ctx->hostname);
136
0
    if(addr_ctx->res)
137
0
      Curl_freeaddrinfo(addr_ctx->res);
138
0
#ifndef CURL_DISABLE_SOCKETPAIR
139
#ifndef USE_EVENTFD
140
    wakeup_close(addr_ctx->sock_pair[1]);
141
#endif
142
0
    wakeup_close(addr_ctx->sock_pair[0]);
143
0
#endif
144
0
    curlx_free(addr_ctx);
145
0
  }
146
0
  *paddr_ctx = NULL;
147
0
}
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
0
{
155
0
  struct async_thrdd_addr_ctx *addr_ctx = curlx_calloc(1, sizeof(*addr_ctx));
156
0
  if(!addr_ctx)
157
0
    return NULL;
158
159
0
  addr_ctx->thread_hnd = curl_thread_t_null;
160
0
  addr_ctx->port = port;
161
0
  addr_ctx->ref_count = 1;
162
163
0
#ifdef HAVE_GETADDRINFO
164
0
  DEBUGASSERT(hints);
165
0
  addr_ctx->hints = *hints;
166
#else
167
  (void)hints;
168
#endif
169
170
0
  Curl_mutex_init(&addr_ctx->mutx);
171
172
0
#ifndef CURL_DISABLE_SOCKETPAIR
173
  /* create socket pair or pipe */
174
0
  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
0
#endif
180
0
  addr_ctx->sock_error = 0;
181
182
  /* Copying hostname string because original can be destroyed by parent
183
   * thread during gethostbyname execution.
184
   */
185
0
  addr_ctx->hostname = curlx_strdup(hostname);
186
0
  if(!addr_ctx->hostname)
187
0
    goto err_exit;
188
189
0
  return addr_ctx;
190
191
0
err_exit:
192
0
  addr_ctx_unlink(&addr_ctx, data);
193
0
  return NULL;
194
0
}
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
0
{
206
0
  struct async_thrdd_addr_ctx *addr_ctx = arg;
207
0
  bool do_abort;
208
209
0
  Curl_mutex_acquire(&addr_ctx->mutx);
210
0
  do_abort = addr_ctx->do_abort;
211
0
  Curl_mutex_release(&addr_ctx->mutx);
212
213
0
  if(!do_abort) {
214
0
    char service[12];
215
0
    int rc;
216
217
0
    curl_msnprintf(service, sizeof(service), "%d", addr_ctx->port);
218
219
0
    rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
220
0
                             &addr_ctx->hints, &addr_ctx->res);
221
222
0
    if(rc) {
223
0
      addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
224
0
      if(addr_ctx->sock_error == 0)
225
0
        addr_ctx->sock_error = RESOLVER_ENOMEM;
226
0
    }
227
0
    else {
228
0
      Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
229
0
    }
230
231
0
    Curl_mutex_acquire(&addr_ctx->mutx);
232
0
    do_abort = addr_ctx->do_abort;
233
0
    Curl_mutex_release(&addr_ctx->mutx);
234
0
#ifndef CURL_DISABLE_SOCKETPAIR
235
0
    if(!do_abort) {
236
0
#ifdef USE_EVENTFD
237
0
      const uint64_t buf[1] = { 1 };
238
#else
239
      const char buf[1] = { 1 };
240
#endif
241
      /* Thread is done, notify transfer */
242
0
      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
0
    }
247
0
#endif
248
0
  }
249
250
0
  addr_ctx_unlink(&addr_ctx, NULL);
251
0
  return 0;
252
0
}
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
6.13k
{
306
6.13k
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
307
6.13k
  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
6.13k
  if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) {
318
0
    bool done;
319
320
0
    Curl_mutex_acquire(&addr->mutx);
321
0
#ifndef CURL_DISABLE_SOCKETPAIR
322
0
    if(!addr->do_abort)
323
0
      Curl_multi_will_close(data, addr->sock_pair[0]);
324
0
#endif
325
0
    addr->do_abort = TRUE;
326
0
    done = addr->thrd_done;
327
0
    Curl_mutex_release(&addr->mutx);
328
329
0
    if(done) {
330
0
      Curl_thread_join(&addr->thread_hnd);
331
0
      CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined");
332
0
    }
333
0
    else {
334
      /* thread is still running. Detach it. */
335
0
      Curl_thread_destroy(&addr->thread_hnd);
336
0
      CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached");
337
0
    }
338
0
  }
339
  /* release our reference to the shared context */
340
6.13k
  addr_ctx_unlink(&thrdd->addr, data);
341
6.13k
}
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
0
{
410
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
411
0
  struct async_thrdd_addr_ctx *addr_ctx;
412
413
  /* !checksrc! disable ERRNOVAR 1 */
414
0
  int err = ENOMEM;
415
416
0
  if(thrdd->addr
417
#ifdef USE_HTTPSRR_ARES
418
     || thrdd->rr.channel
419
#endif
420
0
     ) {
421
0
    CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
422
0
    async_thrdd_destroy(data);
423
0
    DEBUGASSERT(!thrdd->addr);
424
#ifdef USE_HTTPSRR_ARES
425
    DEBUGASSERT(!thrdd->rr.channel);
426
#endif
427
0
  }
428
429
0
  data->state.async.dns = NULL;
430
0
  data->state.async.done = FALSE;
431
0
  data->state.async.port = port;
432
0
  data->state.async.ip_version = ip_version;
433
0
  curlx_free(data->state.async.hostname);
434
0
  data->state.async.hostname = curlx_strdup(hostname);
435
0
  if(!data->state.async.hostname)
436
0
    goto err_exit;
437
438
0
  addr_ctx = addr_ctx_create(data, hostname, port, hints);
439
0
  if(!addr_ctx)
440
0
    goto err_exit;
441
0
  thrdd->addr = addr_ctx;
442
443
  /* passing addr_ctx to the thread adds a reference */
444
0
  addr_ctx->ref_count = 2;
445
0
  addr_ctx->start = curlx_now();
446
447
0
#ifdef HAVE_GETADDRINFO
448
0
  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
0
  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
0
  CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
466
0
  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
0
}
474
475
static void async_thrdd_shutdown(struct Curl_easy *data)
476
0
{
477
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
478
0
  struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr;
479
0
  bool done;
480
481
0
  if(!addr_ctx)
482
0
    return;
483
0
  if(addr_ctx->thread_hnd == curl_thread_t_null)
484
0
    return;
485
486
0
  Curl_mutex_acquire(&addr_ctx->mutx);
487
0
#ifndef CURL_DISABLE_SOCKETPAIR
488
0
  if(!addr_ctx->do_abort)
489
0
    Curl_multi_will_close(data, addr_ctx->sock_pair[0]);
490
0
#endif
491
0
  addr_ctx->do_abort = TRUE;
492
0
  done = addr_ctx->thrd_done;
493
0
  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
0
  if(done && (addr_ctx->thread_hnd != curl_thread_t_null)) {
499
0
    Curl_thread_join(&addr_ctx->thread_hnd);
500
0
    CURL_TRC_DNS(data, "async_thrdd_shutdown, thread joined");
501
0
  }
502
0
}
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
0
{
511
0
  CURLcode result = CURLE_OK;
512
513
0
  if(addr_ctx->thread_hnd != curl_thread_t_null) {
514
    /* not interested in result? cancel, if still running... */
515
0
    if(!entry)
516
0
      async_thrdd_shutdown(data);
517
518
0
    if(addr_ctx->thread_hnd != curl_thread_t_null) {
519
0
      CURL_TRC_DNS(data, "resolve, wait for thread to finish");
520
0
      if(!Curl_thread_join(&addr_ctx->thread_hnd)) {
521
0
        DEBUGASSERT(0);
522
0
      }
523
0
    }
524
525
0
    if(entry)
526
0
      result = Curl_async_is_resolved(data, entry);
527
0
  }
528
529
0
  data->state.async.done = TRUE;
530
0
  if(entry)
531
0
    *entry = data->state.async.dns;
532
533
0
  return result;
534
0
}
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
0
{
542
0
  async_thrdd_shutdown(data);
543
0
}
544
545
void Curl_async_thrdd_destroy(struct Curl_easy *data)
546
6.13k
{
547
6.13k
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
548
549
6.13k
  if(thrdd->addr && !data->set.quick_exit) {
550
0
    (void)asyn_thrdd_await(data, thrdd->addr, NULL);
551
0
  }
552
6.13k
  async_thrdd_destroy(data);
553
6.13k
}
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
0
{
571
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
572
0
  if(thrdd->addr)
573
0
    return asyn_thrdd_await(data, thrdd->addr, entry);
574
0
  return CURLE_FAILED_INIT;
575
0
}
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
0
{
585
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
586
0
  bool done = FALSE;
587
588
0
  DEBUGASSERT(dns);
589
0
  *dns = NULL;
590
591
0
  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
0
  DEBUGASSERT(thrdd->addr);
605
0
  if(!thrdd->addr)
606
0
    return CURLE_FAILED_INIT;
607
608
0
  Curl_mutex_acquire(&thrdd->addr->mutx);
609
0
  done = thrdd->addr->thrd_done;
610
0
  Curl_mutex_release(&thrdd->addr->mutx);
611
612
0
  if(done) {
613
0
    CURLcode result = CURLE_OK;
614
615
0
    data->state.async.done = TRUE;
616
0
    Curl_resolv_unlink(data, &data->state.async.dns);
617
0
    Curl_expire_done(data, EXPIRE_ASYNC_NAME);
618
619
0
    if(thrdd->addr->res) {
620
0
      data->state.async.dns =
621
0
        Curl_dnscache_mk_entry(data, thrdd->addr->res,
622
0
                               data->state.async.hostname, 0,
623
0
                               data->state.async.port, FALSE);
624
0
      thrdd->addr->res = NULL;
625
0
      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
0
      if(!result && data->state.async.dns)
642
0
        result = Curl_dnscache_add(data, data->state.async.dns);
643
0
    }
644
645
0
    if(!result && !data->state.async.dns)
646
0
      result = Curl_resolver_error(data, NULL);
647
0
    if(result)
648
0
      Curl_resolv_unlink(data, &data->state.async.dns);
649
0
    *dns = data->state.async.dns;
650
0
    CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
651
0
                 result, *dns ? "" : "not ");
652
0
    async_thrdd_shutdown(data);
653
0
    return result;
654
0
  }
655
0
  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
0
    timediff_t elapsed = curlx_timediff_ms(curlx_now(),
659
0
                                           data->progress.t_startsingle);
660
0
    if(elapsed < 0)
661
0
      elapsed = 0;
662
663
0
    if(thrdd->addr->poll_interval == 0)
664
      /* Start at 1ms poll interval */
665
0
      thrdd->addr->poll_interval = 1;
666
0
    else if(elapsed >= thrdd->addr->interval_end)
667
      /* Back-off exponentially if last interval expired */
668
0
      thrdd->addr->poll_interval *= 2;
669
670
0
    if(thrdd->addr->poll_interval > 250)
671
0
      thrdd->addr->poll_interval = 250;
672
673
0
    thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
674
0
    Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
675
0
    return CURLE_OK;
676
0
  }
677
0
}
678
679
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
680
0
{
681
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
682
0
  CURLcode result = CURLE_OK;
683
0
  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
0
  if(!thrdd->addr)
697
0
    return result;
698
699
0
  Curl_mutex_acquire(&thrdd->addr->mutx);
700
0
  thrd_done = thrdd->addr->thrd_done;
701
0
  Curl_mutex_release(&thrdd->addr->mutx);
702
703
0
  if(!thrd_done) {
704
0
#ifndef CURL_DISABLE_SOCKETPAIR
705
    /* return read fd to client for polling the DNS resolution status */
706
0
    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
0
  }
721
0
  return result;
722
0
}
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
0
{
756
0
  struct addrinfo hints;
757
0
  int pf = PF_INET;
758
759
0
  CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
760
0
#ifdef CURLRES_IPV6
761
0
  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
762
    /* The stack seems to be IPv6-enabled */
763
0
    if(ip_version == CURL_IPRESOLVE_V6)
764
0
      pf = PF_INET6;
765
0
    else
766
0
      pf = PF_UNSPEC;
767
0
  }
768
#else
769
  (void)ip_version;
770
#endif /* CURLRES_IPV6 */
771
772
0
  memset(&hints, 0, sizeof(hints));
773
0
  hints.ai_family = pf;
774
0
  hints.ai_socktype =
775
0
    (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
776
0
    SOCK_STREAM : SOCK_DGRAM;
777
778
  /* fire up a new resolver thread! */
779
0
  if(async_thrdd_init(data, hostname, port, ip_version, &hints))
780
0
    return CURLE_OK;
781
782
0
  failf(data, "getaddrinfo() thread failed to start");
783
0
  return CURLE_FAILED_INIT;
784
0
}
785
786
#endif /* !HAVE_GETADDRINFO */
787
788
#endif /* CURLRES_THREADED */