Coverage Report

Created: 2025-07-23 06:58

/src/PROJ/curl/lib/asyn-thrdd.c
Line
Count
Source (jump to first uncovered line)
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 "strdup.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 3 #include files should be in this order */
76
#include "curl_printf.h"
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
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
/* Destroy context of threaded resolver */
118
static void addr_ctx_destroy(struct async_thrdd_addr_ctx *addr_ctx)
119
0
{
120
0
  if(addr_ctx) {
121
0
    DEBUGASSERT(!addr_ctx->ref_count);
122
0
    Curl_mutex_destroy(&addr_ctx->mutx);
123
0
    free(addr_ctx->hostname);
124
0
    if(addr_ctx->res)
125
0
      Curl_freeaddrinfo(addr_ctx->res);
126
0
#ifndef CURL_DISABLE_SOCKETPAIR
127
  /*
128
   * close one end of the socket pair (may be done in resolver thread);
129
   * the other end (for reading) is always closed in the parent thread.
130
   */
131
#ifndef USE_EVENTFD
132
  if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
133
    wakeup_close(addr_ctx->sock_pair[1]);
134
  }
135
#endif
136
0
#endif
137
0
    free(addr_ctx);
138
0
  }
139
0
}
140
141
/* Initialize context for threaded resolver */
142
static struct async_thrdd_addr_ctx *
143
addr_ctx_create(const char *hostname, int port,
144
                const struct addrinfo *hints)
145
0
{
146
0
  struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx));
147
0
  if(!addr_ctx)
148
0
    return NULL;
149
150
0
  addr_ctx->thread_hnd = curl_thread_t_null;
151
0
  addr_ctx->port = port;
152
0
#ifndef CURL_DISABLE_SOCKETPAIR
153
0
  addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
154
0
  addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
155
0
#endif
156
0
  addr_ctx->ref_count = 0;
157
158
0
#ifdef HAVE_GETADDRINFO
159
0
  DEBUGASSERT(hints);
160
0
  addr_ctx->hints = *hints;
161
#else
162
  (void) hints;
163
#endif
164
165
0
  Curl_mutex_init(&addr_ctx->mutx);
166
167
0
#ifndef CURL_DISABLE_SOCKETPAIR
168
  /* create socket pair or pipe */
169
0
  if(wakeup_create(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
0
#endif
175
0
  addr_ctx->sock_error = CURL_ASYNC_SUCCESS;
176
177
  /* Copying hostname string because original can be destroyed by parent
178
   * thread during gethostbyname execution.
179
   */
180
0
  addr_ctx->hostname = strdup(hostname);
181
0
  if(!addr_ctx->hostname)
182
0
    goto err_exit;
183
184
0
  addr_ctx->ref_count = 1;
185
0
  return addr_ctx;
186
187
0
err_exit:
188
0
#ifndef CURL_DISABLE_SOCKETPAIR
189
0
  if(addr_ctx->sock_pair[0] != CURL_SOCKET_BAD) {
190
0
    wakeup_close(addr_ctx->sock_pair[0]);
191
0
    addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
192
0
  }
193
0
#endif
194
0
  addr_ctx_destroy(addr_ctx);
195
0
  return NULL;
196
0
}
197
198
#ifdef HAVE_GETADDRINFO
199
200
/*
201
 * getaddrinfo_thread() resolves a name and then exits.
202
 *
203
 * For builds without ARES, but with USE_IPV6, create a resolver thread
204
 * and wait on it.
205
 */
206
static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
207
0
{
208
0
  struct async_thrdd_addr_ctx *addr_ctx = arg;
209
0
  char service[12];
210
0
  int rc;
211
0
  bool all_gone;
212
213
0
  msnprintf(service, sizeof(service), "%d", addr_ctx->port);
214
215
0
  rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
216
0
                           &addr_ctx->hints, &addr_ctx->res);
217
218
0
  if(rc) {
219
0
    addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
220
0
    if(addr_ctx->sock_error == 0)
221
0
      addr_ctx->sock_error = RESOLVER_ENOMEM;
222
0
  }
223
0
  else {
224
0
    Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
225
0
  }
226
227
0
  Curl_mutex_acquire(&addr_ctx->mutx);
228
0
  if(addr_ctx->ref_count > 1) {
229
    /* Someone still waiting on our results. */
230
0
#ifndef CURL_DISABLE_SOCKETPAIR
231
0
    if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
232
0
#ifdef USE_EVENTFD
233
0
      const uint64_t buf[1] = { 1 };
234
#else
235
      const char buf[1] = { 1 };
236
#endif
237
      /* DNS has been resolved, signal client task */
238
0
      if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
239
        /* update sock_error to errno */
240
0
        addr_ctx->sock_error = SOCKERRNO;
241
0
      }
242
0
    }
