Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/curl/lib/vdns/dnscache.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 "urldata.h"
44
#include "curl_addrinfo.h"
45
#include "curl_share.h"
46
#include "curl_trc.h"
47
#include "hash.h"
48
#include "progress.h"
49
#include "rand.h"
50
#include "strcase.h"
51
#include "vdns/dnscache.h"
52
#include "vdns/hostip.h"
53
#include "vdns/httpsrr.h"
54
#include "curlx/inet_ntop.h"
55
#include "curlx/inet_pton.h"
56
#include "curlx/strcopy.h"
57
#include "curlx/strparse.h"
58
59
#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
60
61
86.7k
#define MAX_DNS_CACHE_SIZE 29999
62
63
struct dnsc_id {
64
  struct Curl_str name;
65
  uint16_t port;
66
  char type;
67
};
68
69
static void dnsc_peer2id(struct dnsc_id *pid, char type,
70
                         struct Curl_peer *peer)
71
269k
{
72
269k
  curlx_str_assign(&pid->name, peer->hostname, strlen(peer->hostname));
73
269k
  pid->port = peer->port;
74
269k
  pid->type = type;
75
269k
}
76
77
static void dnsc_str2id(struct dnsc_id *pid, char type,
78
                         struct Curl_str *name, uint16_t port)
79
14.5k
{
80
14.5k
  pid->name = *name;
81
14.5k
  pid->port = port;
82
14.5k
  pid->type = type;
83
14.5k
}
84
85
struct dnsc_key {
86
  char data[MAX_HOSTCACHE_LEN];
87
  size_t len;
88
};
89
90
/*
91
 * Create a hostcache id string for the provided host + port, to be used by
92
 * the DNS caching. Without alloc. Return length of the id string.
93
 */
94
static void dnsc_id2key(struct dnsc_key *key, struct dnsc_id *id)
95
269k
{
96
269k
  size_t namelen = curlx_strlen(&id->name);
97
269k
  if(namelen > (sizeof(key->data) - 8))
98
2.84k
    namelen = sizeof(key->data) - 8;
99
  /* store and lower case the name */
100
269k
  key->data[0] = id->type;
101
269k
  Curl_strntolower(key->data + 1, curlx_str(&id->name), namelen);
102
  /* include the terminating 0 in key length */
103
269k
  key->len = namelen + 2 +
104
269k
             curl_msnprintf(&key->data[namelen + 1], 7, ":%u", id->port);
105
269k
}
106
107
108
static void dnscache_entry_free(struct Curl_dns_entry *dns)
109
58.3k
{
110
58.3k
  Curl_freeaddrinfo(dns->addr);
111
#ifdef USE_HTTPSRR
112
  Curl_httpsrr_destroy(dns->hinfo);
113
#endif
114
58.3k
  curlx_free(dns);
115
58.3k
}
116
117
struct dnscache_prune_data {
118
  struct curltime now;
119
  timediff_t oldest_ms; /* oldest time in cache not pruned. */
120
  timediff_t max_age_ms;
121
};
122
123
/*
124
 * This function is set as a callback to be called for every entry in the DNS
125
 * cache when we want to prune old unused entries.
126
 *
127
 * Returning non-zero means remove the entry, return 0 to keep it in the
128
 * cache.
129
 */
130
static int dnscache_entry_is_stale(void *datap, void *hc)
131
337k
{
132
337k
  struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap;
133
337k
  struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc;
134
135
337k
  if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) {
136
    /* get age in milliseconds */
137
337k
    timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp);
138
337k
    if(!dns->addr)
139
299k
      age *= 2; /* negative entries age twice as fast */
140
337k
    if(age >= prune->max_age_ms)
141
1.09k
      return TRUE;
142
336k
    if(age > prune->oldest_ms)
143
88.0k
      prune->oldest_ms = age;
144
336k
  }
145
336k
  return FALSE;
146
337k
}
147
148
/*
149
 * Prune the DNS cache. This assumes that a lock has already been taken.
150
 * Returns the 'age' of the oldest still kept entry - in milliseconds.
151
 */
152
static timediff_t dnscache_prune(struct Curl_hash *hostcache,
153
                                 timediff_t cache_timeout_ms,
154
                                 struct curltime now)
155
86.7k
{
156
86.7k
  struct dnscache_prune_data user;
157
158
86.7k
  user.max_age_ms = cache_timeout_ms;
159
86.7k
  user.now = now;
160
86.7k
  user.oldest_ms = 0;
161
162
86.7k
  Curl_hash_clean_with_criterium(hostcache,
163
86.7k
                                 (void *)&user,
164
86.7k
                                 dnscache_entry_is_stale);
165
166
86.7k
  return user.oldest_ms;
167
86.7k
}
168
169
static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
170
373k
{
171
373k
  if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS))
172
0
    return &data->share->dnscache;
173
373k
  if(data->multi)
174
373k
    return &data->multi->dnscache;
