Coverage Report

Created: 2026-10-15 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/vdns/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 "url.h"
56
#include "multiif.h"
57
#include "curl_threads.h"
58
#include "progress.h"
59
#include "rand.h"
60
#include "select.h"
61
#include "thrdqueue.h"
62
#include "vdns/hostip.h"
63
#include "vdns/httpsrr.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 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) /* null-terminator 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, &async->httpsrr);
213
}
214
215
/* Initialize the c-ares channel used for the HTTPS RR side-query, without
216
 * submitting any query on it yet. */
217
static CURLcode async_rr_init(struct Curl_easy *data,
218
                              struct Curl_resolv_async *async)
219
{
220
  struct async_thrdd_ctx *thrdd = &async->thrdd;
221
  int status;
222
223
  (void)data;
224
  DEBUGASSERT(!thrdd->rr.channel);
225
  status = ares_init_options(&thrdd->rr.channel, NULL, 0);
226
  if(status != ARES_SUCCESS) {
227
    thrdd->rr.channel = NULL;
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
      return CURLE_FAILED_INIT;
236
  }
237
#endif
238
  return CURLE_OK;
239
}
240
241
/* Submit the HTTPS RR query on the already initialized channel. */
242
static CURLcode async_rr_start(struct Curl_easy *data,
243
                               struct Curl_resolv_async *async)
244
{
245
  struct async_thrdd_ctx *thrdd = &async->thrdd;
246
  char *https_name = NULL;
247
  CURLcode result = CURLE_OK;
248
249
  if(async->peer->port != 443) {
250
    https_name = curl_maprintf("_%u._https.%s",
251
                               async->peer->port, async->peer->hostname);
252
    if(!https_name) {
253
      result = CURLE_OUT_OF_MEMORY;
254
      goto out;
255
    }
256
  }
257
258
  async->queries_ongoing++;
259
  ares_query_dnsrec(thrdd->rr.channel,
260
                    https_name ? https_name : async->peer->hostname,
261
                    ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
262
                    async_thrdd_rr_done, async, NULL);
263
  CURL_TRC_DNS(data, "[HTTPS] query records for %s",
264
               https_name ? https_name : async->peer->hostname);
265
266
out:
267
  curlx_free(https_name);
268
  return result;
269
}
270
#endif
271
272
void Curl_async_thrdd_shutdown(struct Curl_easy *data,
273
                               struct Curl_resolv_async *async)
274
0
{
275
0
  Curl_async_thrdd_destroy(data, async);
276
0
}
277
278
struct async_thrdd_match_ctx {
279
  uint32_t mid;
280
  uint32_t resolv_id;
281
};
282
283
static bool async_thrdd_match_item(void *qitem, void *match_data)
284
0
{
285
0
  const struct async_thrdd_match_ctx *ctx = match_data;
286
0
  struct async_thrdd_item *item = qitem;
287
0
  return (item->mid == ctx->mid) && (item->resolv_id == ctx->resolv_id);
288
0
}
289
290
void Curl_async_thrdd_destroy(struct Curl_easy *data,
291
                              struct Curl_resolv_async *async)
292
0
{
293
0
  (void)data;
294
0
  if(async->queries_ongoing && !async->done &&
295
0
     data->multi && data->multi->resolv_thrdq) {
296
    /* Remove any resolve items still queued */
297
0
    struct async_thrdd_match_ctx mctx;
298
0
    mctx.mid = data->mid;
299
0
    mctx.resolv_id = async->id;
300
0
    Curl_thrdq_clear(data->multi->resolv_thrdq,
301
0
                     async_thrdd_match_item, &mctx);
302
0
  }
303
#ifdef USE_HTTPSRR_ARES
304
  if(async->thrdd.rr.channel) {
305
    ares_destroy(async->thrdd.rr.channel);
306
    async->thrdd.rr.channel = NULL;
307
  }
308
#endif
309
0
  async_thrdd_item_destroy(async->thrdd.res_A);
310
0
  async->thrdd.res_A = NULL;
311
0
  async_thrdd_item_destroy(async->thrdd.res_AAAA);
312
0
  async->thrdd.res_AAAA = NULL;
313
0
}
314
315
/*
316
 * Waits for a resolve to finish. This function should be avoided since using
317
 * this risk getting the multi interface to "hang".
318
 */