243
0
#endif
244
0
  }
245
  /* thread gives up its reference to the shared data now. */
246
0
  --addr_ctx->ref_count;
247
0
  all_gone = !addr_ctx->ref_count;
248
0
  Curl_mutex_release(&addr_ctx->mutx);
249
0
  if(all_gone)
250
0
    addr_ctx_destroy(addr_ctx);
251
252
0
  return 0;
253
0
}
254
255
#else /* HAVE_GETADDRINFO */
256
257
/*
258
 * gethostbyname_thread() resolves a name and then exits.
259
 */
260
static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
261
{
262
  struct async_thrdd_addr_ctx *addr_ctx = arg;
263
  bool all_gone;
264
265
  addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
266
267
  if(!addr_ctx->res) {
268
    addr_ctx->sock_error = SOCKERRNO;
269
    if(addr_ctx->sock_error == 0)
270
      addr_ctx->sock_error = RESOLVER_ENOMEM;
271
  }
272
273
  Curl_mutex_acquire(&addr_ctx->mutx);
274
  /* thread gives up its reference to the shared data now. */
275
  --addr_ctx->ref_count;
276
  all_gone = !addr_ctx->ref_count;;
277
  Curl_mutex_release(&addr_ctx->mutx);
278
  if(all_gone)
279
    addr_ctx_destroy(addr_ctx);
280
281
  return 0;
282
}
283
284
#endif /* HAVE_GETADDRINFO */
285
286
/*
287
 * async_thrdd_destroy() cleans up async resolver data and thread handle.
288
 */
289
static void async_thrdd_destroy(struct Curl_easy *data)
290
0
{
291
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
292
0
  struct async_thrdd_addr_ctx *addr = thrdd->addr;
293
#ifdef USE_HTTPSRR_ARES
294
  if(thrdd->rr.channel) {
295
    ares_destroy(thrdd->rr.channel);
296
    thrdd->rr.channel = NULL;
297
  }
298
  Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
299
#endif
300
301
0
  if(addr) {
302
0
#ifndef CURL_DISABLE_SOCKETPAIR
303
0
    curl_socket_t sock_rd = addr->sock_pair[0];
304
0
#endif
305
0
    bool done;
306
307
    /* Release our reference to the data shared with the thread. */
308
0
    Curl_mutex_acquire(&addr->mutx);
309
0
    --addr->ref_count;
310
0
    CURL_TRC_DNS(data, "resolve, destroy async data, shared ref=%d",
311
0
                 addr->ref_count);
312
0
    done = !addr->ref_count;
313
    /* we give up our reference to `addr`, so NULL our pointer.
314
     * coverity analyses this as being a potential unsynched write,
315
     * assuming two calls to this function could be invoked concurrently.
316
     * Which they never are, as the transfer's side runs single-threaded. */
317
0
    thrdd->addr = NULL;
318
0
    if(!done) {
319
      /* thread is still running. Detach the thread while mutexed, it will
320
       * trigger the cleanup when it releases its reference. */
321
0
      Curl_thread_destroy(&addr->thread_hnd);
322
0
    }
323
0
    Curl_mutex_release(&addr->mutx);
324
325
0
    if(done) {
326
      /* thread has released its reference, join it and
327
       * release the memory we shared with it. */
328
0
      if(addr->thread_hnd != curl_thread_t_null)
329
0
        Curl_thread_join(&addr->thread_hnd);
330
0
      addr_ctx_destroy(addr);
331
0
    }
332
0
#ifndef CURL_DISABLE_SOCKETPAIR
333
    /*
334
     * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
335
     * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
336
     */
337
0
    Curl_multi_will_close(data, sock_rd);
338
0
    wakeup_close(sock_rd);
339
0
#endif
340
0
  }
341
0
}
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)
360
{
361
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
362
  int status;
363
364
  DEBUGASSERT(!thrdd->rr.channel);
365
  status = ares_init_options(&thrdd->rr.channel, NULL, 0);
366
  if(status != ARES_SUCCESS) {
367
    thrdd->rr.channel = NULL;
368
    return CURLE_FAILED_INIT;
369
  }
370
371
  memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
372
  thrdd->rr.hinfo.port = -1;
373
  ares_query_dnsrec(thrdd->rr.channel,
374
                    data->conn->host.name, ARES_CLASS_IN,
375
                    ARES_REC_TYPE_HTTPS,
376
                    async_thrdd_rr_done, data, NULL);
377
  return CURLE_OK;
378
}
379
#endif
380
381
/*
382
 * async_thrdd_init() starts a new thread that performs the actual
383
 * resolve. This function returns before the resolve is done.
384
 *
385
 * Returns FALSE in case of failure, otherwise TRUE.
386
 */
