Coverage Report

Created: 2026-09-01 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/vdns/hostip.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
#ifdef HAVE_NETINET_IN_H
27
#include <netinet/in.h>
28
#endif
29
#ifdef HAVE_NETINET_IN6_H
30
#include <netinet/in6.h>
31
#endif
32
#ifdef HAVE_NETDB_H
33
#include <netdb.h>
34
#endif
35
#ifdef HAVE_ARPA_INET_H
36
#include <arpa/inet.h>
37
#endif
38
#ifdef __VMS
39
#include <in.h>
40
#include <inet.h>
41
#endif
42
43
#include <setjmp.h>  /* for sigjmp_buf, sigsetjmp() */
44
#include <signal.h>
45
46
#include "urldata.h"
47
#include "curl_addrinfo.h"
48
#include "curl_trc.h"
49
#include "url.h"
50
#include "multiif.h"
51
#include "progress.h"
52
#include "select.h"
53
#include "strcase.h"
54
#include "easy_lock.h"
55
#include "vdns/dnscache.h"
56
#include "vdns/doh.h"
57
#include "vdns/hostip.h"
58
#include "vdns/httpsrr.h"
59
#include "curlx/inet_ntop.h"
60
#include "curlx/inet_pton.h"
61
#include "curlx/strcopy.h"
62
#include "curlx/strparse.h"
63
64
#if defined(CURLRES_SYNCH) &&                   \
65
  defined(HAVE_ALARM) &&                        \
66
  defined(SIGALRM) &&                           \
67
  defined(HAVE_SIGSETJMP) &&                    \
68
  defined(GLOBAL_INIT_IS_THREADSAFE)
69
/* alarm-based timeouts can only be used with all the dependencies satisfied */
70
#define USE_ALARM_TIMEOUT
71
#endif
72
73
#define RESOLV_FAIL(for_proxy) \
74
145k
  ((for_proxy) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST)
75
76
#define IS_RESOLV_FAIL(result) \
77
810
  (((result) == CURLE_COULDNT_RESOLVE_HOST) || \
78
810
   ((result) == CURLE_COULDNT_RESOLVE_PROXY))
79
/*
80
 * ipv6works() returns TRUE if IPv6 seems to work.
81
 */
82
#ifdef USE_IPV6
83
static bool ipv6works(struct Curl_easy *data);
84
#else
85
#define ipv6works(x) FALSE
86
#endif
87
88
/*
89
 * hostip.c explained
90
 * ==================
91
 *
92
 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
93
 * source file are these:
94
 *
95
 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
96
 * that. The host may not be able to resolve IPv6, but we do not really have to
97
 * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4
98
 * defined.
99
 *
100
 * USE_RESOLV_ARES - is defined if libcurl is built to use c-ares for
101
 * asynchronous name resolves. This can be Windows or *nix.
102
 *
103
 * USE_RESOLV_THREADED - is defined if libcurl is built to run under (native)
104
 * Windows, and then the name resolve will be done in a new thread, and the
105
 * supported API will be the same as for ares-builds.
106
 *
107
 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
108
 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
109
 * defined.
110
 *
111
 * The host*.c sources files are split up like this:
112
 *
113
 * hostip.c   - method-independent resolver functions and utility functions
114
 * hostip4.c  - IPv4 specific functions
115
 * hostip6.c  - IPv6 specific functions
116
 * asyn.h     - common functions for all async resolvers
117
 * The two asynchronous name resolver backends are implemented in:
118
 * asyn-ares.c - async resolver using c-ares
119
 * asyn-thread.c - async resolver using POSIX threads
120
 *
121
 * The hostip.h is the united header file for all this. It defines the
122
 * CURLRES_* defines based on the config*.h and curl_setup.h defines.
123
 */
124
125
uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version)
126
147k
{
127
147k
  (void)data;
128
147k
  switch(ip_version) {
129
70
  case CURL_IPRESOLVE_V6:
130
70
    return CURL_DNSQ_AAAA;
131
136
  case CURL_IPRESOLVE_V4:
132
136
    return CURL_DNSQ_A;
133
147k
  default:
134
147k
    if(ipv6works(data))
135
147k
      return (CURL_DNSQ_A | CURL_DNSQ_AAAA);
136
0
    else
137
0
      return CURL_DNSQ_A;
138
147k
  }
139
147k
}
140
141
#ifdef CURLVERBOSE
142
const char *Curl_resolv_query_str(uint8_t dns_queries)
143
356
{
144
356
  switch(dns_queries) {
145
0
  case (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS):
146
0
    return "A+AAAA+HTTPS";
147
0
  case (CURL_DNSQ_A | CURL_DNSQ_AAAA):
148
0
    return "A+AAAA";
149
0
  case (CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS):
150
0
    return "AAAA+HTTPS";
151
179
  case (CURL_DNSQ_AAAA):
152
179
    return "AAAA";
153
0
  case (CURL_DNSQ_A | CURL_DNSQ_HTTPS):
154
0
    return "A+HTTPS";
155
177
  case (CURL_DNSQ_A):
156
177
    return "A";
157
0
  case (CURL_DNSQ_HTTPS):
158
0
    return "HTTPS";
159
0
  case 0:
160
0
    return "-";
161
0
  default:
162
0
    DEBUGASSERT(0);
163
0
    return "???";
164
356
  }
165
356
}
166
#endif
167
168
/*
169
 * Curl_printable_address() stores a printable version of the 1st address
170
 * given in the 'ai' argument. The result will be stored in the buf that is
171
 * bufsize bytes big.
172
 *
173
 * If the conversion fails, the target buffer is empty.
174
 */
175
void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
176
                            size_t bufsize)
