Coverage Report

Created: 2026-05-30 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/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
#include "curl_setup.h"
25
26
/***********************************************************************
27
 * Only for threaded name resolves builds
28
 **********************************************************************/
29
#ifdef USE_RESOLV_THREADED
30
31
#ifdef HAVE_NETINET_IN_H
32
#include <netinet/in.h>
33
#endif
34
#ifdef HAVE_NETDB_H
35
#include <netdb.h>
36
#endif
37
#ifdef HAVE_ARPA_INET_H
38
#include <arpa/inet.h>
39
#endif
40
#ifdef __VMS
41
#include <in.h>
42
#include <inet.h>
43
#endif
44
45
#ifdef HAVE_GETADDRINFO
46
0
#define RESOLVER_ENOMEM  EAI_MEMORY  /* = WSA_NOT_ENOUGH_MEMORY on Windows */
47
#else
48
#define RESOLVER_ENOMEM  SOCKENOMEM
49
#endif
50
51
#include "urldata.h"
52
#include "cfilters.h"
53
#include "curl_addrinfo.h"
54
#include "curl_trc.h"
55
#include "hostip.h"
56
#include "httpsrr.h"
57
#include "url.h"
58
#include "multiif.h"
59
#include "curl_threads.h"
60
#include "progress.h"
61
#include "rand.h"
62
#include "select.h"
63
#include "thrdqueue.h"
64
#include "curlx/strparse.h"
65
#include "curlx/wait.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
75
/*
76
 * Curl_async_global_init()
77
 * Called from curl_global_init() to initialize global resolver environment.
78
 * Does nothing here.
79
 */
80
int Curl_async_global_init(void)
81
1
{
82
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
83
  if(ares_library_init(ARES_LIB_INIT_ALL)) {
84
    return CURLE_FAILED_INIT;
85
  }
86
#endif
87
1
  return CURLE_OK;
88
1
}
89
90
/*
91
 * Curl_async_global_cleanup()
92
 * Called from curl_global_cleanup() to destroy global resolver environment.
93
 * Does nothing here.
94
 */
95
void Curl_async_global_cleanup(void)
96
0
{
97
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
98
  ares_library_cleanup();
99
#endif
100
0
}
101
102
#ifdef CURLVERBOSE
103
#define CURL_ASYN_ITEM_DESC_LEN   64
104
0
#define async_item_description(x)   (x)->description
105
#else
106
#define async_item_description(x)   NULL
107
#endif
108
109
struct async_thrdd_item {
110
  struct Curl_addrinfo *res;
111
#ifdef CURLVERBOSE
112
  char description[CURL_ASYN_ITEM_DESC_LEN];
113
#endif
114
  int sock_error;
115
  uint32_t mid;
116
  uint32_t resolv_id;
117
  uint16_t port;
118
  uint8_t transport;
119
  uint8_t dns_queries;
120
#ifdef DEBUGBUILD
121
  uint32_t delay_ms;
122
  uint32_t delay_fail_ms;
123
#endif
124
  char hostname[1];
125
};
126
127
/* Give up reference to add_ctx */
128
static void async_thrdd_item_destroy(struct async_thrdd_item *item)
129
0
{
130
0
  if(item) {
131
0
    if(item->res)
132
0
      Curl_freeaddrinfo(item->res);
133
0
    curlx_free(item);
134
0
  }
135
0
}
136
137
/* Initialize context for threaded resolver */
138
static struct async_thrdd_item *async_thrdd_item_create(
139
  struct Curl_easy *data,
140
  uint32_t resolv_id, uint8_t dns_queries,
141
  const char *hostname, uint16_t port,
142
  uint8_t transport)