387
static bool async_thrdd_init(struct Curl_easy *data,
388
                             const char *hostname, int port, int ip_version,
389
                             const struct addrinfo *hints)
390
0
{
391
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
392
0
  struct async_thrdd_addr_ctx *addr_ctx;
393
394
  /* !checksrc! disable ERRNOVAR 1 */
395
0
  int err = ENOMEM;
396
397
0
  if(thrdd->addr
398
#ifdef USE_HTTPSRR_ARES
399
     || thrdd->rr.channel
400
#endif
401
0
     ) {
402
0
    CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
403
0
    async_thrdd_destroy(data);
404
0
    DEBUGASSERT(!thrdd->addr);
405
#ifdef USE_HTTPSRR_ARES
406
    DEBUGASSERT(!thrdd->rr.channel);
407
#endif
408
0
  }
409
410
0
  data->state.async.dns = NULL;
411
0
  data->state.async.done = FALSE;
412
0
  data->state.async.port = port;
413
0
  data->state.async.ip_version = ip_version;
414
0
  free(data->state.async.hostname);
415
0
  data->state.async.hostname = strdup(hostname);
416
0
  if(!data->state.async.hostname)
417
0
    goto err_exit;
418
419
0
  addr_ctx = addr_ctx_create(hostname, port, hints);
420
0
  if(!addr_ctx)
421
0
    goto err_exit;
422
0
  thrdd->addr = addr_ctx;
423
424
0
  Curl_mutex_acquire(&addr_ctx->mutx);
425
0
  DEBUGASSERT(addr_ctx->ref_count == 1);
426
  /* passing addr_ctx to the thread adds a reference */
427
0
  addr_ctx->start = curlx_now();
428
0
  ++addr_ctx->ref_count;
429
0
#ifdef HAVE_GETADDRINFO
430
0
  addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
431
#else
432
  addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
433
#endif
434
0
  if(addr_ctx->thread_hnd == curl_thread_t_null) {
435
    /* The thread never started, remove its reference that never happened. */
436
0
    --addr_ctx->ref_count;
437
0
    err = errno;
438
0
    Curl_mutex_release(&addr_ctx->mutx);
439
0
    goto err_exit;
440
0
  }
441
0
  Curl_mutex_release(&addr_ctx->mutx);
442
443
#ifdef USE_HTTPSRR_ARES
444
  if(async_rr_start(data))
445
    infof(data, "Failed HTTPS RR operation");
446
#endif
447
0
  CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
448
0
  return TRUE;
449
450
0
err_exit:
451
0
  CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
452
0
  async_thrdd_destroy(data);
453
0
  CURL_SETERRNO(err);
454
0
  return FALSE;
455
0
}
456
457
/*
458
 * 'entry' may be NULL and then no data is returned
459
 */
460
static CURLcode asyn_thrdd_await(struct Curl_easy *data,
461
                                 struct async_thrdd_addr_ctx *addr_ctx,
462
                                 struct Curl_dns_entry **entry)
463
0
{
464
0
  CURLcode result = CURLE_OK;
465
466
0
  DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null);
467
468
0
  CURL_TRC_DNS(data, "resolve, wait for thread to finish");
469
  /* wait for the thread to resolve the name */
470
0
  if(Curl_thread_join(&addr_ctx->thread_hnd)) {
471
0
    if(entry)
472
0
      result = Curl_async_is_resolved(data, entry);
473
0
  }
474
0
  else
475
0
    DEBUGASSERT(0);
476
477
0
  data->state.async.done = TRUE;
478
0
  if(entry)
479
0
    *entry = data->state.async.dns;
480
481
0
  async_thrdd_destroy(data);
482
0
  return result;
483
0
}
484
485
486
/*
487
 * Until we gain a way to signal the resolver threads to stop early, we must
488
 * simply wait for them and ignore their results.
489
 */