175
0
  return NULL;
176
373k
}
177
178
static void dnscache_lock(struct Curl_easy *data,
179
                          struct Curl_dnscache *dnscache)
180
373k
{
181
373k
  if(data->share && dnscache == &data->share->dnscache)
182
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
183
373k
}
184
185
static void dnscache_unlock(struct Curl_easy *data,
186
                            struct Curl_dnscache *dnscache)
187
373k
{
188
373k
  if(data->share && dnscache == &data->share->dnscache)
189
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
190
373k
}
191
192
/*
193
 * Library-wide function for pruning the DNS cache. This function takes and
194
 * returns the appropriate locks.
195
 */
196
void Curl_dnscache_prune(struct Curl_easy *data)
197
86.7k
{
198
86.7k
  struct Curl_dnscache *dnscache = dnscache_get(data);
199
  /* the timeout may be set -1 (forever) */
200
86.7k
  timediff_t timeout_ms = data->set.dns_cache_timeout_ms;
201
202
86.7k
  if(!dnscache || (timeout_ms == -1))
203
    /* NULL hostcache means we cannot do it */
204
0
    return;
205
206
86.7k
  dnscache_lock(data, dnscache);
207
208
86.7k
  do {
209
    /* Remove outdated and unused entries from the hostcache */
210
86.7k
    timediff_t oldest_ms =
211
86.7k
      dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data));
212
213
86.7k
    if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE)
214
      /* prune the ones over half this age */
215
0
      timeout_ms = oldest_ms / 2;
216
86.7k
    else
217
86.7k
      break;
218
219
    /* if the cache size is still too big, use the oldest age as new prune
220
       limit */
221
86.7k
  } while(timeout_ms);
222
223
86.7k
  dnscache_unlock(data, dnscache);
224
86.7k
}
225
226
void Curl_dnscache_clear(struct Curl_easy *data)
227
0
{
228
0
  struct Curl_dnscache *dnscache = dnscache_get(data);
229
0
  if(dnscache) {
230
0
    dnscache_lock(data, dnscache);
231
0
    Curl_hash_clean(&dnscache->entries);
232
0
    dnscache_unlock(data, dnscache);
233
0
  }
234
0
}
235
236
/* lookup address, returns entry if found and not stale */
237
static CURLcode fetch_addr(struct Curl_easy *data,
238
                           struct Curl_dnscache *dnscache,
239
                           uint8_t dns_queries,
240
                           struct Curl_peer *peer,
241
                           struct Curl_dns_entry **pdns)
242
211k
{
243
211k
  struct Curl_dns_entry *dns = NULL;
244
211k
  struct dnsc_id id;
245
211k
  struct dnsc_key key;
246
211k
  char type = CURL_DNSQ_IS_ADDR(dns_queries) ?
247
211k
              CURL_DNST_ADDR : CURL_DNST_HTTPS;
248
211k
  CURLcode result = CURLE_OK;
249
250
211k
  *pdns = NULL;
251
211k
  if(!dnscache)
252
0
    return CURLE_OK;
253
254
211k
  dnsc_peer2id(&id, type, peer);
255
211k
  dnsc_id2key(&key, &id);
256
257
  /* See if it is already in our dns cache */
258
211k
  dns = Curl_hash_pick(&dnscache->entries, key.data, key.len);
259
260
  /* No entry found in cache, check if we might have a wildcard entry */
261
211k
  if(!dns && (type == CURL_DNST_ADDR) && data->state.wildcard_resolve) {
262
0
    struct Curl_str wildname;
263
264
0
    curlx_str_assign(&wildname, "*", 1);
265
0
    dnsc_str2id(&id, CURL_DNST_ADDR, &wildname, peer->port);
266
0
    dnsc_id2key(&key, &id);
267
268
    /* See if it is already in our dns cache */
269
0
    dns = Curl_hash_pick(&dnscache->entries, key.data, key.len);
270
0
  }
271
272
211k
  if(dns && (data->set.dns_cache_timeout_ms != -1)) {
273
    /* See whether the returned entry is stale. Done before we release lock */
274
22.5k
    struct dnscache_prune_data user;
275
276
22.5k
    user.now = *Curl_pgrs_now(data);
277
22.5k
    user.max_age_ms = data->set.dns_cache_timeout_ms;
278
22.5k
    user.oldest_ms = 0;
279
280
22.5k
    if(dnscache_entry_is_stale(&user, dns)) {
281
76
      infof(data, "Hostname in DNS cache was stale, zapped");
282
76
      dns = NULL; /* the memory deallocation is being handled by the hash */
283
76
      Curl_hash_delete(&dnscache->entries, key.data, key.len);
284
76
    }
285
22.5k
  }
286
287
  /* We need to cache address information and HTTPS-RR separately. */
288
211k
  if(dns && CURL_DNSQ_IS_ADDR(dns_queries)) {
289
22.4k
    if((uint8_t)(dns->dns_queries & dns_queries) !=
290
22.4k
       (uint8_t)(dns_queries & CURL_DNSQ_ADDR)) {
291
      /* The entry does not cover all wanted address queries, a miss. */
292
0
      dns = NULL;
293
0
    }
294
22.4k
    else if(!(dns->dns_responses & dns_queries)) {
295
      /* The entry has no responses for the wanted DNS queries. */
296
19.6k
      CURL_TRC_DNS(data, "cache entry does not have type=%s addresses",
297
19.6k
                   Curl_resolv_query_str(dns_queries));
298
19.6k
      dns = NULL;
299
19.6k
      result = CURLE_COULDNT_RESOLVE_HOST;
300
19.6k
    }
301
2.82k
    else if(dns && !dns->addr) { /* negative entry */
302
0
      dns = NULL;
303
0
      result = CURLE_COULDNT_RESOLVE_HOST;
304
0
    }
305
22.4k
  }
306
307
211k
  *pdns = dns;
308
211k
  return result;
309
211k
}
310
311
/*
312
 * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
313
 *
314
 * Curl_resolv() checks initially and multi_runsingle() checks each time
315
 * it discovers the handle in the state WAITRESOLVE whether the hostname
316
 * has already been resolved and the address has already been stored in
317
 * the DNS cache. This short circuits waiting for a lot of pending
318
 * lookups for the same hostname requested by different handles.
319
 *
320
 * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
321
 *
322
 * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after
323
 * use, or we will leak memory!
324
 */