143
0
{
144
0
  size_t hostlen = strlen(hostname);
145
0
  struct async_thrdd_item *item;
146
147
0
  item = curlx_calloc(1, sizeof(*item) + hostlen);
148
0
  if(!item)
149
0
    return NULL;
150
151
0
  if(hostlen) /* NUL byte of name already in struct size */
152
0
    memcpy(item->hostname, hostname, hostlen);
153
0
  item->mid = data->mid;
154
0
  item->resolv_id = resolv_id;
155
0
  item->dns_queries = dns_queries;
156
0
  item->port = port;
157
0
  item->transport = transport;
158
159
0
#ifdef CURLVERBOSE
160
0
  curl_msnprintf(item->description, sizeof(item->description),
161
0
                 "[%" FMT_OFF_T "/%u] %s %s:%u",
162
0
                 data->id, item->resolv_id,
163
0
                 Curl_resolv_query_str(dns_queries),
164
0
                 item->hostname, item->port);
165
0
#endif
166
167
0
#ifdef DEBUGBUILD
168
0
  {
169
0
    const char *p = getenv("CURL_DBG_RESOLV_DELAY");
170
0
    if(p) {
171
0
      curl_off_t l;
172
0
      if(!curlx_str_number(&p, &l, UINT32_MAX)) {
173
0
        item->delay_ms = (uint32_t)l;
174
0
      }
175
0
    }
176
0
    p = getenv("CURL_DBG_RESOLV_FAIL_DELAY");
177
0
    if(p) {
178
0
      curl_off_t l;
179
0
      if(!curlx_str_number(&p, &l, UINT32_MAX)) {
180
0
        unsigned char c = 0;
181
0
        Curl_rand_bytes(data, FALSE, &c, 1);
182
0
        item->delay_fail_ms = (uint32_t)l + c;
183
0
      }
184
0
    }
185
0
  }
186
0
#endif
187
188
0
  return item;
189
0
}
190
191
#ifdef USE_HTTPSRR_ARES
192
193
static void async_thrdd_rr_done(void *user_data, ares_status_t status,
194
                                size_t timeouts,
195
                                const ares_dns_record_t *dnsrec)
196
{
197
  struct Curl_resolv_async *async = user_data;
198
  struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
199
200
  (void)timeouts;
201
  if(!thrdd)
202
    return;
203
204
  async->dns_responses |= CURL_DNSQ_HTTPS;
205
  async->queries_ongoing--;
206
  async->done = !async->queries_ongoing;
207
  if((ARES_SUCCESS == status) && dnsrec)
208
    async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo);
209
}
210
211
static CURLcode async_rr_start(struct Curl_easy *data,
212
                               struct Curl_resolv_async *async)
213
{
214
  struct async_thrdd_ctx *thrdd = &async->thrdd;
215
  int status;
216
  char *rrname = NULL;
217
218
  DEBUGASSERT(!thrdd->rr.channel);
219
  if(async->port != 443) {
220
    rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname);
221
    if(!rrname)
222
      return CURLE_OUT_OF_MEMORY;
223
  }
224
  status = ares_init_options(&thrdd->rr.channel, NULL, 0);
225
  if(status != ARES_SUCCESS) {
226
    thrdd->rr.channel = NULL;
227
    curlx_free(rrname);
228
    return CURLE_FAILED_INIT;
229
  }
230
#ifdef DEBUGBUILD
231
  if(getenv("CURL_DNS_SERVER")) {
232
    const char *servers = getenv("CURL_DNS_SERVER");
233
    status = ares_set_servers_ports_csv(thrdd->rr.channel, servers);
234
    if(status) {
235
      curlx_free(rrname);
236
      return CURLE_FAILED_INIT;
237
    }
238
  }
239
#endif
240
241
  memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
242
  thrdd->rr.hinfo.rrname = rrname;
243
  async->queries_ongoing++;
244
  ares_query_dnsrec(thrdd->rr.channel,
245
                    rrname ? rrname : async->hostname, ARES_CLASS_IN,
246
                    ARES_REC_TYPE_HTTPS,
247
                    async_thrdd_rr_done, async, NULL);
248
  CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s",
249
               rrname ? rrname : async->hostname);
250
  return CURLE_OK;
251
}
252
#endif
253
254
void Curl_async_thrdd_shutdown(struct Curl_easy *data,
255
                               struct Curl_resolv_async *async)
256
0
{
257
0
  Curl_async_thrdd_destroy(data, async);
258
0
}
259
260
struct async_thrdd_match_ctx {
261
  uint32_t mid;
262
  uint32_t resolv_id;
263
};
264
265
static bool async_thrdd_match_item(void *qitem, void *match_data)
266
0
{
267
0
  const struct async_thrdd_match_ctx *ctx = match_data;
268
0
  struct async_thrdd_item *item = qitem;
269
0
  return (item->mid == ctx->mid) && (item->resolv_id == ctx->resolv_id);
270
0
}
271
272
void Curl_async_thrdd_destroy(struct Curl_easy *data,
273
                              struct Curl_resolv_async *async)
