Coverage Report

Created: 2025-07-18 07:19

/src/curl/lib/hostip.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
27
#ifdef HAVE_NETINET_IN_H
28
#include <netinet/in.h>
29
#endif
30
#ifdef HAVE_NETINET_IN6_H
31
#include <netinet/in6.h>
32
#endif
33
#ifdef HAVE_NETDB_H
34
#include <netdb.h>
35
#endif
36
#ifdef HAVE_ARPA_INET_H
37
#include <arpa/inet.h>
38
#endif
39
#ifdef __VMS
40
#include <in.h>
41
#include <inet.h>
42
#endif
43
44
#include <setjmp.h>
45
#ifndef UNDER_CE
46
#include <signal.h>
47
#endif
48
49
#include "urldata.h"
50
#include "sendf.h"
51
#include "connect.h"
52
#include "hostip.h"
53
#include "hash.h"
54
#include "rand.h"
55
#include "share.h"
56
#include "url.h"
57
#include "curlx/inet_ntop.h"
58
#include "curlx/inet_pton.h"
59
#include "multiif.h"
60
#include "doh.h"
61
#include "curlx/warnless.h"
62
#include "select.h"
63
#include "strcase.h"
64
#include "easy_lock.h"
65
#include "curlx/strparse.h"
66
67
/* The last 3 #include files should be in this order */
68
#include "curl_printf.h"
69
#include "curl_memory.h"
70
#include "memdebug.h"
71
72
#if defined(CURLRES_SYNCH) &&                   \
73
  defined(HAVE_ALARM) &&                        \
74
  defined(SIGALRM) &&                           \
75
  defined(HAVE_SIGSETJMP) &&                    \
76
  defined(GLOBAL_INIT_IS_THREADSAFE)
77
/* alarm-based timeouts can only be used with all the dependencies satisfied */
78
#define USE_ALARM_TIMEOUT
79
#endif
80
81
#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
82
83
728
#define MAX_DNS_CACHE_SIZE 29999
84
85
/*
86
 * hostip.c explained
87
 * ==================
88
 *
89
 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
90
 * source file are these:
91
 *
92
 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
93
 * that. The host may not be able to resolve IPv6, but we do not really have to
94
 * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4
95
 * defined.
96
 *
97
 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
98
 * asynchronous name resolves. This can be Windows or *nix.
99
 *
100
 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
101
 * Windows, and then the name resolve will be done in a new thread, and the
102
 * supported API will be the same as for ares-builds.
103
 *
104
 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
105
 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
106
 * defined.
107
 *
108
 * The host*.c sources files are split up like this:
109
 *
110
 * hostip.c   - method-independent resolver functions and utility functions
111
 * hostip4.c  - IPv4 specific functions
112
 * hostip6.c  - IPv6 specific functions
113
 * asyn.h     - common functions for all async resolvers
114
 * The two asynchronous name resolver backends are implemented in:
115
 * asyn-ares.c - async resolver using c-ares
116
 * asyn-thread.c - async resolver using POSIX threads
117
 *
118
 * The hostip.h is the united header file for all this. It defines the
119
 * CURLRES_* defines based on the config*.h and curl_setup.h defines.
120
 */
121
122
static void dnscache_entry_free(struct Curl_dns_entry *dns);
123
124
#ifndef CURL_DISABLE_VERBOSE_STRINGS
125
static void show_resolve_info(struct Curl_easy *data,
126
                              struct Curl_dns_entry *dns);
127
#else
128
#define show_resolve_info(x,y) Curl_nop_stmt
129
#endif
130
131
/*
132
 * Curl_printable_address() stores a printable version of the 1st address
133
 * given in the 'ai' argument. The result will be stored in the buf that is
134
 * bufsize bytes big.
135
 *
136
 * If the conversion fails, the target buffer is empty.
137
 */
138
void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
139
                            size_t bufsize)
140
859
{
141
859
  DEBUGASSERT(bufsize);
142
859
  buf[0] = 0;
143
144
859
  switch(ai->ai_family) {
145
560
  case AF_INET: {
146
560
    const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
147
560
    const struct in_addr *ipaddr4 = &sa4->sin_addr;
148
560
    (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
149
560
    break;
150
0
  }
151
0
#ifdef USE_IPV6
152
299
  case AF_INET6: {
153
299
    const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
154
299
    const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
155
299
    (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
156
299
    break;
157
0
  }
158
0
#endif
159
0
  default:
160
0
    break;
161
859
  }
162
859
}
163
164
/*
165
 * Create a hostcache id string for the provided host + port, to be used by
166
 * the DNS caching. Without alloc. Return length of the id string.
167
 */
168
static size_t
169
create_dnscache_id(const char *name,
170
                   size_t nlen, /* 0 or actual name length */
171
                   int port, char *ptr, size_t buflen)
172
2.83M
{
173
2.83M
  size_t len = nlen ? nlen : strlen(name);
174
2.83M
  DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
175
2.83M
  if(len > (buflen - 7))
176
182
    len = buflen - 7;
177
  /* store and lower case the name */
178
2.83M
  Curl_strntolower(ptr, name, len);
179
2.83M
  return msnprintf(&ptr[len], 7, ":%u", port) + len;
180
2.83M
}
181
182
struct dnscache_prune_data {
183
  time_t now;
184
  time_t oldest; /* oldest time in cache not pruned. */
185
  int max_age_sec;
186
};
187
188
/*
189
 * This function is set as a callback to be called for every entry in the DNS
190
 * cache when we want to prune old unused entries.
191
 *
192
 * Returning non-zero means remove the entry, return 0 to keep it in the
193
 * cache.
194
 */
195
static int
196
dnscache_entry_is_stale(void *datap, void *hc)
197
122k
{
198
122k
  struct dnscache_prune_data *prune =
199
122k
    (struct dnscache_prune_data *) datap;
200
122k
  struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
201
202
122k
  if(dns->timestamp) {
203
    /* age in seconds */
204
122k
    time_t age = prune->now - dns->timestamp;
205
122k
    if(age >= (time_t)prune->max_age_sec)
206
3.33k
      return TRUE;
207
118k
    if(age > prune->oldest)
208
808
      prune->oldest = age;
209
118k
  }
210
118k
  return FALSE;
211
122k
}
212
213
/*
214
 * Prune the DNS cache. This assumes that a lock has already been taken.
215
 * Returns the 'age' of the oldest still kept entry.
216
 */
217
static time_t
218
dnscache_prune(struct Curl_hash *hostcache, int cache_timeout,
219
               time_t now)
220
111k
{
221
111k
  struct dnscache_prune_data user;
222
223
111k
  user.max_age_sec = cache_timeout;
224
111k
  user.now = now;
225
111k
  user.oldest = 0;
226
227
111k
  Curl_hash_clean_with_criterium(hostcache,
228
111k
                                 (void *) &user,
229
111k
                                 dnscache_entry_is_stale);
230
231
111k
  return user.oldest;
232
111k
}
233
234
static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
235
3.05M
{
236
3.05M
  if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS))
237
0
    return &data->share->dnscache;
238
3.05M
  if(data->multi)
239
3.05M
    return &data->multi->dnscache;
240
0
  return NULL;
241
3.05M
}
242
243
static void dnscache_lock(struct Curl_easy *data,
244
                          struct Curl_dnscache *dnscache)
245
3.05M
{
246
3.05M
  if(data->share && dnscache == &data->share->dnscache)
247
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
248
3.05M
}
249
250
static void dnscache_unlock(struct Curl_easy *data,
251
                            struct Curl_dnscache *dnscache)