325
CURLcode Curl_dnscache_get(struct Curl_easy *data,
326
                           uint8_t dns_queries,
327
                           struct Curl_peer *peer,
328
                           struct Curl_dns_entry **pentry)
329
211k
{
330
211k
  struct Curl_dnscache *dnscache = dnscache_get(data);
331
211k
  struct Curl_dns_entry *dns = NULL;
332
211k
  CURLcode result = CURLE_OK;
333
334
211k
  dnscache_lock(data, dnscache);
335
211k
  result = fetch_addr(data, dnscache, dns_queries, peer, &dns);
336
211k
  if(!result && dns)
337
2.82k
    dns->refcount++; /* we pass out a reference */
338
208k
  else if(result) {
339
19.6k
    DEBUGASSERT(!dns);
340
19.6k
    dns = NULL;
341
19.6k
  }
342
211k
  dnscache_unlock(data, dnscache);
343
344
211k
  CURL_TRC_DNS(data, "cache lookup %s:%u queries=%s -> %d %sfound",
345
211k
               peer->hostname, peer->port, Curl_resolv_query_str(dns_queries),
346
211k
               (int)result, dns ? "" : "not ");
347
211k
  *pentry = dns;
348
211k
  return result;
349
211k
}
350
351
#ifndef CURL_DISABLE_SHUFFLE_DNS
352
/*
353
 * Return # of addresses in a Curl_addrinfo struct
354
 */
355
static int num_addresses(const struct Curl_addrinfo *addr)
356
0
{
357
0
  int i = 0;
358
0
  while(addr) {
359
0
    addr = addr->ai_next;
360
0
    i++;
361
0
  }
362
0
  return i;
363
0
}
364
365
/*
366
 * dns_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
367
 * struct by re-linking its linked list.
368
 *
369
 * The addr argument should be the address of a pointer to the head node of a
370
 * `Curl_addrinfo` list and it will be modified to point to the new head after
371
 * shuffling.
372
 *
373
 * Not declared static only to make it easy to use in a unit test!
374
 *
375
 * @unittest 1608
376
 */
377
UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data,
378
                                   struct Curl_addrinfo **addr);
379
UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data,
380
                                   struct Curl_addrinfo **addr)
381
0
{
382
0
  CURLcode result = CURLE_OK;
383
0
  const int num_addrs = num_addresses(*addr);
384
385
0
  if(num_addrs > 1) {
386
0
    struct Curl_addrinfo **nodes;
387
0
    CURL_TRC_DNS(data, "Shuffling %d addresses", num_addrs);
388
389
0
    nodes = curlx_malloc(num_addrs * sizeof(*nodes));
390
0
    if(nodes) {
391
0
      int i;
392
0
      unsigned int *rnd;
393
0
      const size_t rnd_size = num_addrs * sizeof(*rnd);
394
395
      /* build a plain array of Curl_addrinfo pointers */
396
0
      nodes[0] = *addr;
397
0
      for(i = 1; i < num_addrs; i++) {
398
0
        nodes[i] = nodes[i - 1]->ai_next;
399
0
      }
400
401
0
      rnd = curlx_malloc(rnd_size);
402
0
      if(rnd) {
403
        /* Fisher-Yates shuffle */
404
0
        if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
405
0
          struct Curl_addrinfo *swap_tmp;
406
0
          for(i = num_addrs - 1; i > 0; i--) {
407
0
            swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)];
408
0
            nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i];
409
0
            nodes[i] = swap_tmp;
410
0
          }
411
412
          /* relink list in the new order */
413
0
          for(i = 1; i < num_addrs; i++) {
414
0
            nodes[i - 1]->ai_next = nodes[i];
415
0
          }
416
417
0
          nodes[num_addrs - 1]->ai_next = NULL;
418
0
          *addr = nodes[0];
419
0
        }