319
CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
320
                          struct Curl_dns_entry **pdns)
321
0
{
322
0
  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
323
0
  struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
324
0
  timediff_t milli, ms;
325
0
  CURLcode result = CURLE_AGAIN;
326
327
0
  if(!thrdd)
328
0
    return CURLE_FAILED_INIT;
329
330
0
  while(result == CURLE_AGAIN) {
331
0
    while(async->queries_ongoing && !async->done) {
332
0
      Curl_async_thrdd_multi_process(data->multi);
333
0
      if(async->done)
334
0
        break;
335
336
0
      ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
337
0
      if(ms < 3)
338
0
        milli = 0;
339
0
      else if(ms <= 50)
340
0
        milli = ms / 3;
341
0
      else if(ms <= 250)
342
0
        milli = 50;
343
0
      else
344
0
        milli = 200;
345
0
      CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli);
346
0
      curlx_wait_ms(milli);
347
0
    }
348
0
    result = Curl_async_take_result(data, async, pdns);
349
0
  }
350
0
  return result;
351
0
}
352
353
#ifdef HAVE_GETADDRINFO
354
355
/* Was the getaddrinfo() failure an authoritative negative answer,
356
   i.e. the resolver responded that the name (or its data) does not
357
   exist? Transient failures like EAI_AGAIN and local troubles must
358
   not count as negative answers. */
359
static bool gai_negative(int rc)
360
0
{
361
0
  switch(rc) {
362
0
#ifdef EAI_NONAME
363
0
  case EAI_NONAME:
364
0
#endif
365
0
#if defined(EAI_NODATA) && \
366
0
  (!defined(EAI_NONAME) || (EAI_NODATA != EAI_NONAME))
367
0
  case EAI_NODATA:
368
0
#endif
369
0
    return TRUE;
370
0
  default:
371
0
    return FALSE;
372
0
  }
373
0
}
374
375
/* Process the item, using Curl_getaddrinfo_ex() */
376
static void async_thrdd_item_process(void *arg)
377
0
{
378
0
  struct async_thrdd_item *item = arg;
379
0
  struct addrinfo hints;
380
0
  char service[12];
381
0
  int pf = PF_INET;
382
0
  int rc;
383
384
0
#ifdef DEBUGBUILD
385
0
  if(item->delay_ms) {
386
0
    curlx_wait_ms(item->delay_ms);
387
0
  }
388
0
  if(item->delay_fail_ms) {
389
0
    curlx_wait_ms(item->delay_fail_ms);
390
0
    item->negative = item->dbg_negative;
391
0
    return;
392
0
  }
393
0
#endif
394
395
0
  memset(&hints, 0, sizeof(hints));
396
0
#ifdef CURLRES_IPV6
397
0
  if(item->dns_queries & CURL_DNSQ_AAAA) {
398
0
    pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6;
399
0
  }
400
0
#endif
401
0
  hints.ai_family = pf;
402
0
  hints.ai_socktype = Curl_socktype_for_transport(item->transport);
403
0
  hints.ai_protocol = Curl_protocol_for_transport(item->transport);
404
#ifdef __APPLE__
405
  /* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED
406
   * when doing AAAA queries. We do not want this "help". */
407
  hints.ai_flags = AI_ADDRCONFIG;
408
#endif
409
410
0
  curl_msnprintf(service, sizeof(service), "%u", item->port);
411
0
#ifdef AI_NUMERICSERV
412
0
  hints.ai_flags |= AI_NUMERICSERV;
413
0
#endif
414
415
0
  rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
416
0
  if(rc) {
417
0
    item->sockerr = SOCKERRNO ? SOCKERRNO : rc;
418
0
    if(item->sockerr == 0)
419
0
      item->sockerr = RESOLVER_ENOMEM;
420
0
    item->negative = gai_negative(rc);
421
0
  }
422
0
  else {
423
0
    Curl_addrinfo_set_port(item->res, item->port);
424
0
  }
425
0
}
426
427
#else /* HAVE_GETADDRINFO */
428
429
/* Process the item, using Curl_ipv4_resolve_r() */
430
static void async_thrdd_item_process(void *arg)
431
{
432
  struct async_thrdd_item *item = arg;
433
434
#ifdef DEBUGBUILD
435
  if(item->delay_ms) {
436
    curlx_wait_ms(item->delay_ms);
437
  }
438
  if(item->delay_fail_ms) {
439
    curlx_wait_ms(item->delay_fail_ms);
440
    item->negative = item->dbg_negative;
441
    return;
442
  }
443
#endif
444
  item->res = Curl_ipv4_resolve_r(item->hostname, item->port);
445
  if(!item->res) {
446
    item->sockerr = SOCKERRNO;
447
    if(item->sockerr == 0)
448
      item->sockerr = RESOLVER_ENOMEM;
449
    /* this resolver cannot tell a transient failure from an
450
       authoritative negative answer, treat it as before */
451
    item->negative = TRUE;
452
  }
453
}
454
455
#endif /* HAVE_GETADDRINFO */
456
457
#ifdef ENABLE_INTERNAL_WAKEUP
458
static void async_thrdd_event(const struct curl_thrdq *tqueue,
459
                              Curl_thrdq_event ev,
460
                              void *user_data)