252
3.05M
{
253
3.05M
  if(data->share && dnscache == &data->share->dnscache)
254
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
255
3.05M
}
256
257
/*
258
 * Library-wide function for pruning the DNS cache. This function takes and
259
 * returns the appropriate locks.
260
 */
261
void Curl_dnscache_prune(struct Curl_easy *data)
262
111k
{
263
111k
  struct Curl_dnscache *dnscache = dnscache_get(data);
264
111k
  time_t now;
265
  /* the timeout may be set -1 (forever) */
266
111k
  int timeout = data->set.dns_cache_timeout;
267
268
111k
  if(!dnscache)
269
    /* NULL hostcache means we cannot do it */
270
0
    return;
271
272
111k
  dnscache_lock(data, dnscache);
273
274
111k
  now = time(NULL);
275
276
111k
  do {
277
    /* Remove outdated and unused entries from the hostcache */
278
111k
    time_t oldest = dnscache_prune(&dnscache->entries, timeout, now);
279
280
111k
    if(oldest < INT_MAX)
281
111k
      timeout = (int)oldest; /* we know it fits */
282
0
    else
283
0
      timeout = INT_MAX - 1;
284
285
    /* if the cache size is still too big, use the oldest age as new
286
       prune limit */
287
111k
  } while(timeout &&
288
111k
          (Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE));
289
290
111k
  dnscache_unlock(data, dnscache);
291
111k
}
292
293
#ifdef USE_ALARM_TIMEOUT
294
/* Beware this is a global and unique instance. This is used to store the
295
   return address that we can jump back to from inside a signal handler. This
296
   is not thread-safe stuff. */
297
static sigjmp_buf curl_jmpenv;
298
static curl_simple_lock curl_jmpenv_lock;
299
#endif
300
301
/* lookup address, returns entry if found and not stale */
302
static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
303
                                         struct Curl_dnscache *dnscache,
304
                                         const char *hostname,
305
                                         int port,
306
                                         int ip_version)
307
2.73M
{
308
2.73M
  struct Curl_dns_entry *dns = NULL;
309
2.73M
  char entry_id[MAX_HOSTCACHE_LEN];
310
2.73M
  size_t entry_len;
311
312
2.73M
  if(!dnscache)
313
0
    return NULL;
314
315
  /* Create an entry id, based upon the hostname and port */
316
2.73M
  entry_len = create_dnscache_id(hostname, 0, port,
317
2.73M
                                 entry_id, sizeof(entry_id));
318
319
  /* See if it is already in our dns cache */
320
2.73M
  dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
321
322
  /* No entry found in cache, check if we might have a wildcard entry */
323
2.73M
  if(!dns && data->state.wildcard_resolve) {
324
0
    entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id));
325
326
    /* See if it is already in our dns cache */
327
0
    dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
328
0
  }
329
330
2.73M
  if(dns && (data->set.dns_cache_timeout != -1)) {
331
    /* See whether the returned entry is stale. Done before we release lock */
332
16.2k
    struct dnscache_prune_data user;
333
334
16.2k
    user.now = time(NULL);
335
16.2k
    user.max_age_sec = data->set.dns_cache_timeout;
336
16.2k
    user.oldest = 0;
337
338
16.2k
    if(dnscache_entry_is_stale(&user, dns)) {
339
3.17k
      infof(data, "Hostname in DNS cache was stale, zapped");
340
3.17k
      dns = NULL; /* the memory deallocation is being handled by the hash */
341
3.17k
      Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
342
3.17k
    }
343
16.2k
  }
344
345
  /* See if the returned entry matches the required resolve mode */
346
2.73M
  if(dns && ip_version != CURL_IPRESOLVE_WHATEVER) {
347
405
    int pf = PF_INET;
348
405
    bool found = FALSE;
349
405
    struct Curl_addrinfo *addr = dns->addr;
350
351
405
#ifdef PF_INET6
352
405
    if(ip_version == CURL_IPRESOLVE_V6)
353
2
      pf = PF_INET6;
354
405
#endif
355
356
541
    while(addr) {
357
487
      if(addr->ai_family == pf) {
358
351
        found = TRUE;
359
351
        break;
360
351
      }
361
136
      addr = addr->ai_next;
362
136
    }
363
364
405
    if(!found) {
365
54
      infof(data, "Hostname in DNS cache does not have needed family, zapped");
366
54
      dns = NULL; /* the memory deallocation is being handled by the hash */
367
54
      Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
368
54
    }
369
405
  }
370
2.73M
  return dns;
371
2.73M
}
372
373
/*
374
 * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
375
 *
376
 * Curl_resolv() checks initially and multi_runsingle() checks each time
377
 * it discovers the handle in the state WAITRESOLVE whether the hostname
378
 * has already been resolved and the address has already been stored in
379
 * the DNS cache. This short circuits waiting for a lot of pending
380
 * lookups for the same hostname requested by different handles.
381
 *
382
 * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
383
 *
384
 * The returned data *MUST* be "released" with Curl_resolv_unlink() after
385
 * use, or we will leak memory!
386
 */
387
struct Curl_dns_entry *
388
Curl_dnscache_get(struct Curl_easy *data,
389
                  const char *hostname,
390
                  int port,
391
                  int ip_version)
392
2.61M
{
393
2.61M
  struct Curl_dnscache *dnscache = dnscache_get(data);
394
2.61M
  struct Curl_dns_entry *dns = NULL;
395
396
2.61M
  dnscache_lock(data, dnscache);
397
398
2.61M
  dns = fetch_addr(data, dnscache, hostname, port, ip_version);
399
2.61M
  if(dns)
400
23
    dns->refcount++; /* we use it! */
401
402
2.61M
  dnscache_unlock(data, dnscache);
403
404
2.61M
  return dns;
405
2.61M
}
406
407
#ifndef CURL_DISABLE_SHUFFLE_DNS
408
/*
409
 * Return # of addresses in a Curl_addrinfo struct
410
 */
411
static int num_addresses(const struct Curl_addrinfo *addr)
412
1.71k
{
413
1.71k
  int i = 0;
414
3.56k
  while(addr) {
415
1.84k
    addr = addr->ai_next;
416
1.84k
    i++;
417
1.84k
  }
418
1.71k
  return i;
419
1.71k
}
420
421
UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
422
                                    struct Curl_addrinfo **addr);
423
/*
424
 * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
425
 * struct by re-linking its linked list.
426
 *
427
 * The addr argument should be the address of a pointer to the head node of a
428
 * `Curl_addrinfo` list and it will be modified to point to the new head after
429
 * shuffling.
430
 *
431
 * Not declared static only to make it easy to use in a unit test!
432
 *
433
 * @unittest: 1608
434
 */
435
UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
436
                                    struct Curl_addrinfo **addr)