177
1.23k
{
178
1.23k
  DEBUGASSERT(bufsize);
179
1.23k
  buf[0] = 0;
180
181
1.23k
  switch(ai->ai_family) {
182
1.23k
  case AF_INET: {
183
1.23k
    const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
184
1.23k
    const struct in_addr *ipaddr4 = &sa4->sin_addr;
185
1.23k
    (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
186
1.23k
    break;
187
0
  }
188
0
#ifdef USE_IPV6
189
0
  case AF_INET6: {
190
0
    const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
191
0
    const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
192
0
    (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
193
0
    break;
194
0
  }
195
0
#endif
196
0
  default:
197
0
    break;
198
1.23k
  }
199
1.23k
}
200
201
#ifdef USE_ALARM_TIMEOUT
202
/* Beware this is a global and unique instance. This is used to store the
203
   return address that we can jump back to from inside a signal handler. This
204
   is not thread-safe stuff. */
205
static sigjmp_buf curl_jmpenv;
206
static curl_simple_lock curl_jmpenv_lock = CURL_SIMPLE_LOCK_INIT;
207
#endif
208
209
#ifdef USE_IPV6
210
/* return a static IPv6 ::1 for the name */
211
static struct Curl_addrinfo *get_localhost6(uint16_t port, const char *name)
212
0
{
213
0
  struct Curl_addrinfo *ca;
214
0
  const size_t ss_size = sizeof(struct sockaddr_in6);
215
0
  const size_t hostlen = strlen(name);
216
0
  struct sockaddr_in6 sa6;
217
0
  unsigned char ipv6[16];
218
0
  unsigned short port16 = (unsigned short)(port & 0xffff);
219
0
  ca = curlx_calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
220
0
  if(!ca)
221
0
    return NULL;
222
223
0
  memset(&sa6, 0, sizeof(sa6));
224
0
  sa6.sin6_family = AF_INET6;
225
0
  sa6.sin6_port = htons(port16);
226
227
0
  (void)curlx_inet_pton(AF_INET6, "::1", ipv6);
228
0
  memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
229
230
0
  ca->ai_flags     = 0;
231
0
  ca->ai_family    = AF_INET6;
232
0
  ca->ai_socktype  = SOCK_STREAM;
233
0
  ca->ai_protocol  = IPPROTO_TCP;
234
0
  ca->ai_addrlen   = (curl_socklen_t)ss_size;
235
0
  ca->ai_next      = NULL;
236
0
  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
237
0
  memcpy(ca->ai_addr, &sa6, ss_size);
238
0
  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
239
0
  curlx_strcopy(ca->ai_canonname, hostlen + 1, name, hostlen);
240
0
  return ca;
241
0
}
242
#else
243
#define get_localhost6(x, y) NULL
244
#endif
245
246
/* return a static IPv4 127.0.0.1 for the given name */
247
static struct Curl_addrinfo *get_localhost(uint16_t port, const char *name)
248
0
{
249
0
  struct Curl_addrinfo *ca;
250
0
  struct Curl_addrinfo *ca6;
251
0
  const size_t ss_size = sizeof(struct sockaddr_in);
252
0
  const size_t hostlen = strlen(name);
253
0
  struct sockaddr_in sa;
254
0
  unsigned int ipv4;
255
0
  unsigned short port16 = (unsigned short)(port & 0xffff);
256
257
  /* memset to clear the sa.sin_zero field */
258
0
  memset(&sa, 0, sizeof(sa));
259
0
  sa.sin_family = AF_INET;
260
0
  sa.sin_port = htons(port16);
261
0
  if(curlx_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
262
0
    return NULL;
263
0
  memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
264
265
0
  ca = curlx_calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
266
0
  if(!ca)
267
0
    return NULL;
268
0
  ca->ai_flags     = 0;
269
0
  ca->ai_family    = AF_INET;
270
0
  ca->ai_socktype  = SOCK_STREAM;
271
0
  ca->ai_protocol  = IPPROTO_TCP;
272
0
  ca->ai_addrlen   = (curl_socklen_t)ss_size;
273
0
  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
274
0
  memcpy(ca->ai_addr, &sa, ss_size);
275
0
  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
276
0
  curlx_strcopy(ca->ai_canonname, hostlen + 1, name, hostlen);
277
278
0
  ca6 = get_localhost6(port, name);
279
0
  if(!ca6)
280
0
    return ca;
281
0
  ca6->ai_next = ca;
282
0
  return ca6;
283
0
}
284
285
#ifdef USE_IPV6
286
/* the nature of most systems is that IPv6 status does not come and go during a
287
   program's lifetime so we only probe the first time and then we have the
288
   info kept for fast reuse */
289
CURLcode Curl_probeipv6(struct Curl_multi *multi)
290
224k
{
291
  /* probe to see if we have a working IPv6 stack */
292
224k
  curl_socket_t s = CURL_SOCKET(PF_INET6, SOCK_DGRAM, 0);
293
224k
  multi->ipv6_works = FALSE;
294
224k
  if(s == CURL_SOCKET_BAD) {
295
0
    if(SOCKERRNO == SOCKENOMEM)
296
0
      return CURLE_OUT_OF_MEMORY;
297
0
  }
298
224k
  else {
299
224k
    multi->ipv6_works = TRUE;
300
224k
    sclose(s);
301
224k
  }
302
224k
  return CURLE_OK;
303
224k
}
304
305
/*
306
 * ipv6works() returns TRUE if IPv6 seems to work.
307
 */
308
static bool ipv6works(struct Curl_easy *data)
309
147k
{
310
147k
  DEBUGASSERT(data);
311
147k
  DEBUGASSERT(data->multi);
312
147k
  return data ? data->multi->ipv6_works : FALSE;
313
147k
}
314
#endif /* USE_IPV6 */
315
316
/*
317
 * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
318
 * (or IPv6 if supported) address.
319
 */
320
bool Curl_host_is_ipnum(const char *hostname)
321
300k
{
322
300k
  struct in_addr in;
323
300k
#ifdef USE_IPV6
324
300k
  struct in6_addr in6;
325
300k
#endif
326
300k
  if(curlx_inet_pton(AF_INET, hostname, &in) > 0
327
219k
#ifdef USE_IPV6
328
219k
     || curlx_inet_pton(AF_INET6, hostname, &in6) > 0
329
300k
#endif
330
300k
    )
331
92.8k
    return TRUE;
332
208k
  return FALSE;
333
300k
}
334
335
/* return TRUE if 'part' is a case insensitive tail of 'full' */
336
static bool tailmatch(const char *full, size_t flen,
337
                      const char *part, size_t plen)
338
680
{
339
680
  if(plen > flen)
340
549
    return FALSE;
341
131
  return curl_strnequal(part, &full[flen - plen], plen);
342
680
}
343
344
static CURLcode hostip_resolv_failed(struct Curl_easy *data,
345
                                     struct Curl_peer *peer,
346
                                     bool for_proxy)
347
3
{
348
3
  failf(data, "Could not resolve %s: %s",
349
3
        for_proxy ? "proxy" : "host", peer->hostname);
350
3
  return RESOLV_FAIL(for_proxy);
351
3
}
352
353
static bool can_resolve_dns_queries(struct Curl_easy *data,
354
                                    uint8_t dns_queries)
355
182
{
356
182
  (void)data;
357
182
  if((CURL_DNSQ_IS_ADDR(dns_queries) == CURL_DNSQ_AAAA) && !ipv6works(data))
358
0
    return FALSE;
359
182
  return TRUE;
360
182
}
361
362
CURLcode Curl_resolv_announce_start(struct Curl_easy *data,
363
                                    void *resolver)
364
133k
{
365
133k
  if(data->set.resolver_start) {
366
0
    struct Curl_mapi_guard guard;
367
0
    int rc;
368
369
0
    CURL_TRC_DNS(data, "announcing resolve to application");
370
0
    CURL_CBAPI_START(&guard, data, easy_resolver_start);
371
0
    rc = data->set.resolver_start(resolver, NULL,
372
0
                                  data->set.resolver_start_client);
373
0
    CURL_CBAPI_END(&guard);
374
0
    if(rc) {
375
0
      CURL_TRC_DNS(data, "application aborted resolve");
376
0
      return CURLE_ABORTED_BY_CALLBACK;
377
0
    }
378
0
  }
379
133k
  return CURLE_OK;
380
133k
}
381
382
#ifdef USE_CURL_ASYNC
383
384
static struct Curl_resolv_async *hostip_async_new(struct Curl_easy *data,
385
                                                  uint8_t dns_queries,
386
                                                  struct Curl_peer *peer,
387
                                                  uint8_t transport,
388
                                                  bool for_proxy,
389
                                                  timediff_t timeout_ms)
390
340
{
391
340
  struct Curl_resolv_async *async;
392
393
340
  if(!data->multi) {
394
0
    DEBUGASSERT(0);
395
0
    return NULL;
396
0
  }
397
398
  /* struct size already includes the NUL for hostname */
399
340
  async = curlx_calloc(1, sizeof(*async));
400
340
  if(!async)
401
0
    return NULL;
402
403
  /* Give every async resolve operation a "unique" id. This may
404
   * wrap around after a long time, making collisions highly unlikely.
405
   * As we keep the async structs at the easy handle, chances of
406
   * easy `mid plus resolv->id` colliding should be astronomical.
407
   * `resolv_id == 0` is never used. */
408
340
  if(data->multi->last_resolv_id == UINT32_MAX)
409
0
    data->multi->last_resolv_id = 1; /* wrap around */
410
340
  else
411
340
    data->multi->last_resolv_id++;
412
340
  async->id = data->multi->last_resolv_id;
413
340
  Curl_peer_link(&async->peer, peer);
414
340
  async->dns_queries = dns_queries;
415
340
  async->transport = transport;
416
340
  async->for_proxy = for_proxy;
417
340
  async->start = *Curl_pgrs_now(data);
418
340
  async->timeout_ms = timeout_ms;
419
340
  async->is_ipaddr = Curl_is_ipaddr(peer->hostname);
420
340
  if(async->is_ipaddr)
421
0
    async->is_ipv4addr = Curl_is_ipv4addr(peer->hostname);
422
423
340
  return async;
424
340
}
425
426
static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
427
                                          struct Curl_resolv_async *async,
428
                                          struct Curl_dns_entry **pdns)
429
771
{
430
771
  CURLcode result;
431
432
  /* If async resolving is ongoing, this must be set */
433
771
  if(!async)
434
0
    return CURLE_FAILED_INIT;
435
436
771
#ifndef CURL_DISABLE_DOH
437
771
  if(async->doh)
438
353
    result = Curl_doh_take_result(data, async, pdns);
439
418
  else
440
418
#endif
441
418
  result = Curl_async_take_result(data, async, pdns);
442
443
771
  if(result == CURLE_AGAIN) {
444
736
    CURL_TRC_DNS(data, "[%s] resolve incomplete, responses=%s, "
445
736
                 "ongoing=%d for %s:%d",
446
736
                 Curl_resolv_query_str(async->dns_queries),
447
736
                 Curl_resolv_query_str(async->dns_responses),
448
736
                 async->queries_ongoing,
449
736
                 async->peer->hostname, async->peer->port);
450
736
    result = CURLE_OK;
451
736
  }
452
35
  else if(IS_RESOLV_FAIL(result)) {
453
31
    result = Curl_async_failed(data, async, NULL);
454
31
  }
455
4
  else if(result) {
456
    /* a local failure, not a resolve answer. Keep the error as it
457
       is so it does not get treated as one. */
458
0
    CURL_TRC_DNS(data, "[%s] resolve error %d for %s:%u",
459
0
                 Curl_resolv_query_str(async->dns_queries),
460
0
                 (int)result, async->peer->hostname, async->peer->port);
461
0
  }
462
4
  else {
463
4
    CURL_TRC_DNS(data, "[%s] resolve complete for %s:%u",
464
4
                 Curl_resolv_query_str(async->dns_queries),
465
4
                 async->peer->hostname, async->peer->port);
466
4
    DEBUGASSERT(*pdns);
467
4
  }
468
469
771
  return result;
470
771
}
471
472
timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data,
473
                                  uint32_t resolv_id)
474
712
{
475
712
  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
476
712
  if(!async)
477
0
    return CURL_TIMEOUT_RESOLVE_MS;
478
712
  return curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
479
712
}
480
481
bool Curl_resolv_has_answers(struct Curl_easy *data,
482
                             uint32_t resolv_id, uint8_t dns_queries)
483
736
{
484
736
  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
485
736
  uint8_t check_queries;
486
  /* a no longer existing/running resolve has all answers. */
487
736
  if(!async || async->done)
488
0
    return TRUE;
489
  /* Relevant are only queries undertaken. Others are considered answered. */
490
736
  check_queries = (dns_queries & async->dns_queries);
491
736
  if((check_queries & async->dns_responses) != check_queries) {
492
712
    return FALSE;
493
712
  }
494
24
  return TRUE;
495
736
}
496
497
const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data,
498
                                               uint32_t resolv_id,
499
                                               int ai_family,
500
                                               unsigned int index)