461
0
{
462
0
  struct Curl_multi *multi = user_data;
463
0
  (void)tqueue;
464
0
  switch(ev) {
465
0
  case CURL_THRDQ_EV_ITEM_DONE:
466
0
    Curl_multi_wakeup_internal(multi);
467
0
    break;
468
0
  default:
469
0
    break;
470
0
  }
471
0
}
472
#else
473
#define async_thrdd_event   NULL
474
#endif
475
476
static void async_thrdd_item_free(void *item)
477
0
{
478
0
  async_thrdd_item_destroy(item);
479
0
}
480
481
/* Create a thread queue for processing resolv items */
482
CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi,
483
                                     uint32_t min_threads,
484
                                     uint32_t max_threads,
485
                                     uint32_t idle_time_ms)
486
13.6k
{
487
13.6k
  CURLcode result;
488
13.6k
  DEBUGASSERT(!multi->resolv_thrdq);
489
13.6k
  result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS",
490
13.6k
                             min_threads, max_threads, idle_time_ms,
491
13.6k
                             async_thrdd_item_free,
492
13.6k
                             async_thrdd_item_process,
493
13.6k
                             async_thrdd_event,
494
13.6k
                             multi);
495
13.6k
#ifdef DEBUGBUILD
496
13.6k
  if(!result) {
497
13.6k
    const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS");
498
13.6k
    if(p) {
499
0
      curl_off_t l;
500
0
      if(!curlx_str_number(&p, &l, UINT32_MAX)) {
501
0
        result = Curl_async_thrdd_multi_set_props(
502
0
          multi, min_threads, (uint32_t)l, idle_time_ms);
503
0
      }
504
0
    }
505
13.6k
  }
506
13.6k
#endif
507
13.6k
  return result;
508
13.6k
}
509
510
/* Tear down the thread queue, joining active threads or detaching them */
511
void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join)
512
13.6k
{
513
13.6k
  if(multi->resolv_thrdq) {
514
13.6k
#ifdef CURLVERBOSE
515
13.6k
    CURL_TRC_DNS(multi->admin, "destroy thread queue+pool, join=%d", join);
516
13.6k
    CURL_THRDQ_TRACE(multi->resolv_thrdq, multi->admin);
517
13.6k
#endif
518
13.6k
    Curl_thrdq_destroy(multi->resolv_thrdq, join);
519
13.6k
    multi->resolv_thrdq = NULL;
520
13.6k
  }
521
13.6k
}
522
523
#ifdef CURLVERBOSE
524
static void async_thrdd_report_item(struct Curl_easy *data,
525
                                    struct async_thrdd_item *item)