420
0
        curlx_free(rnd);
421
0
      }
422
0
      else
423
0
        result = CURLE_OUT_OF_MEMORY;
424
0
      curlx_free(nodes);
425
0
    }
426
0
    else
427
0
      result = CURLE_OUT_OF_MEMORY;
428
0
  }
429
0
  return result;
430
0
}
431
#endif
432
433
static bool dnscache_ai_has_family(struct Curl_addrinfo *ai,
434
                                   int ai_family)
435
116k
{
436
134k
  for(; ai; ai = ai->ai_next) {
437
35.7k
    if(ai->ai_family == ai_family)
438
17.8k
      return TRUE;
439
35.7k
  }
440
98.7k
  return FALSE;
441
116k
}
442
443
static struct Curl_dns_entry *dnsc_entry_create(struct Curl_easy *data,
444
                                                struct dnsc_id *pid,
445
                                                bool permanent)
446
58.3k
{
447
58.3k
  struct Curl_dns_entry *dns = NULL;
448
449
  /* Create a new cache entry, struct already has the hostname NUL */
450
58.3k
  dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) +
451
58.3k
                     curlx_strlen(&pid->name));
452
58.3k
  if(!dns)
453
0
    goto out;
454
455
58.3k
  dns->refcount = 1; /* the cache has the first reference */
456
58.3k
  dns->hostlen = curlx_strlen(&pid->name);
457
58.3k
  dns->port = pid->port;
458
58.3k
  if(dns->hostlen)
459
58.3k
    memcpy(dns->hostname, curlx_str(&pid->name), dns->hostlen);
460
461
58.3k
  if(permanent) {
462
0
    dns->timestamp.tv_sec = 0; /* an entry that never goes stale */
463
0
    dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
464
0
  }
465
58.3k
  else {
466
58.3k
    dns->timestamp = *Curl_pgrs_now(data);
467
58.3k
  }
468
469
58.3k
out:
470
58.3k
  return dns;
471
58.3k
}
472
473
static struct Curl_dns_entry *
474
dnsc_entry_assign_addr(
475
  struct Curl_easy *data,
476
  struct Curl_dns_entry *dns,
477
  uint8_t dns_queries,
478
  struct Curl_addrinfo **paddr1,
479
  struct Curl_addrinfo **paddr2)
480
58.3k
{
481
58.3k
  if(!dns)
482
0
    goto out;
483
  /* only do this when this is the only reference */
484
58.3k
  DEBUGASSERT(dns->refcount == 1);
485
58.3k
  DEBUGASSERT(dns->type == CURL_DNST_INIT);
486
487
58.3k
  dns->type = CURL_DNST_ADDR;
488
  /* queries should only be about addresses */
489
58.3k
  DEBUGASSERT(!(dns_queries & ~CURL_DNSQ_ADDR));
490
58.3k
  dns->dns_queries = (dns_queries & CURL_DNSQ_ADDR);
491
492
  /* Take the given address lists into the entry */
493
58.3k
  if(paddr1 && *paddr1) {
494
14.5k
    dns->addr = *paddr1;
495
14.5k
    *paddr1 = NULL;
496
14.5k
  }
497
58.3k
  if(paddr2 && *paddr2) {
498
3.16k
    struct Curl_addrinfo **phead = &dns->addr;
499
9.59k
    while(*phead)
500
6.43k
      phead = &(*phead)->ai_next;
501
3.16k
    *phead = *paddr2;
502
3.16k
    *paddr2 = NULL;
503
3.16k
  }
504
505
58.3k
  if((dns_queries & CURL_DNSQ_A) &&
506
58.3k
     dnscache_ai_has_family(dns->addr, PF_INET))
507
14.5k
    dns->dns_responses |= CURL_DNSQ_A;
508
509
58.3k
#ifdef USE_IPV6
510
58.3k
  if((dns_queries & CURL_DNSQ_AAAA) &&
511
58.3k
     dnscache_ai_has_family(dns->addr, PF_INET6))
512
3.28k
    dns->dns_responses |= CURL_DNSQ_AAAA;
513
58.3k
#endif /* USE_IPV6 */
514
515
58.3k
#ifndef CURL_DISABLE_SHUFFLE_DNS
516
  /* shuffle addresses if requested */
517
58.3k
  if(data->set.dns_shuffle_addresses && dns->addr) {
518
0
    CURLcode result = dns_shuffle_addr(data, &dns->addr);
519
0
    if(result) {
520
      /* free without lock, we are the sole owner */
521
0
      dnscache_entry_free(dns);
522
0
      dns = NULL;
523
0
      goto out;
524
0
    }
525
0
  }
526
#else
527
  (void)data;
528
#endif
529
530
58.3k
out:
531
58.3k
  if(paddr1 && *paddr1) {
532
0
    Curl_freeaddrinfo(*paddr1);
533
0
    *paddr1 = NULL;
534
0
  }
535
58.3k
  if(paddr2 && *paddr2) {
536
0
    Curl_freeaddrinfo(*paddr2);
537
0
    *paddr2 = NULL;
538
0
  }
539
58.3k
  return dns;
540
58.3k
}
541
542
struct Curl_dns_entry *Curl_dnsc_mk_addr(struct Curl_easy *data,
543
                                         uint8_t dns_queries,
544
                                         struct Curl_addrinfo **paddr,
545
                                         struct Curl_peer *peer)