501
52
{
502
52
  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
503
52
  if(!async || !CURL_DNSQ_IS_ADDR(async->dns_queries))
504
0
    return NULL;
505
52
  switch(ai_family) {
506
4
  case AF_INET:
507
4
    return Curl_addrinfo_get(async->ai_A, ai_family, index);
508
0
#ifdef USE_IPV6
509
4
  case AF_INET6:
510
4
    return Curl_addrinfo_get(async->ai_AAAA, ai_family, index);
511
0
#endif
512
44
  default:
513
44
    return NULL;
514
52
  }
515
52
}
516
517
#ifdef USE_HTTPSRR
518
519
CURLcode Curl_resolv_https(struct Curl_easy *data,
520
                           struct Curl_peer *peer,
521
                           bool for_proxy,
522
                           timediff_t timeout_ms,
523
                           uint32_t *presolv_id,
524
                           struct Curl_dns_entry **pdns)
525
{
526
  return Curl_resolv(data, peer, CURL_DNSQ_HTTPS, TRNSPRT_TCP,
527
                     for_proxy, timeout_ms, presolv_id, pdns);
528
}
529
530
const struct Curl_https_rrinfo *
531
Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id)
532
{
533
  struct Curl_resolv_async *async;
534
  if(!Curl_resolv_knows_https(data, resolv_id))
535
    return NULL;
536
  async = Curl_async_get(data, resolv_id);
537
  return async ? async->httpsrr : NULL;
538
}
539
540
bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id)
541
{
542
  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
543
  if(async && (async->dns_queries & CURL_DNSQ_HTTPS))
544
    return ((async->dns_responses & CURL_DNSQ_HTTPS) ||
545
            !async->queries_ongoing);
546
  return TRUE; /* we know it will never come */
547
}
548
549
#endif /* USE_HTTPSRR */
550
551
#endif /* USE_CURL_ASYNC */
552
553
/* Start resolving. `*pnegative` is only meaningful when this returns
554
   a CURLE_COULDNT_RESOLVE_* failure: TRUE when the resolver answered
555
   that the name does not exist, FALSE on transient or local failures
556
   that must not be cached as negative entries. */