437
1.71k
{
438
1.71k
  CURLcode result = CURLE_OK;
439
1.71k
  const int num_addrs = num_addresses(*addr);
440
441
1.71k
  if(num_addrs > 1) {
442
138
    struct Curl_addrinfo **nodes;
443
138
    infof(data, "Shuffling %i addresses", num_addrs);
444
445
138
    nodes = malloc(num_addrs*sizeof(*nodes));
446
138
    if(nodes) {
447
138
      int i;
448
138
      unsigned int *rnd;
449
138
      const size_t rnd_size = num_addrs * sizeof(*rnd);
450
451
      /* build a plain array of Curl_addrinfo pointers */
452
138
      nodes[0] = *addr;
453
276
      for(i = 1; i < num_addrs; i++) {
454
138
        nodes[i] = nodes[i-1]->ai_next;
455
138
      }
456
457
138
      rnd = malloc(rnd_size);
458
138
      if(rnd) {
459
        /* Fisher-Yates shuffle */
460
138
        if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
461
138
          struct Curl_addrinfo *swap_tmp;
462
276
          for(i = num_addrs - 1; i > 0; i--) {
463
138
            swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)];
464
138
            nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i];
465
138
            nodes[i] = swap_tmp;
466
138
          }
467
468
          /* relink list in the new order */
469
276
          for(i = 1; i < num_addrs; i++) {
470
138
            nodes[i-1]->ai_next = nodes[i];
471
138
          }
472
473
138
          nodes[num_addrs-1]->ai_next = NULL;
474
138
          *addr = nodes[0];
475
138
        }
476
138
        free(rnd);
477
138
      }
478
0
      else
479
0
        result = CURLE_OUT_OF_MEMORY;
480
138
      free(nodes);
481
138
    }
482
0
    else
483
0
      result = CURLE_OUT_OF_MEMORY;
484
138
  }
485
1.71k
  return result;
486
1.71k
}
487
#endif
488
489
struct Curl_dns_entry *
490
Curl_dnscache_mk_entry(struct Curl_easy *data,
491
                       struct Curl_addrinfo *addr,
492
                       const char *hostname,
493
                       size_t hostlen, /* length or zero */
494
                       int port,
495
                       bool permanent)
496
98.8k
{
497
98.8k
  struct Curl_dns_entry *dns;
498
499
98.8k
#ifndef CURL_DISABLE_SHUFFLE_DNS
500
  /* shuffle addresses if requested */
501
98.8k
  if(data->set.dns_shuffle_addresses) {
502
1.71k
    CURLcode result = Curl_shuffle_addr(data, &addr);
503
1.71k
    if(result) {
504
0
      Curl_freeaddrinfo(addr);
505
0
      return NULL;
506
0
    }
507
1.71k
  }
508
#else
509
  (void)data;
510
#endif
511
98.8k
  if(!hostlen)
512
98.8k
    hostlen = strlen(hostname);
513
514
  /* Create a new cache entry */
515
98.8k
  dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
516
98.8k
  if(!dns) {
517
0
    Curl_freeaddrinfo(addr);
518
0
    return NULL;
519
0
  }
520
521
98.8k
  dns->refcount = 1; /* the cache has the first reference */
522
98.8k
  dns->addr = addr; /* this is the address(es) */
523
98.8k
  if(permanent)
524
0
    dns->timestamp = 0; /* an entry that never goes stale */
525
98.8k
  else {
526
98.8k
    dns->timestamp = time(NULL);
527
98.8k
    if(dns->timestamp == 0)
528
0
      dns->timestamp = 1;
529
98.8k
  }
530
98.8k
  dns->hostport = port;
531
98.8k
  if(hostlen)
532
98.8k
    memcpy(dns->hostname, hostname, hostlen);
533
534
98.8k
  return dns;
535
98.8k
}
536
537
static struct Curl_dns_entry *
538
dnscache_add_addr(struct Curl_easy *data,
539
                  struct Curl_dnscache *dnscache,
540
                  struct Curl_addrinfo *addr,
541
                  const char *hostname,
542
                  size_t hlen, /* length or zero */
543
                  int port,
544
                  bool permanent)
545
0
{
546
0
  char entry_id[MAX_HOSTCACHE_LEN];
547
0
  size_t entry_len;
548
0
  struct Curl_dns_entry *dns;
549
0
  struct Curl_dns_entry *dns2;
550
551
0
  dns = Curl_dnscache_mk_entry(data, addr, hostname, hlen, port, permanent);
552
0
  if(!dns)
553
0
    return NULL;
554
555
  /* Create an entry id, based upon the hostname and port */
556
0
  entry_len = create_dnscache_id(hostname, hlen, port,
557
0
                                 entry_id, sizeof(entry_id));
558
559
  /* Store the resolved data in our DNS cache. */
560
0
  dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
561
0
                       (void *)dns);
562
0
  if(!dns2) {
563
0
    dnscache_entry_free(dns);
564
0
    return NULL;
565
0
  }
566
567
0
  dns = dns2;
568
0
  dns->refcount++;         /* mark entry as in-use */
569
0
  return dns;
570
0
}
571
572
CURLcode Curl_dnscache_add(struct Curl_easy *data,
573
                           struct Curl_dns_entry *entry)
574
98.8k
{
575
98.8k
  struct Curl_dnscache *dnscache = dnscache_get(data);
576
98.8k
  char id[MAX_HOSTCACHE_LEN];
577
98.8k
  size_t idlen;
578
579
98.8k
  if(!dnscache)
580
0
    return CURLE_FAILED_INIT;
581
  /* Create an entry id, based upon the hostname and port */
582
98.8k
  idlen = create_dnscache_id(entry->hostname, 0, entry->hostport,
583
98.8k
                             id, sizeof(id));
584
585
  /* Store the resolved data in our DNS cache and up ref count */
586
98.8k
  dnscache_lock(data, dnscache);
587
98.8k
  if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) {
588
0
    dnscache_unlock(data, dnscache);
589
0
    return CURLE_OUT_OF_MEMORY;
590
0
  }
591
98.8k
  entry->refcount++;
592
98.8k
  dnscache_unlock(data, dnscache);
593
98.8k
  return CURLE_OK;