274
0
{
275
0
  (void)data;
276
0
  if(async->queries_ongoing && !async->done &&
277
0
     data->multi && data->multi->resolv_thrdq) {
278
    /* Remove any resolve items still queued */
279
0
    struct async_thrdd_match_ctx mctx;
280
0
    mctx.mid = data->mid;
281
0
    mctx.resolv_id = async->id;
282
0
    Curl_thrdq_clear(data->multi->resolv_thrdq,
283
0
                     async_thrdd_match_item, &mctx);
284
0
  }
285
#ifdef USE_HTTPSRR_ARES
286
  if(async->thrdd.rr.channel) {
287
    ares_destroy(async->thrdd.rr.channel);
288
    async->thrdd.rr.channel = NULL;
289
  }
290
  Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo);
291
#endif
292
0
  async_thrdd_item_destroy(async->thrdd.res_A);
293
0
  async->thrdd.res_A = NULL;
294
0
  async_thrdd_item_destroy(async->thrdd.res_AAAA);
295
0
  async->thrdd.res_AAAA = NULL;
296
0
}
297
298
/*
299
 * Waits for a resolve to finish. This function should be avoided since using
300
 * this risk getting the multi interface to "hang".
301
 */
302
CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
303
                          struct Curl_dns_entry **pdns)
304
0
{
305
0
  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
306
0
  struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
307
0
  timediff_t milli, ms;
308
309
0
  if(!thrdd)
310
0
    return CURLE_FAILED_INIT;
311
312
0
  while(async->queries_ongoing && !async->done) {
313
0
    Curl_async_thrdd_multi_process(data->multi);
314
0
    if(async->done)
315
0
      break;
316
317
0
    ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
318
0
    if(ms < 3)
319
0
      milli = 0;
320
0
    else if(ms <= 50)
321
0
      milli = ms / 3;
322
0
    else if(ms <= 250)
323
0
      milli = 50;
324
0
    else
325
0
      milli = 200;
326
0
    CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli);
327
0
    curlx_wait_ms(milli);
328
0
  }
329
0
  return Curl_async_take_result(data, async, pdns);
330
0
}
331
332
#ifdef HAVE_GETADDRINFO
333
334
/* Process the item, using Curl_getaddrinfo_ex() */
335
static void async_thrdd_item_process(void *arg)
336
0
{
337
0
  struct async_thrdd_item *item = arg;
338
0
  struct addrinfo hints;
339
0
  char service[12];
340
0
  int pf = PF_INET;
341
0
  int rc;
342
343
0
#ifdef DEBUGBUILD
344
0
  if(item->delay_ms) {
345
0
    curlx_wait_ms(item->delay_ms);
346
0
  }
347
0
  if(item->delay_fail_ms) {
348
0
    curlx_wait_ms(item->delay_fail_ms);
349
0
    return;
350
0
  }
351
0
#endif
352
353
0
  memset(&hints, 0, sizeof(hints));
354
0
#ifdef CURLRES_IPV6
355
0
  if(item->dns_queries & CURL_DNSQ_AAAA) {
356
0
    pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6;
357
0
  }
358
0
#endif
359
0
  hints.ai_family = pf;
360
0
  hints.ai_socktype = Curl_socktype_for_transport(item->transport);
361
0
  hints.ai_protocol = Curl_protocol_for_transport(item->transport);
362
#ifdef __APPLE__
363
  /* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED
364
   * when doing AAAA queries. We do not want this "help". */
365
  hints.ai_flags = AI_ADDRCONFIG;
366
#endif
367
368
0
  curl_msnprintf(service, sizeof(service), "%u", item->port);
369
0
#ifdef AI_NUMERICSERV
370
0
  hints.ai_flags |= AI_NUMERICSERV;
371
0
#endif
372
373
0
  rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
374
0
  if(rc) {
375
0
    item->sock_error = SOCKERRNO ? SOCKERRNO : rc;
376
0
    if(item->sock_error == 0)
377
0
      item->sock_error = RESOLVER_ENOMEM;
378
0
  }
379
0
  else {
380
0
    Curl_addrinfo_set_port(item->res, item->port);
381
0
  }
382
0
}
383
384
#else /* HAVE_GETADDRINFO */
385
386
/* Process the item, using Curl_ipv4_resolve_r() */
387
static void async_thrdd_item_process(void *arg)
388
{
389
  struct async_thrdd_item *item = arg;
390
391
#ifdef DEBUGBUILD
392
  if(item->delay_ms) {
393
    curlx_wait_ms(item->delay_ms);
394
  }
395
  if(item->delay_fail_ms) {
396
    curlx_wait_ms(item->delay_fail_ms);
397
    return;
398
  }
399
#endif
400
  item->res = Curl_ipv4_resolve_r(item->hostname, item->port);
401
  if(!item->res) {
402
    item->sock_error = SOCKERRNO;
403
    if(item->sock_error == 0)
404
      item->sock_error = RESOLVER_ENOMEM;
405
  }
406
}
407
408
#endif /* HAVE_GETADDRINFO */
409
410
#ifdef ENABLE_WAKEUP
411
static void async_thrdd_event(const struct curl_thrdq *tqueue,
412
                              Curl_thrdq_event ev,
413
                              void *user_data)