557
static CURLcode hostip_resolv_start(struct Curl_easy *data,
558
                                    uint8_t dns_queries,
559
                                    struct Curl_peer *peer,
560
                                    uint8_t transport,
561
                                    bool for_proxy,
562
                                    timediff_t timeout_ms,
563
                                    bool allowDOH,
564
                                    uint32_t *presolv_id,
565
                                    struct Curl_dns_entry **pdns,
566
                                    bool *pnegative)
567
133k
{
568
133k
#ifdef USE_CURL_ASYNC
569
133k
  struct Curl_resolv_async *async = NULL;
570
133k
#endif
571
133k
  struct Curl_addrinfo *addr = NULL;
572
133k
  size_t hostname_len;
573
133k
  bool addr_queries = (dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA));
574
133k
  CURLcode result = CURLE_OK;
575
576
133k
  *pnegative = FALSE;
577
578
133k
  (void)timeout_ms; /* not in all ifdefs */
579
133k
  *presolv_id = 0;
580
133k
  *pdns = NULL;
581
582
  /* Check for "known" things to resolve ourselves. */
583
133k
  if(addr_queries) {
584
133k
#ifndef USE_RESOLVE_ON_IPS
585
133k
    if(Curl_is_ipaddr(peer->hostname)) {
586
      /* test655 verifies that the announce is done, even though there
587
       * is no real resolving. So, keep doing this. */
588
133k
      result = Curl_resolv_announce_start(data, NULL);
589
133k
      if(result)
590
0
        goto out;
591
      /* shortcut literal IP addresses, if we are not told to resolve them. */
592
133k
      result = Curl_str2addr(peer->hostname, peer->port, &addr);
593
133k
      goto out;
594
133k
    }
595
340
#endif
596
597
340
    hostname_len = strlen(peer->hostname);
598
340
    if(curl_strequal(peer->hostname, "localhost") ||
599
340
       curl_strequal(peer->hostname, "localhost.") ||
600
340
       tailmatch(peer->hostname, hostname_len, STRCONST(".localhost")) ||
601
340
       tailmatch(peer->hostname, hostname_len, STRCONST(".localhost."))) {
602
0
      result = Curl_resolv_announce_start(data, NULL);
603
0
      if(result)
604
0
        goto out;
605
0
      addr = get_localhost(peer->port, peer->hostname);
606
0
      if(!addr)
607
0
        result = CURLE_OUT_OF_MEMORY;
608
0
      goto out;
609
0
    }
610
340
  }
611
340
#ifndef CURL_DISABLE_DOH
612
340
  if(!Curl_is_ipaddr(peer->hostname) && allowDOH && data->set.doh) {
613
158
    result = Curl_resolv_announce_start(data, NULL);
614
158
    if(result)
615
0
      goto out;
616
158
    if(!async) {
617
158
      async = hostip_async_new(data, dns_queries, peer, transport,
618
158
                               for_proxy, timeout_ms);
619
158
      if(!async) {
620
0
        result = CURLE_OUT_OF_MEMORY;
621
0
        goto out;
622
0
      }
623
158
    }
624
158
    result = Curl_doh(data, async);
625
158
    goto out;
626
158
  }
