Coverage Report

Created: 2025-10-10 06:31

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