414
0
{
415
0
  struct Curl_multi *multi = user_data;
416
0
  (void)tqueue;
417
0
  switch(ev) {
418
0
  case CURL_THRDQ_EV_ITEM_DONE:
419
0
    (void)curl_multi_wakeup(multi);
420
0
    break;
421
0
  default:
422
0
    break;
423
0
  }
424
0
}
425
#else
426
#define async_thrdd_event   NULL
427
#endif
428
429
static void async_thrdd_item_free(void *item)
430
0
{
431
0
  async_thrdd_item_destroy(item);
432
0
}
433
434
/* Create a thread queue for processing resolv items */
435
CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi,
436
                                     uint32_t min_threads,
437
                                     uint32_t max_threads,
438
                                     uint32_t idle_time_ms)
439
0
{
440
0
  CURLcode result;
441
0
  DEBUGASSERT(!multi->resolv_thrdq);
442
0
  result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0,
443
0
                             min_threads, max_threads, idle_time_ms,
444
0
                             async_thrdd_item_free,
445
0
                             async_thrdd_item_process,
446
0
                             async_thrdd_event,
447
0
                             multi);
448
0
#ifdef DEBUGBUILD
449
0
  if(!result) {
450
0
    const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS");
451
0
    if(p) {
452
0
      curl_off_t l;
453
0
      if(!curlx_str_number(&p, &l, UINT32_MAX)) {
454
0
        result = Curl_async_thrdd_multi_set_props(
455
0
          multi, min_threads, (uint32_t)l, idle_time_ms);
456
0
      }
457
0
    }
458
0
  }
459
0
#endif
460
0
  return result;
461
0
}
462
463
/* Tear down the thread queue, joining active threads or detaching them */
464
void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join)
465
0
{
466
0
  if(multi->resolv_thrdq) {
467
0
#ifdef CURLVERBOSE
468
0
    CURL_TRC_DNS(multi->admin, "destroy thread queue+pool, join=%d", join);
469
0
    Curl_thrdq_trace(multi->resolv_thrdq, multi->admin);
470
0
#endif
471
0
    Curl_thrdq_destroy(multi->resolv_thrdq, join);
472
0
    multi->resolv_thrdq = NULL;
473
0
  }
474
0
}
475
476
#ifdef CURLVERBOSE
477
static void async_thrdd_report_item(struct Curl_easy *data,
478
                                    struct async_thrdd_item *item)
