Coverage Report

Created: 2025-07-11 06:33

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