Coverage Report

Created: 2026-07-16 06:10

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