594
98.8k
}
595
596
#ifdef USE_IPV6
597
/* return a static IPv6 ::1 for the name */
598
static struct Curl_addrinfo *get_localhost6(int port, const char *name)
599
853
{
600
853
  struct Curl_addrinfo *ca;
601
853
  const size_t ss_size = sizeof(struct sockaddr_in6);
602
853
  const size_t hostlen = strlen(name);
603
853
  struct sockaddr_in6 sa6;
604
853
  unsigned char ipv6[16];
605
853
  unsigned short port16 = (unsigned short)(port & 0xffff);
606
853
  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
607
853
  if(!ca)
608
0
    return NULL;
609
610
853
  sa6.sin6_family = AF_INET6;
611
853
  sa6.sin6_port = htons(port16);
612
853
  sa6.sin6_flowinfo = 0;
613
853
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
614
853
  sa6.sin6_scope_id = 0;
615
853
#endif
616
617
853
  (void)curlx_inet_pton(AF_INET6, "::1", ipv6);
618
853
  memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
619
620
853
  ca->ai_flags     = 0;
621
853
  ca->ai_family    = AF_INET6;
622
853
  ca->ai_socktype  = SOCK_STREAM;
623
853
  ca->ai_protocol  = IPPROTO_TCP;
624
853
  ca->ai_addrlen   = (curl_socklen_t)ss_size;
625
853
  ca->ai_next      = NULL;
626
853
  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
627
853
  memcpy(ca->ai_addr, &sa6, ss_size);
628
853
  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
629
853
  strcpy(ca->ai_canonname, name);
630
853
  return ca;
631
853
}
632
#else
633
#define get_localhost6(x,y) NULL
634
#endif
635
636
/* return a static IPv4 127.0.0.1 for the given name */
637
static struct Curl_addrinfo *get_localhost(int port, const char *name)
638
853
{
639
853
  struct Curl_addrinfo *ca;
640
853
  struct Curl_addrinfo *ca6;
641
853
  const size_t ss_size = sizeof(struct sockaddr_in);
642
853
  const size_t hostlen = strlen(name);
643
853
  struct sockaddr_in sa;
644
853
  unsigned int ipv4;
645
853
  unsigned short port16 = (unsigned short)(port & 0xffff);
646
647
  /* memset to clear the sa.sin_zero field */
648
853
  memset(&sa, 0, sizeof(sa));
649
853
  sa.sin_family = AF_INET;
650
853
  sa.sin_port = htons(port16);
651
853
  if(curlx_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
652
0
    return NULL;
653
853
  memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
654
655
853
  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
656
853
  if(!ca)
657
0
    return NULL;
658
853
  ca->ai_flags     = 0;
659
853
  ca->ai_family    = AF_INET;
660
853
  ca->ai_socktype  = SOCK_STREAM;
661
853
  ca->ai_protocol  = IPPROTO_TCP;
662
853
  ca->ai_addrlen   = (curl_socklen_t)ss_size;
663
853
  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
664
853
  memcpy(ca->ai_addr, &sa, ss_size);
665
853
  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
666
853
  strcpy(ca->ai_canonname, name);
667
668
853
  ca6 = get_localhost6(port, name);
669
853
  if(!ca6)
670
0
    return ca;
671
853
  ca6->ai_next = ca;
672
853
  return ca6;
673
853
}
674
675
#ifdef USE_IPV6
676
/*
677
 * Curl_ipv6works() returns TRUE if IPv6 seems to work.
678
 */
679
bool Curl_ipv6works(struct Curl_easy *data)
680
15.0k
{
681
15.0k
  if(data) {
682
    /* the nature of most system is that IPv6 status does not come and go
683
       during a program's lifetime so we only probe the first time and then we
684
       have the info kept for fast reuse */
685
9.95k
    DEBUGASSERT(data);
686
9.95k
    DEBUGASSERT(data->multi);
687
9.95k
    if(data->multi->ipv6_up == IPV6_UNKNOWN) {
688
5.14k
      bool works = Curl_ipv6works(NULL);
689
5.14k
      data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
690
5.14k
    }
691
9.95k
    return data->multi->ipv6_up == IPV6_WORKS;
692
9.95k
  }
693
5.14k
  else {
694
5.14k
    int ipv6_works = -1;
695
    /* probe to see if we have a working IPv6 stack */
696
5.14k
    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
697
5.14k
    if(s == CURL_SOCKET_BAD)
698
      /* an IPv6 address was requested but we cannot get/use one */
699
0
      ipv6_works = 0;
700
5.14k
    else {
701
5.14k
      ipv6_works = 1;
702
5.14k
      sclose(s);
703
5.14k
    }
704
5.14k
    return ipv6_works > 0;
705
5.14k
  }
706
15.0k
}
707
#endif /* USE_IPV6 */
708
709
/*
710
 * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
711
 * (or IPv6 if supported) address.
712
 */
713
bool Curl_host_is_ipnum(const char *hostname)
714
86.6k
{
715
86.6k
  struct in_addr in;
716
86.6k
#ifdef USE_IPV6
717
86.6k
  struct in6_addr in6;
718
86.6k
#endif
719
86.6k
  if(curlx_inet_pton(AF_INET, hostname, &in) > 0
720
86.6k
#ifdef USE_IPV6
721
86.6k
     || curlx_inet_pton(AF_INET6, hostname, &in6) > 0
722
86.6k
#endif
723
86.6k
    )
724
27.3k
    return TRUE;
725
59.2k
  return FALSE;
726
86.6k
}
727
728
729
/* return TRUE if 'part' is a case insensitive tail of 'full' */
730
static bool tailmatch(const char *full, size_t flen,
731
                      const char *part, size_t plen)
732
25.8k
{
733
25.8k
  if(plen > flen)
734
23.8k
    return FALSE;
735
1.99k
  return curl_strnequal(part, &full[flen - plen], plen);
736
25.8k
}
737
738
static struct Curl_addrinfo *
739
convert_ipaddr_direct(const char *hostname, int port, bool *is_ipaddr)
740
107k
{
741
107k
  struct in_addr in;
742
107k
  *is_ipaddr = FALSE;
743
  /* First check if this is an IPv4 address string */
744
107k
  if(curlx_inet_pton(AF_INET, hostname, &in) > 0) {
745
    /* This is a dotted IP address 123.123.123.123-style */
746
93.8k
    *is_ipaddr = TRUE;
747
#ifdef USE_RESOLVE_ON_IPS
748
    (void)port;
749
    return NULL;
750
#else
751
93.8k
    return Curl_ip2addr(AF_INET, &in, hostname, port);
752
93.8k
#endif
753
93.8k
  }
754
13.8k
#ifdef USE_IPV6
755
13.8k
  else {
756
13.8k
    struct in6_addr in6;
757
    /* check if this is an IPv6 address string */
758
13.8k
    if(curlx_inet_pton(AF_INET6, hostname, &in6) > 0) {
759
      /* This is an IPv6 address literal */
760
273
      *is_ipaddr = TRUE;
761
#ifdef USE_RESOLVE_ON_IPS
762
      return NULL;
763
#else
764
273
      return Curl_ip2addr(AF_INET6, &in6, hostname, port);
765
273
#endif
766
273
    }
767
13.8k
  }
768
13.6k
#endif /* USE_IPV6 */
769
13.6k
  return NULL;
770
107k
}
771
772
static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version)
773
8.60k
{
774
8.60k
#ifdef CURLRES_IPV6
775
8.60k
  if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
776
0
    return FALSE;
777
#elif defined(CURLRES_IPV4)
778
  (void)data;
779
  if(ip_version == CURL_IPRESOLVE_V6)
780
    return FALSE;
781
#else
782
#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined
783
#endif
784
8.60k
  return TRUE;
785
8.60k
}
786
787
/*
788
 * Curl_resolv() is the main name resolve function within libcurl. It resolves
789
 * a name and returns a pointer to the entry in the 'entry' argument (if one
790
 * is provided). This function might return immediately if we are using asynch
791
 * resolves. See the return codes.
792
 *
793
 * The cache entry we return will get its 'inuse' counter increased when this
794
 * function is used. You MUST call Curl_resolv_unlink() later (when you are
795
 * done using this struct) to decrease the reference counter again.
796
 *
797
 * Return codes:
798
 * CURLE_OK = success, *entry set to non-NULL
799
 * CURLE_AGAIN = resolving in progress, *entry == NULL
800
 * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
801
 * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
802
 */
803
CURLcode Curl_resolv(struct Curl_easy *data,
804
                     const char *hostname,
805
                     int port,
806
                     int ip_version,
807
                     bool allowDOH,
808
                     struct Curl_dns_entry **entry)