546
10.7k
{
547
10.7k
  struct dnsc_id id;
548
10.7k
  struct Curl_dns_entry *dns;
549
550
10.7k
  dnsc_peer2id(&id, CURL_DNST_ADDR, peer);
551
10.7k
  dns = dnsc_entry_create(data, &id, FALSE);
552
10.7k
  dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL);
553
10.7k
  return dns;
554
10.7k
}
555
556
struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data,
557
                                          uint8_t dns_queries,
558
                                          struct Curl_addrinfo **paddr1,
559
                                          struct Curl_addrinfo **paddr2,
560
                                          struct Curl_peer *peer)
561
3.79k
{
562
3.79k
  struct dnsc_id id;
563
3.79k
  struct Curl_dns_entry *dns;
564
565
3.79k
  dnsc_peer2id(&id, CURL_DNST_ADDR, peer);
566
3.79k
  dns = dnsc_entry_create(data, &id, FALSE);
567
3.79k
  dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr1, paddr2);
568
3.79k
  return dns;
569
3.79k
}
570
571
#ifdef USE_HTTPSRR
572
static struct Curl_dns_entry *
573
dnsc_entry_assign_https(struct Curl_dns_entry *dns,
574
                        struct Curl_https_rrinfo **phinfo)
575
{
576
  if(!dns)
577
    goto out;
578
  /* only do this when this is the only reference */
579
  DEBUGASSERT(dns->refcount == 1);
580
  DEBUGASSERT(dns->type == CURL_DNST_INIT);
581
582
  if(dns->hinfo) {
583
    Curl_httpsrr_destroy(dns->hinfo);
584
    dns->hinfo = NULL;
585
  }
586
  dns->type = CURL_DNST_HTTPS;
587
  dns->dns_responses = dns->dns_queries = CURL_DNSQ_HTTPS;
588
  if(phinfo) {
589
    dns->hinfo = *phinfo;
590
    *phinfo = NULL;
591
  }
592
out:
593
  if(phinfo && *phinfo) {
594
    Curl_httpsrr_destroy(*phinfo);
595
    *phinfo = NULL;
596
  }
597
  return dns;
598
}
599
600
struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data,
601
                                          struct Curl_https_rrinfo **phinfo,
602
                                          struct Curl_peer *peer)
603
{
604
  struct dnsc_id id;
605
  struct Curl_dns_entry *dns;
606
607
  dnsc_peer2id(&id, CURL_DNST_HTTPS, peer);
608
  dns = dnsc_entry_create(data, &id, FALSE);
609
  dns = dnsc_entry_assign_https(dns, phinfo);
610
  return dns;
611
}
612
613
static struct Curl_dns_entry *dnsc_add_https(struct Curl_easy *data,
614
                                             struct Curl_dnscache *dnscache,
615
                                             struct Curl_https_rrinfo **phinfo,
616
                                             struct dnsc_id *id,
617
                                             bool permanent)
618
{
619
  struct Curl_dns_entry *dns, *dns2;
620
  struct dnsc_key key;
621
622
  dns = dnsc_entry_create(data, id, permanent);
623
  dns = dnsc_entry_assign_https(dns, phinfo);
624
  if(!dns)
625
    return NULL;
626
627
  /* Store the resolved data in our DNS cache. */
628
  dnsc_id2key(&key, id);
629
  dns2 = Curl_hash_add(&dnscache->entries, key.data, key.len, (void *)dns);
630
  if(!dns2) {
631
    dnscache_entry_free(dns);
632
    return NULL;
633
  }
634
635
  dns = dns2;
636
  dns->refcount++;         /* mark entry as in-use */
637
  return dns;
638
}
639
640
#endif /* USE_HTTPSRR */
641
642
static struct Curl_dns_entry *dnsc_add_addr(struct Curl_easy *data,
643
                                            struct Curl_dnscache *dnscache,
644
                                            uint8_t dns_queries,
645
                                            struct Curl_addrinfo **paddr,
646
                                            struct dnsc_id *id,
647
                                            struct dnsc_key *key,
648
                                            bool permanent)