526
0
{
527
0
  char buf[MAX_IPADR_LEN];
528
0
  struct dynbuf tmp;
529
0
  const char *sep = "";
530
0
  const struct Curl_addrinfo *ai = item->res;
531
0
  CURLcode result;
532
0
  int ai_family;
533
0
#ifdef USE_IPV6
534
0
  ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET;
535
#else
536
  ai_family = AF_INET;
537
#endif
538
539
0
  if(!CURL_TRC_DNS_is_verbose(data))
540
0
    return;
541
542
0
  curlx_dyn_init(&tmp, 1024);
543
0
  for(; ai; ai = ai->ai_next) {
544
0
    if(ai->ai_family == ai_family) {
545
0
      Curl_printable_address(ai, buf, sizeof(buf));
546
0
      result = curlx_dyn_addf(&tmp, "%s%s", sep, buf);
547
0
      if(result) {
548
0
        CURL_TRC_DNS(data, "too many IP, cannot show");
549
0
        goto out;
550
0
      }
551
0
      sep = ", ";
552
0
    }
553
0
  }
554
555
0
  CURL_TRC_DNS(data, "Host %s:%u resolved IPv%c: %s",
556
0
               item->hostname, item->port,
557
0
               (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4',
558
0
               (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)"));
559
0
out:
560
0
  curlx_dyn_free(&tmp);
561
0
}
562
#endif /* CURLVERBOSE */
563
564
/* Process the receiving end of the thread queue, dispatching
565
 * processed items to their transfer when it can still be found
566
 * and has an `async` state present. Otherwise, destroy the item. */
567
void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
568
14.6k
{
569
14.6k
  struct Curl_easy *data;
570
14.6k
  void *qitem;
571
572
14.6k
  while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) {
573
    /* dispatch resolve result */
574
0
    struct async_thrdd_item *item = qitem;
575
0
    struct Curl_resolv_async *async = NULL;
576
577
0
    data = Curl_multi_get_easy(multi, item->mid);
578
0
    if(data)
579
0
      async = Curl_async_get(data, item->resolv_id);
580
0
    if(async) {
581
0
      struct async_thrdd_item **pdest = &async->thrdd.res_A;
582
583
0
#ifdef CURLRES_IPV6
584
0
      if(item->dns_queries & CURL_DNSQ_AAAA)
585
0
        pdest = &async->thrdd.res_AAAA;
586
0
#endif
587
0
      if(!*pdest) {
588
0
        *pdest = item;
589
0
        item = NULL;
590
0
      }
591
0
      else
592
0
        DEBUGASSERT(0); /* should not receive duplicates here */
593
594
0
      --async->queries_ongoing;
595
0
      Curl_multi_mark_dirty(data);
596
0
    }
597
0
    async_thrdd_item_free(item);
598
0
  }
599
14.6k
  CURL_THRDQ_TRACE(multi->resolv_thrdq, multi->admin);
600
14.6k
}
601
602
CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
603
                                          uint32_t min_threads,
604
                                          uint32_t max_threads,
605
                                          uint32_t idle_time_ms)
606
0
{
607
0
  return Curl_thrdq_set_props(multi->resolv_thrdq,
608
0
                              min_threads, max_threads, idle_time_ms);
609
0
}
610
611
static CURLcode async_thrdd_query(struct Curl_easy *data,
612
                                  struct Curl_resolv_async *async,
613
                                  uint8_t dns_queries)
614
0
{
615
0
  struct async_thrdd_item *item;
616
0
  CURLcode result;
617
618
0
  item = async_thrdd_item_create(data, async->id, dns_queries,
619
0
                                 async->peer->hostname, async->peer->port,
620
0
                                 async->transport);
621
0
  if(!item) {
622
0
    result = CURLE_OUT_OF_MEMORY;
623
0
    goto out;
624
0
  }
625
0
  CURL_TRC_DNS(data, "queueing query %s", item->description);
626
0
  result = Curl_thrdq_send(data->multi->resolv_thrdq, item,
627
0
                           async_item_description(item), async->timeout_ms);
628
0
  if(result)
629
0
    goto out;
630
0
  item = NULL;
631
0
  async->queries_ongoing++;
632
633
0
out:
634
0
  if(item)
635
0
    async_thrdd_item_free(item);
636
0
  return result;
637
0
}
638
639
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
640
                                struct Curl_resolv_async *async)