809
120k
{
810
120k
  struct Curl_dnscache *dnscache = dnscache_get(data);
811
120k
  struct Curl_dns_entry *dns = NULL;
812
120k
  struct Curl_addrinfo *addr = NULL;
813
120k
  int respwait = 0;
814
120k
  bool is_ipaddr;
815
120k
  size_t hostname_len;
816
817
120k
#ifndef CURL_DISABLE_DOH
818
120k
  data->conn->bits.doh = FALSE; /* default is not */
819
#else
820
  (void)allowDOH;
821
#endif
822
120k
  if(!dnscache)
823
0
    goto error;
824
825
  /* We should intentionally error and not resolve .onion TLDs */
826
120k
  hostname_len = strlen(hostname);
827
120k
  if(hostname_len >= 7 &&
828
120k
     (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
829
99.7k
      curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
830
0
    failf(data, "Not resolving .onion address (RFC 7686)");
831
0
    goto error;
832
0
  }
833
834
  /* Let's check our DNS cache first */
835
120k
  dnscache_lock(data, dnscache);
836
120k
  dns = fetch_addr(data, dnscache, hostname, port, ip_version);
837
120k
  if(dns)
838
12.9k
    dns->refcount++; /* we pass out the reference. */
839
120k
  dnscache_unlock(data, dnscache);
840
120k
  if(dns) {
841
12.9k
    infof(data, "Hostname %s was found in DNS cache", hostname);
842
12.9k
    goto out;
843
12.9k
  }
844
845
  /* No luck, we need to resolve hostname. Notify user callback. */
846
107k
  if(data->set.resolver_start) {
847
0
    void *resolver = NULL;
848
0
    int st;
849
0
#ifdef CURLRES_ASYNCH
850
0
    if(Curl_async_get_impl(data, &resolver))
851
0
      goto error;
852
0
#endif
853
0
    Curl_set_in_callback(data, TRUE);
854
0
    st = data->set.resolver_start(resolver, NULL,
855
0
                                  data->set.resolver_start_client);
856
0
    Curl_set_in_callback(data, FALSE);
857
0
    if(st)
858
0
      goto error;
859
0
  }
860
861
  /* shortcut literal IP addresses, if we are not told to resolve them. */
862
107k
  addr = convert_ipaddr_direct(hostname, port, &is_ipaddr);
863
107k
  if(addr)
864
94.1k
    goto out;
865
866
13.6k
#ifndef USE_RESOLVE_ON_IPS
867
  /* allowed to convert, hostname is IP address, then NULL means error */
868
13.6k
  if(is_ipaddr)
869
0
    goto error;
870
13.6k
#endif
871
872
  /* Really need a resolver for hostname. */
873
13.6k
  if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
874
0
    goto error;
875
876
13.6k
  if(!is_ipaddr &&
877
13.6k
     (curl_strequal(hostname, "localhost") ||
878
13.6k
      curl_strequal(hostname, "localhost.") ||
879
13.6k
      tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
880
13.6k
      tailmatch(hostname, hostname_len, STRCONST(".localhost.")))) {
881
853
    addr = get_localhost(port, hostname);
882
853
  }
883
12.7k
#ifndef CURL_DISABLE_DOH
884
12.7k
  else if(!is_ipaddr && allowDOH && data->set.doh) {
885
4.15k
    addr = Curl_doh(data, hostname, port, ip_version, &respwait);
886
4.15k
  }
887
8.60k
#endif
888
8.60k
  else {
889
    /* Can we provide the requested IP specifics in resolving? */
890
8.60k
    if(!can_resolve_ip_version(data, ip_version))
891
0
      goto error;
892
893
8.60k
#ifdef CURLRES_ASYNCH
894
8.60k
    addr = Curl_async_getaddrinfo(data, hostname, port, ip_version, &respwait);
895
#else
896
    respwait = 0; /* no async waiting here */
897
    addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version);
898
#endif
899
8.60k
  }
900
901
120k
out:
902
  /* We either have found a `dns` or looked up the `addr`
903
   * or `respwait` is set for an async operation.
904
   * Everything else is a failure to resolve. */
905
120k
  if(dns) {
906
12.9k
    *entry = dns;
907
12.9k
    return CURLE_OK;
908
12.9k
  }
909
107k
  else if(addr) {
910
    /* we got a response, create a dns entry, add to cache, return */
911
94.9k
    dns = Curl_dnscache_mk_entry(data, addr, hostname, 0, port, FALSE);
912
94.9k
    if(!dns)
913
0
      goto error;
914
94.9k
    if(Curl_dnscache_add(data, dns))
915
0
      goto error;
916
94.9k
    show_resolve_info(data, dns);
917
94.9k
    *entry = dns;
918
94.9k
    return CURLE_OK;
919
94.9k
  }
920
12.7k
  else if(respwait) {
921
12.6k
    if(!Curl_resolv_check(data, &dns)) {
922
12.6k
      *entry = dns;
923
12.6k
      return dns ? CURLE_OK : CURLE_AGAIN;
924
12.6k
    }
925
12.6k
  }
926
95
error:
927
95
  if(dns)
928
0
    Curl_resolv_unlink(data, &dns);
929
95
  *entry = NULL;
930
95
  Curl_async_shutdown(data);
931
95
  return CURLE_COULDNT_RESOLVE_HOST;
932
120k
}
933
934
CURLcode Curl_resolv_blocking(struct Curl_easy *data,
935
                              const char *hostname,
936
                              int port,
937
                              int ip_version,
938
                              struct Curl_dns_entry **dnsentry)
939
15.6k
{
940
15.6k
  CURLcode result;
941
942
15.6k
  *dnsentry = NULL;
943
15.6k
  result = Curl_resolv(data, hostname, port, ip_version, FALSE, dnsentry);
944
15.6k
  switch(result) {
945
10.7k
  case CURLE_OK:
946
10.7k
    DEBUGASSERT(*dnsentry);
947
10.7k
    return CURLE_OK;
948
4.87k
  case CURLE_AGAIN:
949
4.87k
    DEBUGASSERT(!*dnsentry);
950
4.87k
    result = Curl_async_await(data, dnsentry);
951
4.87k
    if(result || !*dnsentry) {
952
      /* close the connection, since we cannot return failure here without
953
         cleaning up this connection properly. */
954
1.21k
      connclose(data->conn, "async resolve failed");
955
1.21k
    }
956
4.87k
    return result;
957
0
  default:
958
0
    return result;
959
15.6k
  }
960
15.6k
}
961
962
#ifdef USE_ALARM_TIMEOUT
963
/*
964
 * This signal handler jumps back into the main libcurl code and continues
965
 * execution. This effectively causes the remainder of the application to run
966
 * within a signal handler which is nonportable and could lead to problems.
967
 */
968
CURL_NORETURN static
969
void alarmfunc(int sig)
970
{
971
  (void)sig;
972
  siglongjmp(curl_jmpenv, 1);
973
}
974
#endif /* USE_ALARM_TIMEOUT */
975
976
/*
977
 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
978
 * timeout. This function might return immediately if we are using asynch
979
 * resolves. See the return codes.
980
 *
981
 * The cache entry we return will get its 'inuse' counter increased when this
982
 * function is used. You MUST call Curl_resolv_unlink() later (when you are
983
 * done using this struct) to decrease the reference counter again.
984
 *
985
 * If built with a synchronous resolver and use of signals is not
986
 * disabled by the application, then a nonzero timeout will cause a
987
 * timeout after the specified number of milliseconds. Otherwise, timeout
988
 * is ignored.
989
 *
990
 * Return codes:
991
 * CURLE_OK = success, *entry set to non-NULL
992
 * CURLE_AGAIN = resolving in progress, *entry == NULL
993
 * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
994
 * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
995
 */
996
997
CURLcode Curl_resolv_timeout(struct Curl_easy *data,
998
                             const char *hostname,
999
                             int port,
1000
                             int ip_version,
1001
                             struct Curl_dns_entry **entry,
1002
                             timediff_t timeoutms)