479
0
{
480
0
  char buf[MAX_IPADR_LEN];
481
0
  struct dynbuf tmp;
482
0
  const char *sep = "";
483
0
  const struct Curl_addrinfo *ai = item->res;
484
0
  int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET;
485
0
  CURLcode result;
486
487
0
  if(!CURL_TRC_DNS_is_verbose(data))
488
0
    return;
489
490
0
  curlx_dyn_init(&tmp, 1024);
491
0
  for(; ai; ai = ai->ai_next) {
492
0
    if(ai->ai_family == ai_family) {
493
0
      Curl_printable_address(ai, buf, sizeof(buf));
494
0
      result = curlx_dyn_addf(&tmp, "%s%s", sep, buf);
495
0
      if(result) {
496
0
        CURL_TRC_DNS(data, "too many IP, cannot show");
497
0
        goto out;
498
0
      }
499
0
      sep = ", ";
500
0
    }
501
0
  }
502
503
0
  CURL_TRC_DNS(data, "Host %s:%u resolved IPv%c: %s",
504
0
               item->hostname, item->port,
505
0
               (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4',
506
0
               (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)"));
507
0
out:
508
0
  curlx_dyn_free(&tmp);
509
0
}
510
#endif /* CURLVERBOSE */
511
512
/* Process the receiving end of the thread queue, dispatching
513
 * processed items to their transfer when it can still be found
514
 * and has an `async` state present. Otherwise, destroy the item. */
515
void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
516
0
{
517
0
  struct Curl_easy *data;
518
0
  void *qitem;
519
520
0
  while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) {
521
    /* dispatch resolve result */
522
0
    struct async_thrdd_item *item = qitem;
523
0
    struct Curl_resolv_async *async = NULL;
524
525
0
    data = Curl_multi_get_easy(multi, item->mid);
526
0
    if(data)
527
0
      async = Curl_async_get(data, item->resolv_id);
528
0
    if(async) {
529
0
      struct async_thrdd_item **pdest = &async->thrdd.res_A;
530
531
0
      async->dns_responses |= item->dns_queries;
532
0
      --async->queries_ongoing;
533
0
      async->done = !async->queries_ongoing;
534
535
0
#ifdef CURLRES_IPV6
536
0
      if(item->dns_queries & CURL_DNSQ_AAAA)
537
0
        pdest = &async->thrdd.res_AAAA;
538
0
#endif
539
0
      if(!*pdest) {
540
0
        VERBOSE(async_thrdd_report_item(data, item));
541
0
        *pdest = item;
542
0
        item = NULL;
543
0
      }
544
0
      else
545
0
        DEBUGASSERT(0); /* should not receive duplicates here */
546
0
      Curl_multi_mark_dirty(data);
547
0
    }
548
0
    async_thrdd_item_free(item);
549
0
  }
550
0
#ifdef CURLVERBOSE
551
0
  Curl_thrdq_trace(multi->resolv_thrdq, multi->admin);
552
0
#endif
553
0
}
554
555
CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
556
                                          uint32_t min_threads,
557
                                          uint32_t max_threads,
558
                                          uint32_t idle_time_ms)
559
0
{
560
0
  return Curl_thrdq_set_props(multi->resolv_thrdq, 0,
561
0
                              min_threads, max_threads, idle_time_ms);
562
0
}
563
564
static CURLcode async_thrdd_query(struct Curl_easy *data,
565
                                  struct Curl_resolv_async *async,
566
                                  uint8_t dns_queries)
567
0
{
568
0
  struct async_thrdd_item *item;
569
0
  CURLcode result;
570
571
0
  item = async_thrdd_item_create(data, async->id, dns_queries,
572
0
                                 async->hostname, async->port,
573
0
                                 async->transport);
574
0
  if(!item) {
575
0
    result = CURLE_OUT_OF_MEMORY;
576
0
    goto out;
577
0
  }
578
0
  CURL_TRC_DNS(data, "queueing query %s", item->description);
579
0
  result = Curl_thrdq_send(data->multi->resolv_thrdq, item,
580
0
                           async_item_description(item), async->timeout_ms);
581
0
  if(result)
582
0
    goto out;
583
0
  item = NULL;
584
0
  async->queries_ongoing++;
585
586
0
out:
587
0
  if(item)
588
0
    async_thrdd_item_free(item);
589
0
  return result;
590
0
}
591
592
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
593
                                struct Curl_resolv_async *async)