490
void Curl_async_thrdd_shutdown(struct Curl_easy *data)
491
0
{
492
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
493
494
  /* If we are still resolving, we must wait for the threads to fully clean up,
495
     unfortunately. Otherwise, we can simply cancel to clean up any resolver
496
     data. */
497
0
  if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null) &&
498
0
     !data->set.quick_exit)
499
0
    (void)asyn_thrdd_await(data, thrdd->addr, NULL);
500
0
  else
501
0
    async_thrdd_destroy(data);
502
0
}
503
504
void Curl_async_thrdd_destroy(struct Curl_easy *data)
505
0
{
506
0
  Curl_async_thrdd_shutdown(data);
507
0
}
508
509
/*
510
 * Curl_async_await()
511
 *
512
 * Waits for a resolve to finish. This function should be avoided since using
513
 * this risk getting the multi interface to "hang".
514
 *
515
 * If 'entry' is non-NULL, make it point to the resolved dns entry
516
 *
517
 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
518
 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
519
 *
520
 * This is the version for resolves-in-a-thread.
521
 */
522
CURLcode Curl_async_await(struct Curl_easy *data,
523
                          struct Curl_dns_entry **entry)
524
0
{
525
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
526
0
  if(thrdd->addr)
527
0
    return asyn_thrdd_await(data, thrdd->addr, entry);
528
0
  return CURLE_FAILED_INIT;
529
0
}
530
531
/*
532
 * Curl_async_is_resolved() is called repeatedly to check if a previous
533
 * name resolve request has completed. It should also make sure to time-out if
534
 * the operation seems to take too long.
535
 */
536
CURLcode Curl_async_is_resolved(struct Curl_easy *data,
537
                                struct Curl_dns_entry **dns)
538
0
{
539
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
540
0
  bool done = FALSE;
541
542
0
  DEBUGASSERT(dns);
543
0
  *dns = NULL;
544
545
0
  if(data->state.async.done) {
546
0
    *dns = data->state.async.dns;
547
0
    CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
548
0
                 *dns ? "" : "not ");
549
0
    return CURLE_OK;
550
0
  }
551
552
#ifdef USE_HTTPSRR_ARES
553
  /* best effort, ignore errors */
554
  if(thrdd->rr.channel)
555
    (void)Curl_ares_perform(thrdd->rr.channel, 0);
556
#endif
557
558
0
  DEBUGASSERT(thrdd->addr);
559
0
  if(!thrdd->addr)
560
0
    return CURLE_FAILED_INIT;
561
562
0
  Curl_mutex_acquire(&thrdd->addr->mutx);
563
0
  done = (thrdd->addr->ref_count == 1);
564
0
  Curl_mutex_release(&thrdd->addr->mutx);
565
566
0
  if(done) {
567
0
    CURLcode result = CURLE_OK;
568
569
0
    data->state.async.done = TRUE;
570
0
    Curl_resolv_unlink(data, &data->state.async.dns);
571
572
0
    if(thrdd->addr->res) {
573
0
      data->state.async.dns =
574
0
        Curl_dnscache_mk_entry(data, thrdd->addr->res,
575
0
                               data->state.async.hostname, 0,
576
0
                               data->state.async.port, FALSE);
577
0
      thrdd->addr->res = NULL;
578
0
      if(!data->state.async.dns)
579
0
        result = CURLE_OUT_OF_MEMORY;
580
581
#ifdef USE_HTTPSRR_ARES
582
      if(thrdd->rr.channel) {
583
        result = thrdd->rr.result;
584
        if(!result) {
585
          struct Curl_https_rrinfo *lhrr;
586
          lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
587
          if(!lhrr)
588
            result = CURLE_OUT_OF_MEMORY;
589
          else
590
            data->state.async.dns->hinfo = lhrr;
591
        }
592
      }
593
#endif
594
0
      if(!result && data->state.async.dns)
595
0
        result = Curl_dnscache_add(data, data->state.async.dns);
596
0
    }
597
598
0
    if(!result && !data->state.async.dns)
599
0
      result = Curl_resolver_error(data);
600
0
    if(result)
601
0
      Curl_resolv_unlink(data, &data->state.async.dns);
602
0
    *dns = data->state.async.dns;
603
0
    CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
604
0
                 result, *dns ? "" : "not ");
605
0
    async_thrdd_destroy(data);
606
0
    return result;
607
0
  }
