Coverage Report

Created: 2025-07-18 07:18

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