1003
104k
{
1004
#ifdef USE_ALARM_TIMEOUT
1005
#ifdef HAVE_SIGACTION
1006
  struct sigaction keep_sigact;   /* store the old struct here */
1007
  volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
1008
  struct sigaction sigact;
1009
#else
1010
#ifdef HAVE_SIGNAL
1011
  void (*keep_sigact)(int);       /* store the old handler here */
1012
#endif /* HAVE_SIGNAL */
1013
#endif /* HAVE_SIGACTION */
1014
  volatile long timeout;
1015
  volatile unsigned int prev_alarm = 0;
1016
#endif /* USE_ALARM_TIMEOUT */
1017
104k
  CURLcode result;
1018
1019
104k
  *entry = NULL;
1020
1021
104k
  if(timeoutms < 0)
1022
    /* got an already expired timeout */
1023
2
    return CURLE_OPERATION_TIMEDOUT;
1024
1025
#ifdef USE_ALARM_TIMEOUT
1026
  if(data->set.no_signal)
1027
    /* Ignore the timeout when signals are disabled */
1028
    timeout = 0;
1029
  else
1030
    timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
1031
1032
  if(!timeout
1033
#ifndef CURL_DISABLE_DOH
1034
     || data->set.doh
1035
#endif
1036
    )
1037
    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested or resolve
1038
       done using DoH */
1039
    return Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
1040
1041
  if(timeout < 1000) {
1042
    /* The alarm() function only provides integer second resolution, so if
1043
       we want to wait less than one second we must bail out already now. */
1044
    failf(data,
1045
        "remaining timeout of %ld too small to resolve via SIGALRM method",
1046
        timeout);
1047
    return CURLE_OPERATION_TIMEDOUT;
1048
  }
1049
  /* This allows us to time-out from the name resolver, as the timeout
1050
     will generate a signal and we will siglongjmp() from that here.
1051
     This technique has problems (see alarmfunc).
1052
     This should be the last thing we do before calling Curl_resolv(),
1053
     as otherwise we would have to worry about variables that get modified
1054
     before we invoke Curl_resolv() (and thus use "volatile"). */
1055
  curl_simple_lock_lock(&curl_jmpenv_lock);
1056
1057
  if(sigsetjmp(curl_jmpenv, 1)) {
1058
    /* this is coming from a siglongjmp() after an alarm signal */
1059
    failf(data, "name lookup timed out");
1060
    result = CURLE_OPERATION_TIMEDOUT;
1061
    goto clean_up;
1062
  }
1063
  else {
1064
    /*************************************************************
1065
     * Set signal handler to catch SIGALRM
1066
     * Store the old value to be able to set it back later!
1067
     *************************************************************/
1068
#ifdef HAVE_SIGACTION
1069
    sigaction(SIGALRM, NULL, &sigact);
1070
    keep_sigact = sigact;
1071
    keep_copysig = TRUE; /* yes, we have a copy */
1072
    sigact.sa_handler = alarmfunc;
1073
#ifdef SA_RESTART
1074
    /* HP-UX does not have SA_RESTART but defaults to that behavior! */
1075
    sigact.sa_flags &= ~SA_RESTART;
1076
#endif
1077
    /* now set the new struct */
1078
    sigaction(SIGALRM, &sigact, NULL);
1079
#else /* HAVE_SIGACTION */
1080
    /* no sigaction(), revert to the much lamer signal() */
1081
#ifdef HAVE_SIGNAL
1082
    keep_sigact = signal(SIGALRM, alarmfunc);
1083
#endif
1084
#endif /* HAVE_SIGACTION */
1085
1086
    /* alarm() makes a signal get sent when the timeout fires off, and that
1087
       will abort system calls */
1088
    prev_alarm = alarm(curlx_sltoui(timeout/1000L));
1089
  }
1090
1091
#else /* USE_ALARM_TIMEOUT */
1092
#ifndef CURLRES_ASYNCH
1093
  if(timeoutms)
1094
    infof(data, "timeout on name lookup is not supported");
1095
#else
1096
104k
  (void)timeoutms; /* timeoutms not used with an async resolver */
1097
104k
#endif
1098
104k
#endif /* else USE_ALARM_TIMEOUT */
1099
1100
  /* Perform the actual name resolution. This might be interrupted by an
1101
   * alarm if it takes too long.
1102
   */
1103
104k
  result = Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
1104
1105
#ifdef USE_ALARM_TIMEOUT
1106
clean_up:
1107
1108
  if(!prev_alarm)
1109
    /* deactivate a possibly active alarm before uninstalling the handler */
1110
    alarm(0);
1111
1112
#ifdef HAVE_SIGACTION
1113
  if(keep_copysig) {
1114
    /* we got a struct as it looked before, now put that one back nice
1115
       and clean */
1116
    sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
1117
  }
1118
#else
1119
#ifdef HAVE_SIGNAL
1120
  /* restore the previous SIGALRM handler */
1121
  signal(SIGALRM, keep_sigact);
1122
#endif
1123
#endif /* HAVE_SIGACTION */
1124
1125
  curl_simple_lock_unlock(&curl_jmpenv_lock);
1126
1127
  /* switch back the alarm() to either zero or to what it was before minus
1128
     the time we spent until now! */
1129
  if(prev_alarm) {
1130
    /* there was an alarm() set before us, now put it back */
1131
    timediff_t elapsed_secs = curlx_timediff(curlx_now(),
1132
                                            data->conn->created) / 1000;
1133
1134
    /* the alarm period is counted in even number of seconds */
1135
    unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
1136
1137
    if(!alarm_set ||
1138
       ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
1139
      /* if the alarm time-left reached zero or turned "negative" (counted
1140
         with unsigned values), we should fire off a SIGALRM here, but we
1141
         will not, and zero would be to switch it off so we never set it to
1142
         less than 1! */
1143
      alarm(1);
1144
      result = CURLE_OPERATION_TIMEDOUT;
1145
      failf(data, "Previous alarm fired off");
1146
    }
1147
    else
1148
      alarm((unsigned int)alarm_set);
1149
  }
1150
#endif /* USE_ALARM_TIMEOUT */
1151
1152
104k
  return result;
1153
104k
}
1154
1155
static void dnscache_entry_free(struct Curl_dns_entry *dns)
1156
100k
{
1157
100k
  Curl_freeaddrinfo(dns->addr);
1158
#ifdef USE_HTTPSRR
1159
  if(dns->hinfo) {
1160
    Curl_httpsrr_cleanup(dns->hinfo);
1161
    free(dns->hinfo);
1162
  }
1163
#endif
1164
100k
  free(dns);
1165
100k
}
1166
1167
/*
1168
 * Curl_resolv_unlink() releases a reference to the given cached DNS entry.
1169
 * When the reference count reaches 0, the entry is destroyed. It is important
1170
 * that only one unlink is made for each Curl_resolv() call.
1171
 *
1172
 * May be called with 'data' == NULL for global cache.
1173
 */
1174
void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
1175
1.03M
{
1176
1.03M
  if(*pdns) {
1177
113k
    struct Curl_dnscache *dnscache = dnscache_get(data);
1178
113k
    struct Curl_dns_entry *dns = *pdns;
1179
113k
    *pdns = NULL;
1180
113k
    dnscache_lock(data, dnscache);
1181
113k
    dns->refcount--;
1182
113k
    if(dns->refcount == 0)
1183
1.28k
      dnscache_entry_free(dns);
1184
113k
    dnscache_unlock(data, dnscache);
1185
113k
  }
1186
1.03M
}
1187
1188
static void dnscache_entry_dtor(void *entry)
1189
98.8k
{
1190
98.8k
  struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry;
1191
98.8k
  DEBUGASSERT(dns && (dns->refcount > 0));
1192
98.8k
  dns->refcount--;
1193
98.8k
  if(dns->refcount == 0)
1194
98.8k
    dnscache_entry_free(dns);
1195
98.8k
}
1196
1197
/*
1198
 * Curl_dnscache_init() inits a new DNS cache.
1199
 */