608
0
  else {
609
    /* poll for name lookup done with exponential backoff up to 250ms */
610
    /* should be fine even if this converts to 32-bit */
611
0
    timediff_t elapsed = curlx_timediff(curlx_now(),
612
0
                                       data->progress.t_startsingle);
613
0
    if(elapsed < 0)
614
0
      elapsed = 0;
615
616
0
    if(thrdd->addr->poll_interval == 0)
617
      /* Start at 1ms poll interval */
618
0
      thrdd->addr->poll_interval = 1;
619
0
    else if(elapsed >= thrdd->addr->interval_end)
620
      /* Back-off exponentially if last interval expired  */
621
0
      thrdd->addr->poll_interval *= 2;
622
623
0
    if(thrdd->addr->poll_interval > 250)
624
0
      thrdd->addr->poll_interval = 250;
625
626
0
    thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
627
0
    Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
628
0
    return CURLE_OK;
629
0
  }
630
0
}
631
632
int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
633
0
{
634
0
  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
635
0
  int ret_val = 0;
636
0
#if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES)
637
0
  int socketi = 0;
638
#else
639
  (void)socks;
640
#endif
641
642
#ifdef USE_HTTPSRR_ARES
643
  if(thrdd->rr.channel) {
644
    ret_val = Curl_ares_getsock(data, thrdd->rr.channel, socks);
645
    for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
646
      if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
647
         !ARES_GETSOCK_WRITABLE(ret_val, socketi))
648
        break;
649
  }
650
#endif
651
0
  if(!thrdd->addr)
652
0
    return ret_val;
653
654
0
#ifndef CURL_DISABLE_SOCKETPAIR
655
0
  if(thrdd->addr) {
656
    /* return read fd to client for polling the DNS resolution status */
657
0
    socks[socketi] = thrdd->addr->sock_pair[0];
658
0
    ret_val |= GETSOCK_READSOCK(socketi);
659
0
  }
660
0
  else
661
0
#endif
662
0
  {
663
0
    timediff_t milli;
664
0
    timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start);
665
0
    if(ms < 3)
666
0
      milli = 0;
667
0
    else if(ms <= 50)
668
0
      milli = ms/3;
669
0
    else if(ms <= 250)
670
0
      milli = 50;
671
0
    else
672
0
      milli = 200;
673
0
    Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
674
0
  }
675
676
0
  return ret_val;
677
0
}
678
679
#ifndef HAVE_GETADDRINFO
680
/*
681
 * Curl_async_getaddrinfo() - for platforms without getaddrinfo
682
 */
683
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
684
                                             const char *hostname,
685
                                             int port,
686
                                             int ip_version,
687
                                             int *waitp)
688
{
689
  (void)ip_version;
690
  *waitp = 0; /* default to synchronous response */
691
692
  /* fire up a new resolver thread! */
693
  if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
694
    *waitp = 1; /* expect asynchronous response */
695
    return NULL;
696
  }
697
698
  failf(data, "getaddrinfo() thread failed");
699
700
  return NULL;
701
}
702
703
#else /* !HAVE_GETADDRINFO */
704
705
/*
706
 * Curl_async_getaddrinfo() - for getaddrinfo
707
 */
708
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
709
                                             const char *hostname,
710
                                             int port,
711
                                             int ip_version,
712
                                             int *waitp)
713
0
{
714
0
  struct addrinfo hints;
715
0
  int pf = PF_INET;
716
0
  *waitp = 0; /* default to synchronous response */
717
718
0
  CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
719
0
#ifdef CURLRES_IPV6
720
0
  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
721
    /* The stack seems to be IPv6-enabled */
722
0
    if(ip_version == CURL_IPRESOLVE_V6)
723
0
      pf = PF_INET6;
724
0
    else
725
0
      pf = PF_UNSPEC;
726
0
  }
727
#else
728
  (void)ip_version;
729
#endif /* CURLRES_IPV6 */
730
731
0
  memset(&hints, 0, sizeof(hints));
732
0
  hints.ai_family = pf;
733
0
  hints.ai_socktype =
734
0
    (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
735
0
    SOCK_STREAM : SOCK_DGRAM;
736
737
  /* fire up a new resolver thread! */
738
0
  if(async_thrdd_init(data, hostname, port, ip_version, &hints)) {
739
0
    *waitp = 1; /* expect asynchronous response */
740
0
    return NULL;
741
0
  }
742
743
0
  failf(data, "getaddrinfo() thread failed to start");
744
0
  return NULL;
745
746
0
}
747
748
#endif /* !HAVE_GETADDRINFO */
749
750
#endif /* CURLRES_THREADED */