627
#else
628
  (void)allowDOH;
629
#endif
630
631
  /* Can we provide the requested IP specifics in resolving? */
632
182
  if(!can_resolve_dns_queries(data, dns_queries)) {
633
0
    result = RESOLV_FAIL(for_proxy);
634
0
    goto out;
635
0
  }
636
637
182
#ifdef CURLRES_ASYNCH
638
182
  (void)addr;
639
182
  if(!async) {
640
182
    async = hostip_async_new(data, dns_queries, peer, transport,
641
182
                             for_proxy, timeout_ms);
642
182
    if(!async) {
643
0
      result = CURLE_OUT_OF_MEMORY;
644
0
      goto out;
645
0
    }
646
182
  }
647
182
  result = Curl_async_getaddrinfo(data, async);
648
182
  if(result == CURLE_AGAIN) {
649
    /* the answer might be there already. Check. */
650
0
    CURLcode r2 = hostip_resolv_take_result(data, async, pdns);
651
0
    if(r2)
652
0
      result = r2;
653
0
    else if(*pdns)
654
0
      result = CURLE_OK;
655
0
  }
656
#else
657
  result = Curl_resolv_announce_start(data, NULL);
658
  if(result)
659
    goto out;
660
  addr = Curl_sync_getaddrinfo(data, dns_queries, peer->hostname, peer->port,
661
                               transport);
662
  if(!addr) {
663
    result = RESOLV_FAIL(for_proxy);
664
    /* the synchronous resolvers do not tell a transient failure from
665
       an authoritative negative answer, treat it as before */
666
    *pnegative = TRUE;
667
  }
668
#endif
669
670
133k
out:
671
133k
  if(!result) {
672
133k
    if(addr) {
673
      /* we got a response, create a dns entry, add to cache, return */
674
133k
      DEBUGASSERT(!*pdns);
675
133k
      *pdns = Curl_dnsc_mk_addr(data, dns_queries, &addr, peer);
676
133k
      if(!*pdns)
677
0
        result = CURLE_OUT_OF_MEMORY;
678
133k
    }
679
335
    else if(!*pdns)
680
335
      result = CURLE_AGAIN;
681
133k
  }
682
5
  else if(*pdns)
683
0
    Curl_dns_entry_unlink(data, pdns);
684
5
  else if(addr)
685
0
    Curl_freeaddrinfo(addr);
686
687
133k
#ifdef USE_CURL_ASYNC
688
133k
  if(async) {
689
340
    if(result == CURLE_AGAIN) { /* still need it, link, return id. */
690
335
      *presolv_id = async->id;
691
335
      async->next = data->state.async;
692
335
      data->state.async = async;
693
335
    }
694
5
    else {
695
5
      *pnegative = !!async->negative_answer;
696
5
      Curl_async_destroy(data, async);
697
5
    }
698
340
  }
699
133k
#endif
700
133k
  return result;
701
133k
}
702
703
static CURLcode hostip_resolv(struct Curl_easy *data,
704
                              uint8_t dns_queries,
705
                              struct Curl_peer *peer,
706
                              uint8_t transport,
707
                              bool for_proxy,
708
                              timediff_t timeout_ms,
709
                              bool allowDOH,
710
                              uint32_t *presolv_id,
711
                              struct Curl_dns_entry **pdns)
712
145k
{
713
145k
  size_t hostname_len;
714
145k
  CURLcode result = RESOLV_FAIL(for_proxy);
715
145k
  bool cache_dns = FALSE;
716
145k
  bool negative = FALSE;
717
718
145k
  (void)timeout_ms; /* not used in all ifdefs */
719
145k
  *presolv_id = 0;
720
145k
  *pdns = NULL;
721
722
#ifdef CURL_DISABLE_DOH
723
  (void)allowDOH;
724
#endif
725
726
  /* We should intentionally error and not resolve .onion TLDs */
727
145k
  hostname_len = strlen(peer->hostname);
728
145k
  DEBUGASSERT(hostname_len);
729
145k
  if(hostname_len >= 7 &&
730
145k
     (curl_strequal(&peer->hostname[hostname_len - 6], ".onion") ||
731
145k
      curl_strequal(&peer->hostname[hostname_len - 7], ".onion."))) {
732
0
    failf(data, "Not resolving .onion address (RFC 7686)");
733
0
    goto out;
734
0
  }
735
736
145k
#ifdef DEBUGBUILD
737
145k
  CURL_TRC_DNS(data, "[%s] hostip_resolv(%s:%u)",
738
145k
               Curl_resolv_query_str(dns_queries), peer->hostname, peer->port);
739
145k
  if((CURL_DNSQ_IS_ADDR(dns_queries) == CURL_DNSQ_AAAA) &&
740
8
     getenv("CURL_DBG_RESOLV_FAIL_IPV6")) {
741
0
    infof(data, "DEBUG fail ipv6 resolve");
742
0
    result = hostip_resolv_failed(data, peer, for_proxy);
743
0
    goto out;
744
0
  }
745
145k
#endif
746
  /* Let's check our DNS cache first */
747
145k
  result = Curl_dnscache_get(data, dns_queries, peer, pdns);
748
145k
  if(*pdns) {
749
12.0k
    infof(data, "Hostname %s was found in DNS cache", peer->hostname);
750
12.0k
    result = CURLE_OK;
751
12.0k
  }
752
133k
  else if(result) {
753
3
    infof(data, "Negative DNS entry");
754
3
    result = hostip_resolv_failed(data, peer, for_proxy);
755
3
  }
756
133k
  else {
757
    /* No luck, we need to start resolving. */
758
133k
    cache_dns = TRUE;
759
133k
    result = hostip_resolv_start(data, dns_queries, peer, transport,
760
133k
                                 for_proxy, timeout_ms, allowDOH,
761
133k
                                 presolv_id, pdns, &negative);
762
133k
    CURL_TRC_DNS(data, "[%s] hostip_resolv started -> %d",
763
133k
                 Curl_resolv_query_str(dns_queries), (int)result);
764
133k
  }
765
766
145k
out:
767
145k
  if(result && (result != CURLE_AGAIN)) {
768
8
    Curl_dns_entry_unlink(data, pdns);
769
8
    if(IS_RESOLV_FAIL(result)) {
770
3
      if(cache_dns && negative)
771
0
        Curl_dnscache_add_negative(data, dns_queries, peer);
772
3
      if(dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA))
773
3
        failf(data, "Could not resolve: %s:%u", peer->hostname, peer->port);
774
3
    }
775
5
    else {
776
5
      failf(data, "Error %d resolving %s:%u",
777
5
            (int)result, peer->hostname, peer->port);
778
5
    }
779
8
  }