1200
void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
1201
150k
{
1202
150k
  Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare,
1203
150k
                 dnscache_entry_dtor);
1204
150k
}
1205
1206
void Curl_dnscache_destroy(struct Curl_dnscache *dns)
1207
150k
{
1208
150k
  Curl_hash_destroy(&dns->entries);
1209
150k
}
1210
1211
CURLcode Curl_loadhostpairs(struct Curl_easy *data)
1212
0
{
1213
0
  struct Curl_dnscache *dnscache = dnscache_get(data);
1214
0
  struct curl_slist *hostp;
1215
1216
0
  if(!dnscache)
1217
0
    return CURLE_FAILED_INIT;
1218
1219
  /* Default is no wildcard found */
1220
0
  data->state.wildcard_resolve = FALSE;
1221
1222
0
  for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
1223
0
    char entry_id[MAX_HOSTCACHE_LEN];
1224
0
    const char *host = hostp->data;
1225
0
    struct Curl_str source;
1226
0
    if(!host)
1227
0
      continue;
1228
0
    if(*host == '-') {
1229
0
      curl_off_t num = 0;
1230
0
      size_t entry_len;
1231
0
      host++;
1232
0
      if(!curlx_str_single(&host, '[')) {
1233
0
        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
1234
0
           curlx_str_single(&host, ']') ||
1235
0
           curlx_str_single(&host, ':'))
1236
0
          continue;
1237
0
      }
1238
0
      else {
1239
0
        if(curlx_str_until(&host, &source, 4096, ':') ||
1240
0
           curlx_str_single(&host, ':')) {
1241
0
          continue;
1242
0
        }
1243
0
      }
1244
1245
0
      if(!curlx_str_number(&host, &num, 0xffff)) {
1246
        /* Create an entry id, based upon the hostname and port */
1247
0
        entry_len = create_dnscache_id(curlx_str(&source),
1248
0
                                       curlx_strlen(&source), (int)num,
1249
0
                                       entry_id, sizeof(entry_id));
1250
0
        dnscache_lock(data, dnscache);
1251
        /* delete entry, ignore if it did not exist */
1252
0
        Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
1253
0
        dnscache_unlock(data, dnscache);
1254
0
      }
1255
0
    }
1256
0
    else {
1257
0
      struct Curl_dns_entry *dns;
1258
0
      struct Curl_addrinfo *head = NULL, *tail = NULL;
1259
0
      size_t entry_len;
1260
0
      char address[64];
1261
0
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1262
0
      const char *addresses = NULL;
1263
0
#endif
1264
0
      curl_off_t port = 0;
1265
0
      bool permanent = TRUE;
1266
0
      bool error = TRUE;
1267
1268
0
      if(*host == '+') {
1269
0
        host++;
1270
0
        permanent = FALSE;
1271
0
      }
1272
0
      if(!curlx_str_single(&host, '[')) {
1273
0
        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
1274
0
           curlx_str_single(&host, ']'))
1275
0
          continue;
1276
0
      }
1277
0
      else {
1278
0
        if(curlx_str_until(&host, &source, 4096, ':'))
1279
0
          continue;
1280
0
      }
1281
0
      if(curlx_str_single(&host, ':') ||
1282
0
         curlx_str_number(&host, &port, 0xffff) ||
1283
0
         curlx_str_single(&host, ':'))
1284
0
        goto err;
1285
1286
0
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1287
0
      addresses = host;
1288
0
#endif
1289
1290
      /* start the address section */
1291
0
      while(*host) {
1292
0
        struct Curl_str target;
1293
0
        struct Curl_addrinfo *ai;
1294
1295
0
        if(!curlx_str_single(&host, '[')) {
1296
0
          if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
1297
0
             curlx_str_single(&host, ']'))
1298
0
            goto err;
1299
0
        }
1300
0
        else {
1301
0
          if(curlx_str_until(&host, &target, 4096, ',')) {
1302
0
            if(curlx_str_single(&host, ','))
1303
0
              goto err;
1304
            /* survive nothing but just a comma */
1305
0
            continue;
1306
0
          }
1307
0
        }
1308
#ifndef USE_IPV6
1309
        if(memchr(target.str, ':', target.len)) {
1310
          infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
1311
                address);
1312
          if(curlx_str_single(&host, ','))
1313
            goto err;
1314
          continue;
1315
        }
1316
#endif
1317
1318
0
        if(curlx_strlen(&target) >= sizeof(address))
1319
0
          goto err;
1320
1321
0
        memcpy(address, curlx_str(&target), curlx_strlen(&target));
1322
0
        address[curlx_strlen(&target)] = '\0';
1323
1324
0
        ai = Curl_str2addr(address, (int)port);
1325
0
        if(!ai) {
1326
0
          infof(data, "Resolve address '%s' found illegal", address);
1327
0
          goto err;
1328
0
        }
1329
1330
0
        if(tail) {
1331
0
          tail->ai_next = ai;
1332
0
          tail = tail->ai_next;
1333
0
        }
1334
0
        else {
1335
0
          head = tail = ai;
1336
0
        }
1337
0
        if(curlx_str_single(&host, ','))
1338
0
          break;
1339
0
      }
1340
1341
0
      if(!head)
1342
0
        goto err;
1343
1344
0
      error = FALSE;
1345
0
err:
1346
0
      if(error) {
1347
0
        failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'",
1348
0
              hostp->data);
1349
0
        Curl_freeaddrinfo(head);
1350
0
        return CURLE_SETOPT_OPTION_SYNTAX;
1351
0
      }
1352
1353
      /* Create an entry id, based upon the hostname and port */
1354
0
      entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source),
1355
0
                                     (int)port,
1356
0
                                     entry_id, sizeof(entry_id));
1357
1358
0
      dnscache_lock(data, dnscache);
1359
1360
      /* See if it is already in our dns cache */
1361
0
      dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
1362
1363
0
      if(dns) {
1364
0
        infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T
1365
0
              " - old addresses discarded", (int)curlx_strlen(&source),
1366
0
              curlx_str(&source), port);
1367
        /* delete old entry, there are two reasons for this
1368
         1. old entry may have different addresses.
1369
         2. even if entry with correct addresses is already in the cache,
1370
            but if it is close to expire, then by the time next http
1371
            request is made, it can get expired and pruned because old
1372
            entry is not necessarily marked as permanent.
1373
         3. when adding a non-permanent entry, we want it to remove and
1374
            replace an existing permanent entry.
1375
         4. when adding a non-permanent entry, we want it to get a "fresh"
1376
            timeout that starts _now_. */
1377
1378
0
        Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
1379
0
      }
1380
1381
      /* put this new host in the cache */
1382
0
      dns = dnscache_add_addr(data, dnscache, head, curlx_str(&source),
1383
0
                              curlx_strlen(&source), (int)port, permanent);
1384
0
      if(dns) {
1385
        /* release the returned reference; the cache itself will keep the
1386
         * entry alive: */
1387
0
        dns->refcount--;
1388
0
      }
1389
1390
0
      dnscache_unlock(data, dnscache);
1391
1392
0
      if(!dns)
1393
0
        return CURLE_OUT_OF_MEMORY;
1394
1395
0
#ifndef CURL_DISABLE_VERBOSE_STRINGS
1396
0
      infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s",
1397
0
            (int)curlx_strlen(&source), curlx_str(&source), port, addresses,
1398
0
            permanent ? "" : " (non-permanent)");
1399
0
#endif
1400
1401
      /* Wildcard hostname */