594
0
{
595
0
  CURLcode result = CURLE_FAILED_INIT;
596
0
  void *resolver = NULL;
597
598
0
  if(async->queries_ongoing || async->done)
599
0
    return CURLE_FAILED_INIT;
600
601
#ifdef USE_HTTPSRR_ARES
602
  DEBUGASSERT(!async->thrdd.rr.channel);
603
  if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) {
604
    result = async_rr_start(data, async);
605
    if(result)
606
      goto out;
607
    resolver = async->thrdd.rr.channel;
608
  }
609
#endif
610
611
0
  result = Curl_resolv_announce_start(data, resolver);
612
0
  if(result)
613
0
    return result;
614
615
0
#ifdef CURLRES_IPV6
616
  /* Do not start an AAAA query for an ipv4 address when
617
   * we will start an A query for it. */
618
0
  if((async->dns_queries & CURL_DNSQ_AAAA) &&
619
0
     !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) {
620
0
    result = async_thrdd_query(data, async, CURL_DNSQ_AAAA);
621
0
    if(result)
622
0
      goto out;
623
0
  }
624
0
#endif
625
0
  if(async->dns_queries & CURL_DNSQ_A) {
626
0
    result = async_thrdd_query(data, async, CURL_DNSQ_A);
627
0
    if(result)
628
0
      goto out;
629
0
  }
630
631
0
#ifdef CURLVERBOSE
632
0
  Curl_thrdq_trace(data->multi->resolv_thrdq, data);
633
0
#endif
634
635
0
out:
636
0
  if(result)
637
0
    CURL_TRC_DNS(data, "error queueing query %s:%d -> %d",
638
0
                 async->hostname, async->port, result);
639
0
  return result;
640
0
}
641
642
CURLcode Curl_async_pollset(struct Curl_easy *data,
643
                            struct Curl_resolv_async *async,
644
                            struct easy_pollset *ps)
645
0
{
646
0
  timediff_t timeout_ms;
647
648
0
  timeout_ms = Curl_async_timeleft_ms(data, async);
649
#ifdef USE_HTTPSRR_ARES
650
  if(async->thrdd.rr.channel) {
651
    CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps);
652
    if(result)
653
      return result;
654
    timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel);
655
  }
656
#else
657
0
  (void)ps;
658
0
#endif
659
660
0
  if(!async->done) {
661
#ifndef ENABLE_WAKEUP
662
    timediff_t stutter_ms, elapsed_ms;
663
    elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
664
    if(elapsed_ms < 3)
665
      stutter_ms = 1;
666
    else if(elapsed_ms <= 50)
667
      stutter_ms = elapsed_ms / 3;
668
    else if(elapsed_ms <= 250)
669
      stutter_ms = 50;
670
    else
671
      stutter_ms = 200;
672
    timeout_ms = CURLMIN(stutter_ms, timeout_ms);
673
#endif
674
0
    Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME);
675
0
  }
676
0
  return CURLE_OK;
677
0
}
678
679
/*
680
 * Curl_async_take_result() is called repeatedly to check if a previous
681
 * name resolve request has completed. It should also make sure to time-out if
682
 * the operation seems to take too long.
683
 */
684
CURLcode Curl_async_take_result(struct Curl_easy *data,
685
                                struct Curl_resolv_async *async,
686
                                struct Curl_dns_entry **pdns)