780
145k
  else if(cache_dns && *pdns) {
781
133k
    result = Curl_dnscache_add(data, *pdns);
782
133k
    if(result)
783
0
      Curl_dns_entry_unlink(data, pdns);
784
133k
  }
785
786
145k
  return result;
787
145k
}
788
789
CURLcode Curl_resolv_blocking(struct Curl_easy *data,
790
                              uint8_t dns_queries,
791
                              const char *hostname,
792
                              uint16_t port,
793
                              uint8_t transport,
794
                              struct Curl_dns_entry **pdns)
795
1.11k
{
796
1.11k
  struct Curl_peer *peer = NULL;
797
1.11k
  CURLcode result;
798
1.11k
  uint32_t resolv_id;
799
800
1.11k
  DEBUGASSERT(hostname && *hostname);
801
1.11k
  *pdns = NULL;
802
803
1.11k
  result = Curl_peer_create(data, data->conn->scheme, hostname, port, &peer);
804
1.11k
  if(result)
805
0
    goto out;
806
807
  /* We cannot do a blocking resolve using DoH currently */
808
1.11k
  result = hostip_resolv(data, dns_queries, peer, transport, FALSE, 0, FALSE,
809
1.11k
                         &resolv_id, pdns);
810
1.11k
  switch(result) {
811
1.11k
  case CURLE_OK:
812
1.11k
    DEBUGASSERT(*pdns);
813
1.11k
    break;
814
1.11k
#ifdef USE_CURL_ASYNC
815
1.11k
  case CURLE_AGAIN:
816
0
    DEBUGASSERT(!*pdns);
817
0
    result = Curl_async_await(data, resolv_id, pdns);
818
0
    Curl_resolv_destroy(data, resolv_id);
819
0
    break;
820
0
#endif
821
0
  default:
822
0
    break;
823
1.11k
  }
824
825
1.11k
out:
826
1.11k
  Curl_peer_unlink(&peer);
827
1.11k
  return result;
828
1.11k
}
829
830
#ifdef USE_ALARM_TIMEOUT
831
/*
832
 * This signal handler jumps back into the main libcurl code and continues
833
 * execution. This effectively causes the remainder of the application to run
834
 * within a signal handler which is nonportable and could lead to problems.
835
 */
836
CURL_NORETURN static void alarmfunc(int sig)
837
{
838
  (void)sig;
839
  siglongjmp(curl_jmpenv, 1);
840
}
841
842
static CURLcode resolv_alarm_timeout(struct Curl_easy *data,
843
                                     uint8_t dns_queries,
844
                                     struct Curl_peer *peer,
845
                                     uint8_t transport,
846
                                     bool for_proxy,
847
                                     timediff_t timeout_ms,
848
                                     uint32_t *presolv_id,
849
                                     struct Curl_dns_entry **entry)