641
0
{
642
0
  CURLcode result = CURLE_FAILED_INIT;
643
0
  void *resolver = NULL;
644
645
0
  if(async->queries_ongoing || async->done)
646
0
    return CURLE_FAILED_INIT;
647
648
#ifdef USE_HTTPSRR_ARES
649
  DEBUGASSERT(!async->thrdd.rr.channel);
650
  if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) {
651
    result = async_rr_init(data, async);
652
    if(result)
653
      goto out;
654
    resolver = async->thrdd.rr.channel;
655
  }
656
#endif
657
658
0
  result = Curl_resolv_announce_start(data, resolver);
659
0
  if(result)
660
0
    return result;
661
662
#ifdef USE_HTTPSRR_ARES
663
  if(resolver) {
664
    result = async_rr_start(data, async);
665
    if(result)
666
      goto out;
667
  }
668
#endif
669
670
0
#ifdef CURLRES_IPV6
671
  /* Do not start an AAAA query for an IPv4 address when
672
   * we will start an A query for it. */
673
0
  if((async->dns_queries & CURL_DNSQ_AAAA) &&
674
0
     !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) {
675
0
    result = async_thrdd_query(data, async, CURL_DNSQ_AAAA);
676
0
    if(result)
677
0
      goto out;
678
0
  }
679
0
#endif
680
0
  if(async->dns_queries & CURL_DNSQ_A) {
681
0
    result = async_thrdd_query(data, async, CURL_DNSQ_A);
682
0
    if(result)
683
0
      goto out;
684
0
  }
685
686
0
  CURL_THRDQ_TRACE(data->multi->resolv_thrdq, data);
687
688
0
out:
689
0
  if(!async->queries_ongoing)
690
0
    async->done = TRUE;
691
692
0
  if(result)
693
0
    CURL_TRC_DNS(data, "error queueing query %s:%d -> %d",
694
0
                 async->peer->hostname, async->peer->port, (int)result);
695
0
  return result;
696
0
}
697
698
CURLcode Curl_async_pollset(struct Curl_easy *data,
699
                            struct Curl_resolv_async *async,
700
                            struct easy_pollset *ps)
701
0
{
702
0
  timediff_t timeout_ms;
703
704
0
  timeout_ms = Curl_async_timeleft_ms(data, async);
705
#ifdef USE_HTTPSRR_ARES
706
  if(async->thrdd.rr.channel) {
707
    CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps);
708
    if(result)
709
      return result;
710
    timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel);
711
  }
712
#else
713
0
  (void)ps;
714
0
#endif
715
716
0
  if(!async->done) {
717
0
    const struct curltime *pnow = Curl_pgrs_now(data);
718
#ifndef ENABLE_INTERNAL_WAKEUP
719
    timediff_t stutter_ms, elapsed_ms;
720
    elapsed_ms = curlx_ptimediff_ms(pnow, &async->start);
721
    if(elapsed_ms < 3)
722
      stutter_ms = 1;
723
    else if(elapsed_ms <= 50)
724
      stutter_ms = elapsed_ms / 3;
725
    else if(elapsed_ms <= 250)
726
      stutter_ms = 50;
727
    else
728
      stutter_ms = 200;
729
    timeout_ms = CURLMIN(stutter_ms, timeout_ms);
730
#else
731
0
    if(async->queries_ongoing &&
732
0
       !Curl_thrdq_check_started(data->multi->resolv_thrdq)) {
733
      /* The queue has items but starting a worker thread to process
734
         them just failed again; expire soon to check once more,
735
         instead of sleeping on the full resolve timeout. */
736
0
      CURL_TRC_DNS(data, "resolver thread start failed again, retrying");
737
0
      timeout_ms = CURLMIN(100, timeout_ms);
738
0
    }
739
0
#endif
740
0
    Curl_expire_set(data, EXPIRE_ASYNC_NAME, timeout_ms, pnow);
741
0
  }
742
0
  return CURLE_OK;
743
0
}
744
745
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
746
static bool async_thrdd_item_missing_scope(struct Curl_easy *data,
747
                                           struct async_thrdd_item *item)
748
0
{
749
0
  const struct Curl_addrinfo *ai;
750
751
  /* scope id already globally set */
752
0
  if(data->conn && data->conn->scope_id)
753
0
    return FALSE;
754
755
0
  for(ai = item->res; ai; ai = ai->ai_next) {
756
0
    if(ai->ai_family == AF_INET6) {
757
0
      struct sockaddr_in6 *sa6 = (void *)ai->ai_addr;
758
0
      if(IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) && !sa6->sin6_scope_id)
759
0
        return TRUE;
760
0
    }
761
0
  }