687
0
{
688
0
  struct async_thrdd_ctx *thrdd = &async->thrdd;
689
0
  struct Curl_dns_entry *dns = NULL;
690
0
  CURLcode result = CURLE_OK;
691
692
0
  DEBUGASSERT(pdns);
693
0
  *pdns = NULL;
694
0
  if(!async->queries_ongoing && !async->done) {
695
0
    DEBUGASSERT(0);
696
0
    return CURLE_FAILED_INIT;
697
0
  }
698
699
#ifdef USE_HTTPSRR_ARES
700
  /* best effort, ignore errors */
701
  if(thrdd->rr.channel)
702
    (void)Curl_ares_perform(thrdd->rr.channel, 0);
703
#endif
704
#ifndef ENABLE_WAKEUP
705
  Curl_async_thrdd_multi_process(data->multi);
706
#endif
707
708
0
  if(!async->done)
709
0
    return CURLE_AGAIN;
710
711
0
  Curl_expire_done(data, EXPIRE_ASYNC_NAME);
712
0
  if(async->result) {
713
0
    result = async->result;
714
0
    goto out;
715
0
  }
716
717
0
  if((thrdd->res_A && thrdd->res_A->res) ||
718
0
     (thrdd->res_AAAA && thrdd->res_AAAA->res)) {
719
0
    dns = Curl_dnscache_mk_entry2(
720
0
      data, async->dns_queries,
721
0
      thrdd->res_A ? &thrdd->res_A->res : NULL,
722
0
      thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL,
723
0
      async->hostname, async->port);
724
0
    if(!dns) {
725
0
      result = CURLE_OUT_OF_MEMORY;
726
0
      goto out;
727
0
    }
728
729
#ifdef USE_HTTPSRR_ARES
730
    if(thrdd->rr.channel) {
731
      struct Curl_https_rrinfo *lhrr = NULL;
732
      if(thrdd->rr.hinfo.complete) {
733
        lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
734
        if(!lhrr) {
735
          result = CURLE_OUT_OF_MEMORY;
736
          goto out;
737
        }
738
      }
739
      Curl_httpsrr_trace(data, lhrr);
740
      Curl_dns_entry_set_https_rr(dns, lhrr);
741
    }
742
#endif
743
0
  }
744
745
0
  if(dns) {
746
0
    *pdns = dns;
747
0
    dns = NULL;
748
0
  }
749
0
#ifdef CURLVERBOSE
750
0
  Curl_thrdq_trace(data->multi->resolv_thrdq, data);
751
0
#endif
752
753
0
out:
754
0
  Curl_dns_entry_unlink(data, &dns);
755
0
  Curl_async_thrdd_shutdown(data, async);
756
0
  if(!result && !*pdns)
757
0
    result = Curl_async_failed(data, async, NULL);
758
0
  if(result &&
759
0
     (result != CURLE_COULDNT_RESOLVE_HOST) &&
760
0
     (result != CURLE_COULDNT_RESOLVE_PROXY)) {
761
0
    CURL_TRC_DNS(data, "Error %d resolving %s:%d",
762
0
                 result, async->hostname, async->port);
763
0
  }
764
0
  return result;
765
0
}
766
767
static const struct Curl_addrinfo *async_thrdd_get_ai(
768
  const struct Curl_addrinfo *ai,
769
  int ai_family, unsigned int index)
770
0
{
771
0
  unsigned int i = 0;
772
0
  for(i = 0; ai; ai = ai->ai_next) {
773
0
    if(ai->ai_family == ai_family) {
774
0
      if(i == index)
775
0
        return ai;
776
0
      ++i;
777
0
    }
778
0
  }
779
0
  return NULL;
780
0
}
781
782
const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data,
783
                                              struct Curl_resolv_async *async,
784
                                              int ai_family,
785
                                              unsigned int index)
786
0
{
787
0
  struct async_thrdd_ctx *thrdd = &async->thrdd;
788
789
0
  (void)data;
790
0
  switch(ai_family) {
791
0
  case AF_INET:
792
0
    if(thrdd->res_A)
793
0
      return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index);
794
0
    break;
795
0
  case AF_INET6:
796
0
    if(thrdd->res_AAAA)
797
0
      return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index);
798
0
    break;
799
0
  default:
800
0
    break;
801
0
  }
802
0
  return NULL;
803
0
}
804
805
#ifdef USE_HTTPSRR
806
const struct Curl_https_rrinfo *Curl_async_get_https(
807
  struct Curl_easy *data,
808
  struct Curl_resolv_async *async)
809
{
810
#ifdef USE_HTTPSRR_ARES
811
  if(Curl_async_knows_https(data, async))
812
    return &async->thrdd.rr.hinfo;
813
#else
814
  (void)data;
815
  (void)async;
816
#endif
817
  return NULL;
818
}
819
820
bool Curl_async_knows_https(struct Curl_easy *data,
821
                            struct Curl_resolv_async *async)
822
{
823
  (void)data;
824
  if(async->dns_queries & CURL_DNSQ_HTTPS)
825
    return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done);
826
  return TRUE; /* we know it will never come */
827
}
828
829
#endif /* USE_HTTPSRR */
830
831
#endif /* USE_RESOLV_THREADED */