850
{
851
#ifdef HAVE_SIGACTION
852
  struct sigaction keep_sigact; /* store the old struct here */
853
  volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
854
  struct sigaction sigact;
855
#else
856
#ifdef HAVE_SIGNAL
857
  void (*keep_sigact)(int);       /* store the old handler here */
858
#endif /* HAVE_SIGNAL */
859
#endif /* HAVE_SIGACTION */
860
  volatile long timeout;
861
  volatile unsigned int prev_alarm = 0;
862
  CURLcode result;
863
864
  DEBUGASSERT(peer->hostname && *peer->hostname);
865
  DEBUGASSERT(timeout_ms > 0);
866
  DEBUGASSERT(!data->set.no_signal);
867
#ifndef CURL_DISABLE_DOH
868
  DEBUGASSERT(!data->set.doh);
869
#endif
870
871
  *entry = NULL;
872
  timeout = (timeout_ms > LONG_MAX) ? LONG_MAX : (long)timeout_ms;
873
  if(timeout < 1000) {
874
    /* The alarm() function only provides integer second resolution, so if
875
       we want to wait less than one second we must bail out already now. */
876
    failf(data,
877
          "remaining timeout of %ld too small to resolve via SIGALRM method",
878
          timeout);
879
    return CURLE_OPERATION_TIMEDOUT;
880
  }
881
  /* This allows us to time-out from the name resolver, as the timeout
882
     will generate a signal and we will siglongjmp() from that here.
883
     This technique has problems (see alarmfunc).
884
     This should be the last thing we do before calling Curl_resolv(),
885
     as otherwise we would have to worry about variables that get modified
886
     before we invoke Curl_resolv() (and thus use "volatile"). */
887
  curl_simple_lock_lock(&curl_jmpenv_lock);
888
889
  if(sigsetjmp(curl_jmpenv, 1)) {
890
    /* this is coming from a siglongjmp() after an alarm signal */
891
    failf(data, "name lookup timed out");
892
    result = CURLE_OPERATION_TIMEDOUT;
893
    goto clean_up;
894
  }
895
  else {
896
    /*************************************************************
897
     * Set signal handler to catch SIGALRM
898
     * Store the old value to be able to set it back later!
899
     *************************************************************/
900
#ifdef HAVE_SIGACTION
901
    sigaction(SIGALRM, NULL, &sigact);
902
    keep_sigact = sigact;
903
    keep_copysig = TRUE; /* yes, we have a copy */
904
    sigact.sa_handler = alarmfunc;
905
#ifdef SA_RESTART
906
    /* HP-UX does not have SA_RESTART but defaults to that behavior! */
907
    sigact.sa_flags &= ~SA_RESTART;
908
#endif
909
    /* now set the new struct */
910
    sigaction(SIGALRM, &sigact, NULL);
911
#else /* HAVE_SIGACTION */
912
    /* no sigaction(), revert to the much lamer signal() */
913
#ifdef HAVE_SIGNAL
914
    keep_sigact = signal(SIGALRM, alarmfunc);
915
#endif
916
#endif /* HAVE_SIGACTION */
917
918
    /* alarm() makes a signal get sent when the timeout fires off, and that
919
       will abort system calls */
920
    prev_alarm = alarm(curlx_sltoui(timeout / 1000L));
921
  }
922
923
  /* Perform the actual name resolution. This might be interrupted by an
924
   * alarm if it takes too long. */
925
  result = hostip_resolv(data, dns_queries, peer, transport,
926
                         for_proxy, timeout_ms, FALSE, presolv_id, entry);
927
928
clean_up:
929
  if(!prev_alarm)
930
    /* deactivate a possibly active alarm before uninstalling the handler */
931
    alarm(0);
932
933
#ifdef HAVE_SIGACTION
934
  if(keep_copysig) {
935
    /* we got a struct as it looked before, now put that one back nice
936
       and clean */
937
    sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
938
  }
939
#else
940
#ifdef HAVE_SIGNAL
941
  /* restore the previous SIGALRM handler */
942
  signal(SIGALRM, keep_sigact);
943
#endif
944
#endif /* HAVE_SIGACTION */
945
946
  curl_simple_lock_unlock(&curl_jmpenv_lock);
947
948
  /* switch back the alarm() to either zero or to what it was before minus
949
     the time we spent until now! */
950
  if(prev_alarm) {
951
    /* there was an alarm() set before us, now put it back */
952
    timediff_t elapsed_secs = curlx_ptimediff_ms(Curl_pgrs_now(data),
953
                                                 &data->conn->created) / 1000;
954
955
    /* the alarm period is counted in even number of seconds */
956
    unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
957
958
    if(!alarm_set ||
959
       ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000))) {
960
      /* if the alarm time-left reached zero or turned "negative" (counted
961
         with unsigned values), we should fire off a SIGALRM here, but we
962
         will not, and zero would be to switch it off so we never set it to
963
         less than 1! */
964
      alarm(1);
965
      result = CURLE_OPERATION_TIMEDOUT;
966
      failf(data, "Previous alarm fired off");
967
    }
968
    else
969
      alarm((unsigned int)alarm_set);
970
  }
971
972
  return result;
973
}
974
975
#endif /* USE_ALARM_TIMEOUT */
976
977
#ifdef USE_UNIX_SOCKETS
978
static CURLcode resolv_unix(struct Curl_easy *data,
979
                            struct Curl_peer *peer,
980
                            struct Curl_dns_entry **pdns)
981
1.87k
{
982
1.87k
  struct Curl_addrinfo *addr;
983
1.87k
  CURLcode result;
984
985
1.87k
  DEBUGASSERT(peer->unix_socket);
986
1.87k
  *pdns = NULL;
987
988
1.87k
  result = Curl_unix2addr(peer->hostname, (bool)peer->abstract_uds, &addr);
989
1.87k
  if(result) {
990
192
    if(result == CURLE_TOO_LARGE) {
991
      /* Long paths are not supported for now */
992
192
      failf(data, "Unix socket path too long: '%s'", peer->hostname);
993
192
      result = CURLE_COULDNT_RESOLVE_HOST;
994
192
    }
995
192
    return result;
996
192
  }
997
998
1.67k
  *pdns = Curl_dnsc_mk_addr(data, 0, &addr, peer);
999
1.67k
  return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY;
1000
1.87k
}
1001
#endif /* USE_UNIX_SOCKETS */
1002
1003
/*
1004
 * Curl_resolv() is the main name resolve function within libcurl. It resolves
1005
 * a name and returns a pointer to the entry in the 'entry' argument. This
1006
 * function might return immediately if we are using asynch resolves. See the
1007
 * return codes.
1008
 *
1009
 * The cache entry we return will get its 'inuse' counter increased when this
1010
 * function is used. You MUST call Curl_dns_entry_unlink() later (when you are
1011
 * done using this struct) to decrease the reference counter again.
1012
 *
1013
 * If built with a synchronous resolver and use of signals is not
1014
 * disabled by the application, then a nonzero timeout will cause a
1015
 * timeout after the specified number of milliseconds. Otherwise, timeout
1016
 * is ignored.
1017
 *
1018
 * Return codes:
1019
 * CURLE_OK = success, *pdns set to non-NULL
1020
 * CURLE_AGAIN = resolving in progress, *pdns == NULL
1021
 * any other CURLcode error, *pdns == NULL
1022
 */
1023
CURLcode Curl_resolv(struct Curl_easy *data,
1024
                     struct Curl_peer *peer,
1025
                     uint8_t dns_queries,
1026
                     uint8_t transport,
1027
                     bool for_proxy,
1028
                     timediff_t timeout_ms,
1029
                     uint32_t *presolv_id,
1030
                     struct Curl_dns_entry **pdns)