649
0
{
650
0
  struct Curl_dns_entry *dns;
651
0
  struct Curl_dns_entry *dns2;
652
653
0
  dns = dnsc_entry_create(data, id, permanent);
654
0
  dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL);
655
0
  if(!dns)
656
0
    return NULL;
657
658
  /* Store the resolved data in our DNS cache. */
659
0
  dns2 = Curl_hash_add(&dnscache->entries, key->data, key->len, (void *)dns);
660
0
  if(!dns2) {
661
0
    dnscache_entry_free(dns);
662
0
    return NULL;
663
0
  }
664
665
0
  dns = dns2;
666
0
  dns->refcount++;         /* mark entry as in-use */
667
0
  return dns;
668
0
}
669
670
static struct Curl_dns_entry *
671
dnsc_add_peer_addr(struct Curl_easy *data,
672
                   struct Curl_dnscache *dnscache,
673
                   uint8_t dns_queries,
674
                   struct Curl_addrinfo **paddr,
675
                   struct dnsc_id *id,
676
                   bool permanent)
677
43.7k
{
678
43.7k
  struct Curl_dns_entry *dns;
679
43.7k
  struct Curl_dns_entry *dns2;
680
43.7k
  struct dnsc_key key;
681
682
43.7k
  dns = dnsc_entry_create(data, id, permanent);
683
43.7k
  dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL);
684
43.7k
  if(!dns)
685
0
    return NULL;
686
687
688
  /* Store the resolved data in our DNS cache. */
689
43.7k
  dnsc_id2key(&key, id);
690
43.7k
  dns2 = Curl_hash_add(&dnscache->entries, key.data, key.len, (void *)dns);
691
43.7k
  if(!dns2) {
692
0
    dnscache_entry_free(dns);
693
0
    return NULL;
694
0
  }
695
696
43.7k
  dns = dns2;
697
43.7k
  dns->refcount++;         /* mark entry as in-use */
698
43.7k
  return dns;
699
43.7k
}
700
701
CURLcode Curl_dnscache_add(struct Curl_easy *data,
702
                           struct Curl_dns_entry *entry)
703
14.5k
{
704
14.5k
  struct Curl_dnscache *dnscache = dnscache_get(data);
705
14.5k
  struct Curl_str name;
706
14.5k
  struct dnsc_id id;
707
14.5k
  struct dnsc_key key;
708
709
14.5k
  if(!dnscache)
710
0
    return CURLE_FAILED_INIT;
711
14.5k
  if(!entry || (entry->type == CURL_DNST_INIT))
712
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
713
714
14.5k
  curlx_str_assign(&name, entry->hostname, entry->hostlen);
715
14.5k
  dnsc_str2id(&id, entry->type, &name, entry->port);
716
14.5k
  dnsc_id2key(&key, &id);
717
718
  /* Store the resolved data in our DNS cache and up ref count */
719
14.5k
  dnscache_lock(data, dnscache);
720
14.5k
  if(!Curl_hash_add(&dnscache->entries, key.data, key.len, (void *)entry)) {
721
0
    dnscache_unlock(data, dnscache);
722
0
    return CURLE_OUT_OF_MEMORY;
723
0
  }
724
14.5k
  entry->refcount++;
725
14.5k
  dnscache_unlock(data, dnscache);
726
14.5k
  CURL_TRC_DNS(data, "cached entry for %s:%u queries=%s",
727
14.5k
               entry->hostname, entry->port,
728
14.5k
               Curl_resolv_query_str(entry->dns_queries));
729
14.5k
  return CURLE_OK;
730
14.5k
}
731
732
CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
733
                                    uint8_t dns_queries,
734
                                    struct Curl_peer *peer)
735
43.7k
{
736
43.7k
  struct Curl_dnscache *dnscache = dnscache_get(data);
737
43.7k
  struct Curl_dns_entry *dns = NULL;
738
43.7k
  struct dnsc_id id;
739
43.7k
  CURLcode result = CURLE_OK;
740
741
43.7k
  DEBUGASSERT(dnscache);
742
43.7k
  if(!dnscache)
743
0
    return CURLE_FAILED_INIT;
744
745
43.7k
  dnscache_lock(data, dnscache);
746
747
43.7k
  if(dns_queries & CURL_DNSQ_ADDR) {
748
    /* put this new host in the cache */
749
43.7k
    dnsc_peer2id(&id, CURL_DNST_ADDR, peer);
750
43.7k
    dns = dnsc_add_peer_addr(data, dnscache, dns_queries, NULL, &id, FALSE);
751
43.7k
    if(!dns)
752
0
      result = CURLE_OUT_OF_MEMORY;
753
43.7k
  }
754
#ifdef USE_HTTPSRR
755
  else if(dns_queries == CURL_DNSQ_HTTPS) {
756
    dnsc_peer2id(&id, CURL_DNST_HTTPS, peer);
757
    dns = dnsc_add_https(data, dnscache, NULL, &id, FALSE);
758
    if(!dns)
759
      result = CURLE_OUT_OF_MEMORY;
760
  }
761
#endif
762
0
  else {
763
    /* a query we do not know, just cache nothing */
764
0
    DEBUGASSERT(0);
765
0
  }
766
767
43.7k
  if(dns) {
768
    /* release the returned reference; the cache itself will keep the
769
     * entry alive: */
770
43.7k
    dns->refcount--;
771
43.7k
    CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s",
772
43.7k
                 peer->hostname, peer->port,
773
43.7k
                 Curl_resolv_query_str(dns_queries));
774
43.7k
  }