1402
0
      if(curlx_str_casecompare(&source, "*")) {
1403
0
        infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard",
1404
0
              port);
1405
0
        data->state.wildcard_resolve = TRUE;
1406
0
      }
1407
0
    }
1408
0
  }
1409
0
  data->state.resolve = NULL; /* dealt with now */
1410
1411
0
  return CURLE_OK;
1412
0
}
1413
1414
#ifndef CURL_DISABLE_VERBOSE_STRINGS
1415
static void show_resolve_info(struct Curl_easy *data,
1416
                              struct Curl_dns_entry *dns)
1417
95.2k
{
1418
95.2k
  struct Curl_addrinfo *a;
1419
95.2k
  CURLcode result = CURLE_OK;
1420
95.2k
#ifdef CURLRES_IPV6
1421
95.2k
  struct dynbuf out[2];
1422
#else
1423
  struct dynbuf out[1];
1424
#endif
1425
95.2k
  DEBUGASSERT(data);
1426
95.2k
  DEBUGASSERT(dns);
1427
1428
95.2k
  if(!data->set.verbose ||
1429
     /* ignore no name or numerical IP addresses */
1430
95.2k
     !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
1431
95.2k
    return;
1432
1433
0
  a = dns->addr;
1434
1435
0
  infof(data, "Host %s:%d was resolved.",
1436
0
        (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
1437
1438
0
  curlx_dyn_init(&out[0], 1024);
1439
0
#ifdef CURLRES_IPV6
1440
0
  curlx_dyn_init(&out[1], 1024);
1441
0
#endif
1442
1443
0
  while(a) {
1444
0
    if(
1445
0
#ifdef CURLRES_IPV6
1446
0
       a->ai_family == PF_INET6 ||
1447
0
#endif
1448
0
       a->ai_family == PF_INET) {
1449
0
      char buf[MAX_IPADR_LEN];
1450
0
      struct dynbuf *d = &out[(a->ai_family != PF_INET)];
1451
0
      Curl_printable_address(a, buf, sizeof(buf));
1452
0
      if(curlx_dyn_len(d))
1453
0
        result = curlx_dyn_addn(d, ", ", 2);
1454
0
      if(!result)
1455
0
        result = curlx_dyn_add(d, buf);
1456
0
      if(result) {
1457
0
        infof(data, "too many IP, cannot show");
1458
0
        goto fail;
1459
0
      }
1460
0
    }
1461
0
    a = a->ai_next;
1462
0
  }
1463
1464
0
#ifdef CURLRES_IPV6
1465
0
  infof(data, "IPv6: %s",
1466
0
        (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)"));
1467
0
#endif
1468
0
  infof(data, "IPv4: %s",
1469
0
        (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)"));
1470
1471
0
fail:
1472
0
  curlx_dyn_free(&out[0]);
1473
0
#ifdef CURLRES_IPV6
1474
0
  curlx_dyn_free(&out[1]);
1475
0
#endif
1476
0
}
1477
#endif
1478
1479
#ifdef USE_CURL_ASYNC
1480
CURLcode Curl_resolv_check(struct Curl_easy *data,
1481
                           struct Curl_dns_entry **dns)
1482
2.61M
{
1483
2.61M
  CURLcode result;
1484
1485
  /* If async resolving is ongoing, this must be set */
1486
2.61M
  if(!data->state.async.hostname)
1487
0
    return CURLE_FAILED_INIT;
1488
1489
  /* check if we have the name resolved by now (from someone else) */
1490
2.61M
  *dns = Curl_dnscache_get(data, data->state.async.hostname,
1491
2.61M
                           data->state.async.port,
1492
2.61M
                           data->state.async.ip_version);
1493
2.61M
  if(*dns) {
1494
    /* Tell a possibly async resolver we no longer need the results. */
1495
23
    infof(data, "Hostname '%s' was found in DNS cache",
1496
23
          data->state.async.hostname);
1497
23
    Curl_async_shutdown(data);
1498
23
    data->state.async.dns = *dns;
1499
23
    data->state.async.done = TRUE;
1500
23
    return CURLE_OK;
1501
23
  }
1502
1503
2.61M
#ifndef CURL_DISABLE_DOH
1504
2.61M
  if(data->conn->bits.doh) {
1505
1.46M
    result = Curl_doh_is_resolved(data, dns);
1506
1.46M
  }
1507
1.15M
  else
1508
1.15M
#endif
1509
1.15M
  result = Curl_async_is_resolved(data, dns);
1510
2.61M
  if(*dns)
1511
255
    show_resolve_info(data, *dns);
1512
2.61M
  return result;
1513
2.61M
}
1514
#endif
1515
1516
int Curl_resolv_getsock(struct Curl_easy *data,
1517
                        curl_socket_t *socks)
1518
1.03M
{
1519
1.03M
#ifdef CURLRES_ASYNCH
1520
1.03M
#ifndef CURL_DISABLE_DOH
1521
1.03M
  if(data->conn->bits.doh)
1522
    /* nothing to wait for during DoH resolve, those handles have their own
1523
       sockets */
1524
8.29k
    return GETSOCK_BLANK;
1525
1.02M
#endif
1526
1.02M
  return Curl_async_getsock(data, socks);
1527
#else
1528
  (void)data;
1529
  (void)socks;
1530
  return GETSOCK_BLANK;
1531
#endif
1532
1.03M
}
1533
1534
/* Call this function after Curl_connect() has returned async=TRUE and
1535
   then a successful name resolve has been received.
1536
1537
   Note: this function disconnects and frees the conn data in case of
1538
   resolve failure */
1539
CURLcode Curl_once_resolved(struct Curl_easy *data,
1540
                            struct Curl_dns_entry *dns,
1541
                            bool *protocol_done)
1542
273
{
1543
273
  CURLcode result;
1544
273
  struct connectdata *conn = data->conn;
1545
1546
273
#ifdef USE_CURL_ASYNC
1547
273
  if(data->state.async.dns) {
1548
273
    DEBUGASSERT(data->state.async.dns == dns);
1549
273
    data->state.async.dns = NULL;
1550
273
  }
1551
273
#endif
1552
1553
273
  result = Curl_setup_conn(data, dns, protocol_done);
1554
1555
273
  if(result) {
1556
0
    Curl_detach_connection(data);
1557
0
    Curl_conn_terminate(data, conn, TRUE);
1558
0
  }
1559
273
  return result;
1560
273
}
1561
1562
/*
1563
 * Curl_resolver_error() calls failf() with the appropriate message after a
1564
 * resolve error
1565
 */
1566
1567
#ifdef USE_CURL_ASYNC
1568
CURLcode Curl_resolver_error(struct Curl_easy *data)
1569
3.23k
{
1570
3.23k
  struct connectdata *conn = data->conn;
1571
3.23k
  const char *host_or_proxy = "host";
1572
3.23k
  const char *name = conn->host.dispname;
1573
3.23k
  CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
1574
1575
3.23k
#ifndef CURL_DISABLE_PROXY
1576
3.23k
  if(conn->bits.proxy) {
1577
424
    host_or_proxy = "proxy";
1578
424
    result = CURLE_COULDNT_RESOLVE_PROXY;
1579
424
    name = conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
1580
424
      conn->http_proxy.host.dispname;
1581
424
  }
1582
3.23k
#endif
1583
1584
3.23k
  failf(data, "Could not resolve %s: %s", host_or_proxy, name);
1585
3.23k
  return result;
1586
3.23k
}
1587
#endif /* USE_CURL_ASYNC */