762
0
  return FALSE;
763
0
}
764
765
static void async_thrdd_item_strip_results(struct async_thrdd_item *item,
766
                                           int ai_family)
767
0
{
768
0
  struct Curl_addrinfo *ai = item->res, **panchor = &item->res;
769
0
  while(ai) {
770
0
    if(ai->ai_family == ai_family) {
771
0
      *panchor = ai->ai_next;
772
0
      ai->ai_next = NULL;
773
0
      Curl_freeaddrinfo(ai);
774
0
      ai = *panchor;
775
0
    }
776
0
    else {
777
0
      panchor = &ai->ai_next;
778
0
      ai = ai->ai_next;
779
0
    }
780
0
  }
781
0
}
782
783
#endif /* USE_IPV6 && HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID */
784
785
static CURLcode async_thrdd_check_done(struct Curl_easy *data,
786
                                       struct Curl_resolv_async *async)
787
0
{
788
0
  struct async_thrdd_ctx *thrdd = &async->thrdd;
789
790
0
  (void)data;
791
0
  if(thrdd->res_A && !thrdd->processed_A) {
792
0
    VERBOSE(async_thrdd_report_item(data, thrdd->res_A));
793
    /* move addrinfos to the async result member */
794
0
    async->dns_responses |= thrdd->res_A->dns_queries;
795
0
    async->ai_A = thrdd->res_A->res;
796
0
    thrdd->res_A->res = NULL;
797
0
    thrdd->processed_A = TRUE;
798
0
  }
799
800
0
  if(thrdd->res_AAAA && !thrdd->processed_AAAA) {
801
0
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
802
    /* do we accept the incoming AAAA response? */
803
0
    if(!(thrdd->res_AAAA->dns_queries & CURL_DNSQ_A) &&
804
0
       async_thrdd_item_missing_scope(data, thrdd->res_AAAA)) {
805
      /* We queried "only" AF_INET6. This may be problematic when the
806
       * result has ipv6 link-local addresses and did not give
807
       * any scope id for it. glibc has a long outstanding bug
808
       * <https://sourceware.org/bugzilla/show_bug.cgi?id=14413>
809
       * that gives scope ids only on AF_UNSPEC queries. */
810
0
      struct async_thrdd_item *item = thrdd->res_AAAA;
811
0
      CURLcode result;
812
813
      /* Reuse the item and queue it again, this time with added
814
       * CURL_DNSQ_A which resolves using AF_UNSPEC. */
815
0
      thrdd->res_AAAA = NULL;
816
0
      item->dns_queries |= CURL_DNSQ_A;
817
0
      if(item->res) {
818
0
        Curl_freeaddrinfo(item->res);
819
0
        item->res = NULL;
820
0
      }
821
822
0
      CURL_TRC_DNS(data, "re-queueing query %s for AF_UNSPEC resolve",
823
0
                   item->description);
824
0
      result = Curl_thrdq_send(data->multi->resolv_thrdq, item,
825
0
                               async_item_description(item),
826
0
                               async->timeout_ms);
827
0
      if(result) {
828
0
        async_thrdd_item_free(item);
829
0
        return result;
830
0
      }
831
0
      async->queries_ongoing++;
832
0
      return CURLE_AGAIN;
833
0
    }
834
    /* accepting the AAAA result, strip it of any AF_INET entries,
835
     * as we might have resolved it with AF_UNSPEC. */
836
0
    async_thrdd_item_strip_results(thrdd->res_AAAA, AF_INET);
837
0
#endif /* USE_IPV6 && HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID */
838
0
    VERBOSE(async_thrdd_report_item(data, thrdd->res_AAAA));
839
    /* move addrinfos to the async result member */
840
0
    async->dns_responses |= thrdd->res_AAAA->dns_queries;
841
0
    async->ai_AAAA = thrdd->res_AAAA->res;
842
0
    thrdd->res_AAAA->res = NULL;
843
0
    thrdd->processed_AAAA = TRUE;
844
0
  }
845
846
0
  if(async->queries_ongoing)
847
0
    return CURLE_AGAIN;
848
0
  async->done = TRUE;
849
0
  return CURLE_OK;
850
0
}
851
852
/*
853
 * Curl_async_take_result() is called repeatedly to check if a previous
854
 * name resolve request has completed. It should also make sure to time-out if
855
 * the operation seems to take too long.
856
 */