775
43.7k
  dnscache_unlock(data, dnscache);
776
43.7k
  return result;
777
43.7k
}
778
779
/*
780
 * Curl_dns_entry_unlink() releases a reference to the given cached DNS entry.
781
 * When the reference count reaches 0, the entry is destroyed. It is important
782
 * that only one unlink is made for each Curl_resolv() call.
783
 *
784
 * May be called with 'data' == NULL for global cache.
785
 */
786
void Curl_dns_entry_unlink(struct Curl_easy *data,
787
                           struct Curl_dns_entry **pdns)
788
161k
{
789
161k
  if(*pdns) {
790
17.4k
    struct Curl_dnscache *dnscache = dnscache_get(data);
791
17.4k
    struct Curl_dns_entry *dns = *pdns;
792
17.4k
    *pdns = NULL;
793
17.4k
    dnscache_lock(data, dnscache);
794
17.4k
    dns->refcount--;
795
17.4k
    if(dns->refcount == 0)
796
0
      dnscache_entry_free(dns);
797
17.4k
    dnscache_unlock(data, dnscache);
798
17.4k
  }
799
161k
}
800
801
static void dnscache_entry_dtor(void *entry)
802
58.3k
{
803
58.3k
  struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry;
804
58.3k
  DEBUGASSERT(dns && (dns->refcount > 0));
805
58.3k
  dns->refcount--;
806
58.3k
  if(dns->refcount == 0)
807
58.3k
    dnscache_entry_free(dns);
808
58.3k
}
809
810
/*
811
 * Curl_dnscache_init() inits a new DNS cache.
812
 */
813
void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
814
112k
{
815
112k
  Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare,
816
112k
                 dnscache_entry_dtor);
817
112k
}
818
819
void Curl_dnscache_destroy(struct Curl_dnscache *dns)
820
112k
{
821
112k
  Curl_hash_destroy(&dns->entries);
822
112k
}
823
824
CURLcode Curl_loadhostpairs(struct Curl_easy *data)
825
0
{
826
0
  struct Curl_dnscache *dnscache = dnscache_get(data);
827
0
  struct curl_slist *hostp;
828
0
  struct dnsc_id id;
829
0
  struct dnsc_key key;
830
831
0
  if(!dnscache)
832
0
    return CURLE_FAILED_INIT;
833
834
  /* Default is no wildcard found */
835
0
  data->state.wildcard_resolve = FALSE;
836
837
0
  for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
838
0
    const char *host = hostp->data;
839
0
    struct Curl_str source;
840
0
    if(!host)
841
0
      continue;
842
0
    if(*host == '-') {
843
0
      curl_off_t num = 0;
844
0
      host++;
845
0
      if(!curlx_str_single(&host, '[')) {
846
0
        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
847
0
           curlx_str_single(&host, ']') ||
848
0
           curlx_str_single(&host, ':'))
849
0
          continue;
850
0
      }
851
0
      else {
852
0
        if(curlx_str_until(&host, &source, 4096, ':') ||
853
0
           curlx_str_single(&host, ':')) {
854
0
          continue;
855
0
        }
856
0
      }
857
858
0
      if(!curlx_str_number(&host, &num, 0xffff)) {
859
        /* Create an entry id, based upon the hostname and port */
860
0
        dnsc_str2id(&id, CURL_DNST_ADDR, &source, (uint16_t)num);
861
0
        dnsc_id2key(&key, &id);
862
0
        dnscache_lock(data, dnscache);
863
        /* delete entry, ignore if it did not exist */
864
0
        Curl_hash_delete(&dnscache->entries, key.data, key.len);
865
0
        dnscache_unlock(data, dnscache);
866
0
      }
867
0
    }
868
0
    else {
869
0
      struct Curl_dns_entry *dns;
870
0
      struct Curl_addrinfo *head = NULL, *tail = NULL;
871
0
      char address[64];
872
0
      curl_off_t tmpofft = 0;
873
0
      uint16_t port = 0;
874
0
      bool permanent = TRUE;
875
0
      bool error = TRUE;
876
0
      VERBOSE(const char *addresses = NULL);
877
878
0
      if(*host == '+') {
879
0
        host++;
880
0
        permanent = FALSE;
881
0
      }
882
0
      if(!curlx_str_single(&host, '[')) {
883
0
        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
884
0
           curlx_str_single(&host, ']'))
885
0
          continue;
886
0
      }
