Coverage Report

Created: 2025-10-30 06:17

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