1031
146k
{
1032
146k
  *presolv_id = 0;
1033
146k
  *pdns = NULL;
1034
1035
146k
  if(timeout_ms < 0)
1036
    /* got an already expired timeout */
1037
0
    return CURLE_OPERATION_TIMEDOUT;
1038
146k
  else if(!timeout_ms)
1039
13
    timeout_ms = CURL_TIMEOUT_RESOLVE_MS;
1040
1041
146k
#ifdef USE_UNIX_SOCKETS
1042
146k
  if((dns_queries & CURL_DNSQ_ADDR) && peer->unix_socket)
1043
1.87k
    return resolv_unix(data, peer, pdns);
1044
#else
1045
  if(peer->unix_socket)
1046
    return hostip_resolv_failed(data, peer, for_proxy);
1047
#endif
1048
1049
#ifdef USE_ALARM_TIMEOUT
1050
  if(dns_queries & CURL_DNSQ_ADDR) {
1051
    if(timeout_ms && data->set.no_signal) {
1052
      /* Cannot use ALARM when signals are disabled */
1053
      timeout_ms = 0;
1054
    }
1055
    if(timeout_ms && !Curl_doh_wanted(data)) {
1056
      return resolv_alarm_timeout(data, dns_queries, peer, transport,
1057
                                  for_proxy, timeout_ms, presolv_id, pdns);
1058
    }
1059
  }
1060
#endif /* !USE_ALARM_TIMEOUT */
1061
1062
#ifndef CURLRES_ASYNCH
1063
  if(timeout_ms)
1064
    infof(data, "timeout on name lookup is not supported");
1065
#endif
1066
1067
144k
  return hostip_resolv(data, dns_queries, peer, transport,
1068
144k
                       for_proxy, timeout_ms, TRUE, presolv_id, pdns);
1069
146k
}
1070
1071
#ifdef USE_CURL_ASYNC
1072
1073
struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data,
1074
                                         uint32_t resolv_id)
1075
2.36k
{
1076
2.36k
  struct Curl_resolv_async *async = data->state.async;
1077
2.36k
  for(; async; async = async->next) {
1078
2.36k
    if(async->id == resolv_id)
1079
2.36k
      return async;
1080
2.36k
  }
1081
0
  return NULL;
1082
2.36k
}
1083
1084
CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id,
1085
                                 struct Curl_dns_entry **pdns)
1086
771
{
1087
771
  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
1088
771
  CURLcode result;
1089
1090
  /* If async resolving is ongoing, this must be set */
1091
771
  if(!async)
1092
0
    return CURLE_FAILED_INIT;
1093
1094
  /* check if we have the name resolved by now (from someone else) */
1095
771
  result = Curl_dnscache_get(data, async->dns_queries, async->peer, pdns);
1096
771
  if(*pdns) {
1097
    /* Tell a possibly async resolver we no longer need the results. */
1098
0
    infof(data, "Hostname '%s' was found in DNS cache", async->peer->hostname);
1099
0
    Curl_async_shutdown(data, async);
1100
0
    return CURLE_OK;
1101
0
  }
1102
771
  else if(result) {
1103
0
    Curl_async_shutdown(data, async);
1104
0
    return Curl_async_failed(data, async, NULL);
1105
0
  }
1106
1107
771
  result = hostip_resolv_take_result(data, async, pdns);
1108
1109
771
  if(*pdns) {
1110
    /* Add to cache */
1111
4
    result = Curl_dnscache_add(data, *pdns);
1112
4
    if(result)
1113
0
      Curl_dns_entry_unlink(data, pdns);
1114
4
  }
1115
767
  else if(IS_RESOLV_FAIL(result)) {
1116
    /* Only cache the failure when the resolver answered that the
1117
       name does not exist. Transient failures, e.g. an unreachable
1118
       or overloaded DNS server or local resource shortages, say
1119
       nothing about the name and would poison the cache for every
1120
       transfer using it. */
1121
31
    if(async->negative_answer)
1122
3
      Curl_dnscache_add_negative(data, async->dns_queries, async->peer);
1123
31
    if(async->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA))
1124
31
      failf(data, "Could not resolve: %s:%u",
1125
31
            async->peer->hostname, async->peer->port);
1126
31
  }
1127
736
  else if(result) {
1128
0
    failf(data, "Error %d resolving %s:%u",
1129
0
          (int)result, async->peer->hostname, async->peer->port);
1130
0
  }
1131
771
  return result;
1132
771
}
1133
1134
CURLcode Curl_resolv_pollset(struct Curl_easy *data,
1135
                             struct easy_pollset *ps)
1136
9.47k
{
1137
9.47k
  struct Curl_resolv_async *async = data->state.async;
1138
9.47k
  CURLcode result = CURLE_OK;
1139
1140
9.47k
  (void)ps;
1141
10.2k
  for(; async && !result; async = async->next) {
1142
736
#ifndef CURL_DISABLE_DOH
1143
736
    if(async->doh) /* DoH has nothing for the pollset */
1144
325
      continue;
1145
411
#endif
1146
411
    result = Curl_async_pollset(data, async, ps);
1147
411
  }
1148
9.47k
  return result;
1149
9.47k
}
1150
1151
void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id)
1152
100k
{
1153
100k
  struct Curl_resolv_async **panchor = &data->state.async;
1154
1155
100k
  for(; *panchor; panchor = &(*panchor)->next) {
1156
0
    struct Curl_resolv_async *async = *panchor;
1157
0
    if(async->id == resolv_id) {
1158
0
      *panchor = async->next;
1159
0
      Curl_async_destroy(data, async);
1160
0
      break;
1161
0
    }
1162
0
  }
1163
100k
}
1164
1165
void Curl_resolv_shutdown_all(struct Curl_easy *data)
1166
192k
{
1167
192k
  struct Curl_resolv_async *async = data->state.async;
1168
193k
  for(; async; async = async->next) {
1169
335
    Curl_async_shutdown(data, async);
1170
335
  }
1171
192k
}
1172
1173
void Curl_resolv_destroy_all(struct Curl_easy *data)
1174
711k
{
1175
711k
  while(data->state.async) {
1176
335
    struct Curl_resolv_async *async = data->state.async;
1177
335
    data->state.async = async->next;
1178
335
    Curl_async_destroy(data, async);
1179
335
  }
1180
711k
}
1181
1182
#endif /* USE_CURL_ASYNC */