887
0
      else {
888
0
        if(curlx_str_until(&host, &source, 4096, ':'))
889
0
          continue;
890
0
      }
891
0
      if(curlx_str_single(&host, ':') ||
892
0
         curlx_str_number(&host, &tmpofft, 0xffff) ||
893
0
         curlx_str_single(&host, ':'))
894
0
        goto err;
895
0
      port = (uint16_t)tmpofft;
896
897
0
      VERBOSE(addresses = host);
898
899
      /* start the address section */
900
0
      while(*host) {
901
0
        struct Curl_str target;
902
0
        struct Curl_addrinfo *ai;
903
0
        CURLcode result;
904
905
0
        if(!curlx_str_single(&host, '[')) {
906
0
          if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
907
0
             curlx_str_single(&host, ']'))
908
0
            goto err;
909
0
        }
910
0
        else {
911
0
          if(curlx_str_until(&host, &target, 4096, ',')) {
912
0
            if(curlx_str_single(&host, ','))
913
0
              goto err;
914
            /* survive nothing but a comma */
915
0
            continue;
916
0
          }
917
0
        }
918
#ifndef USE_IPV6
919
        if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) {
920
          infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.",
921
                (int)curlx_strlen(&target), curlx_str(&target));
922
          if(curlx_str_single(&host, ','))
923
            goto err;
924
          continue;
925
        }
926
#endif
927
928
0
        if(curlx_strlen(&target) >= sizeof(address))
929
0
          goto err;
930
931
0
        memcpy(address, curlx_str(&target), curlx_strlen(&target));
932
0
        address[curlx_strlen(&target)] = '\0';
933
934
0
        result = Curl_str2addr(address, port, &ai);
935
0
        if(result) {
936
0
          infof(data, "Resolve IP address '%s' found is illegal", address);
937
0
          goto err;
938
0
        }
939
940
0
        if(tail) {
941
0
          tail->ai_next = ai;
942
0
          tail = tail->ai_next;
943
0
        }
944
0
        else {
945
0
          head = tail = ai;
946
0
        }
947
0
        if(curlx_str_single(&host, ','))
948
0
          break;
949
0
      }
950
951
0
      if(!head)
952
0
        goto err;
953
954
0
      error = FALSE;
955
0
err:
956
0
      if(error) {
957
0
        failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data);
958
0
        Curl_freeaddrinfo(head);
959
0
        return CURLE_SETOPT_OPTION_SYNTAX;
960
0
      }
961
962
0
      dnsc_str2id(&id, CURL_DNST_ADDR, &source, port);
963
0
      dnsc_id2key(&key, &id);
964
0
      dnscache_lock(data, dnscache);
965
966
      /* See if it is already in our dns cache */
967
0
      dns = Curl_hash_pick(&dnscache->entries, key.data, key.len);
968
969
0
      if(dns) {
970
0
        infof(data, "RESOLVE %.*s:%u - old addresses discarded",
971
0
              (int)curlx_strlen(&source),
972
0
              curlx_str(&source), port);
973
        /* delete old entry, there are two reasons for this
974
         1. old entry may have different addresses.
975
         2. even if entry with correct addresses is already in the cache,
976
            but if it is close to expire, then by the time next http
977
            request is made, it can get expired and pruned because old
978
            entry is not necessarily marked as permanent.
979
         3. when adding a non-permanent entry, we want it to remove and
980
            replace an existing permanent entry.
981
         4. when adding a non-permanent entry, we want it to get a "fresh"
982
            timeout that starts _now_. */
983
984
0
        Curl_hash_delete(&dnscache->entries, key.data, key.len);
985
0
      }
986
987
      /* put this new host in the cache, override all address queries */
988
0
      dns = dnsc_add_addr(data, dnscache, CURL_DNSQ_ADDR, &head,
989
0
                          &id, &key, permanent);
990
0
      if(dns)
991
        /* release the returned reference; the cache itself will keep the
992
         * entry alive: */
993
0
        dns->refcount--;
994
995
0
      dnscache_unlock(data, dnscache);
996
997
0
      if(!dns)
998
0
        return CURLE_OUT_OF_MEMORY;
999
1000
0
      infof(data, "[DNS] added %.*s:%u:%s to cache%s",
1001
0
            (int)curlx_strlen(&id.name), curlx_str(&id.name), id.port,
1002
0
            addresses, permanent ? "" : " (non-permanent)");
1003
1004
      /* Wildcard hostname */
1005
0
      if(curlx_str_casecompare(&source, "*")) {
1006
0
        infof(data, "RESOLVE *:%u using wildcard", port);
1007
0
        data->state.wildcard_resolve = TRUE;
1008
0
      }
1009
0
    }
1010
0
  }
1011
0
  data->state.resolve = NULL; /* dealt with now */
1012
1013
0
  return CURLE_OK;
1014
0
}