857
CURLcode Curl_async_take_result(struct Curl_easy *data,
858
                                struct Curl_resolv_async *async,
859
                                struct Curl_dns_entry **pdns)
860
0
{
861
0
  struct async_thrdd_ctx *thrdd = &async->thrdd;
862
0
  struct Curl_dns_entry *dns = NULL;
863
0
  CURLcode result = CURLE_OK;
864
865
0
  DEBUGASSERT(pdns);
866
0
  *pdns = NULL;
867
868
#ifdef USE_HTTPSRR_ARES
869
  /* best effort, ignore errors */
870
  if(thrdd->rr.channel)
871
    (void)Curl_ares_perform(thrdd->rr.channel, 0);
872
#endif
873
#ifndef ENABLE_INTERNAL_WAKEUP
874
  Curl_async_thrdd_multi_process(data->multi);
875
#endif
876
877
0
  result = async_thrdd_check_done(data, async);
878
0
  if(result)
879
0
    return result;
880
881
0
  Curl_expire_clear(data, EXPIRE_ASYNC_NAME);
882
883
  /* A failure is an authoritative negative answer, eligible for
884
     negative caching, only when every A/AAAA query performed came
885
     back answering that the name does not exist. A query that
886
     failed transiently or never returned is not an answer. */
887
0
  {
888
0
    const uint8_t ip_queries =
889
0
      async->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA);
890
0
    bool negative = (async->dns_responses & ip_queries) == ip_queries;
891
0
    if(thrdd->res_A && (async->ai_A || !thrdd->res_A->negative))
892
0
      negative = FALSE;
893
0
    if(thrdd->res_AAAA && (async->ai_AAAA || !thrdd->res_AAAA->negative))
894
0
      negative = FALSE;
895
0
    if(!thrdd->res_A && !thrdd->res_AAAA)
896
0
      negative = FALSE;
897
0
    async->negative_answer = negative;
898
0
  }
899
900
0
  if(async->result) {
901
0
    result = async->result;
902
0
    goto out;
903
0
  }
904
905
0
  if(async->ai_A || async->ai_AAAA) {
906
0
    dns = Curl_dnsc_mk_addr2(
907
0
      data, async->dns_queries, &async->ai_A, &async->ai_AAAA, async->peer);
908
0
    if(!dns) {
909
0
      result = CURLE_OUT_OF_MEMORY;
910
0
      goto out;
911
0
    }
912
0
  }
913
914
#ifdef USE_HTTPSRR_ARES
915
  if(!dns && thrdd->rr.channel) {
916
    Curl_httpsrr_trace(data, async->httpsrr);
917
    dns = Curl_dnsc_mk_https(data, &async->httpsrr, async->peer);
918
    if(!dns) {
919
      result = CURLE_OUT_OF_MEMORY;
920
      goto out;
921
    }
922
  }
923
#endif
924
925
0
  if(dns) {
926
0
    *pdns = dns;
927
0
    dns = NULL;
928
0
  }
929
0
  CURL_THRDQ_TRACE(data->multi->resolv_thrdq, data);
930
931
0
out:
932
0
  Curl_dns_entry_unlink(data, &dns);
933
0
  Curl_async_thrdd_shutdown(data, async);
934
0
  if(!result && !*pdns)
935
0
    result = Curl_async_failed(data, async, NULL);
936
0
  if(result &&
937
0
     (result != CURLE_COULDNT_RESOLVE_HOST) &&
938
0
     (result != CURLE_COULDNT_RESOLVE_PROXY)) {
939
0
    CURL_TRC_DNS(data, "Error %d resolving %s:%d",
940
0
                 (int)result, async->peer->hostname, async->peer->port);
941
0
  }
942
0
  return result;
943
0
}
944
945
#endif /* USE_RESOLV_THREADED */