Coverage Report

Created: 2025-08-26 06:58

/src/bind9/lib/dns/badcache.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/*! \file */
15
16
#include <inttypes.h>
17
#include <stdbool.h>
18
19
#include <isc/async.h>
20
#include <isc/buffer.h>
21
#include <isc/hash.h>
22
#include <isc/log.h>
23
#include <isc/loop.h>
24
#include <isc/mem.h>
25
#include <isc/mutex.h>
26
#include <isc/rwlock.h>
27
#include <isc/spinlock.h>
28
#include <isc/stdtime.h>
29
#include <isc/string.h>
30
#include <isc/thread.h>
31
#include <isc/time.h>
32
#include <isc/urcu.h>
33
#include <isc/util.h>
34
35
#include <dns/badcache.h>
36
#include <dns/fixedname.h>
37
#include <dns/name.h>
38
#include <dns/rdatatype.h>
39
#include <dns/types.h>
40
41
typedef struct dns_bcentry dns_bcentry_t;
42
43
typedef struct dns_bckey {
44
  const dns_name_t *name;
45
  dns_rdatatype_t type;
46
} dns__bckey_t;
47
48
struct dns_badcache {
49
  unsigned int magic;
50
  isc_mem_t *mctx;
51
  struct cds_lfht *ht;
52
  struct cds_list_head *lru;
53
  uint32_t nloops;
54
};
55
56
0
#define BADCACHE_MAGIC    ISC_MAGIC('B', 'd', 'C', 'a')
57
#define VALID_BADCACHE(m) ISC_MAGIC_VALID(m, BADCACHE_MAGIC)
58
59
0
#define BADCACHE_INIT_SIZE (1 << 10) /* Must be power of 2 */
60
0
#define BADCACHE_MIN_SIZE  (1 << 8)  /* Must be power of 2 */
61
62
struct dns_bcentry {
63
  isc_loop_t *loop;
64
  isc_stdtime_t expire;
65
  uint32_t flags;
66
67
  struct cds_lfht_node ht_node;
68
  struct rcu_head rcu_head;
69
  struct cds_list_head lru_head;
70
71
  dns_name_t name;
72
  dns_rdatatype_t type;
73
};
74
75
static void
76
bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp);
77
78
static void
79
bcentry_destroy(struct rcu_head *rcu_head);
80
81
static bool
82
bcentry_alive(struct cds_lfht *ht, dns_bcentry_t *bad, isc_stdtime_t now);
83
84
dns_badcache_t *
85
0
dns_badcache_new(isc_mem_t *mctx) {
86
0
  uint32_t nloops = isc_loopmgr_nloops();
87
0
  dns_badcache_t *bc = isc_mem_get(mctx, sizeof(*bc));
88
0
  *bc = (dns_badcache_t){
89
0
    .magic = BADCACHE_MAGIC,
90
0
    .nloops = nloops,
91
0
  };
92
93
0
  bc->ht = cds_lfht_new(BADCACHE_INIT_SIZE, BADCACHE_MIN_SIZE, 0,
94
0
            CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
95
0
  INSIST(bc->ht != NULL);
96
97
0
  bc->lru = isc_mem_cget(mctx, bc->nloops, sizeof(bc->lru[0]));
98
0
  for (size_t i = 0; i < bc->nloops; i++) {
99
0
    CDS_INIT_LIST_HEAD(&bc->lru[i]);
100
0
  }
101
102
0
  isc_mem_attach(mctx, &bc->mctx);
103
104
0
  return bc;
105
0
}
106
107
void
108
0
dns_badcache_destroy(dns_badcache_t **bcp) {
109
0
  REQUIRE(bcp != NULL && *bcp != NULL);
110
0
  REQUIRE(VALID_BADCACHE(*bcp));
111
112
0
  dns_badcache_t *bc = *bcp;
113
0
  *bcp = NULL;
114
0
  bc->magic = 0;
115
116
0
  dns_bcentry_t *bad = NULL;
117
0
  struct cds_lfht_iter iter;
118
0
  cds_lfht_for_each_entry(bc->ht, &iter, bad, ht_node) {
119
0
    INSIST(!cds_lfht_del(bc->ht, &bad->ht_node));
120
0
    bcentry_destroy(&bad->rcu_head);
121
0
  }
122
0
  RUNTIME_CHECK(!cds_lfht_destroy(bc->ht, NULL));
123
124
0
  isc_mem_cput(bc->mctx, bc->lru, bc->nloops, sizeof(bc->lru[0]));
125
126
0
  isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t));
127
0
}
128
129
static int
130
0
bcentry_match(struct cds_lfht_node *ht_node, const void *key0) {
131
0
  const dns__bckey_t *key = key0;
132
0
  dns_bcentry_t *bad = caa_container_of(ht_node, dns_bcentry_t, ht_node);
133
134
0
  return (bad->type == key->type) &&
135
0
         dns_name_equal(&bad->name, key->name);
136
0
}
137
138
static uint32_t
139
0
bcentry_hash(const dns__bckey_t *key) {
140
0
  isc_hash32_t state;
141
0
  isc_hash32_init(&state);
142
0
  isc_hash32_hash(&state, key->name->ndata, key->name->length, false);
143
0
  isc_hash32_hash(&state, &key->type, sizeof(key->type), true);
144
0
  return isc_hash32_finalize(&state);
145
0
}
146
147
static dns_bcentry_t *
148
0
bcentry_lookup(struct cds_lfht *ht, uint32_t hashval, dns__bckey_t *key) {
149
0
  struct cds_lfht_iter iter;
150
151
0
  cds_lfht_lookup(ht, hashval, bcentry_match, key, &iter);
152
153
0
  return cds_lfht_entry(cds_lfht_iter_get_node(&iter), dns_bcentry_t,
154
0
            ht_node);
155
0
}
156
157
static dns_bcentry_t *
158
bcentry_new(isc_loop_t *loop, const dns_name_t *name,
159
      const dns_rdatatype_t type, const uint32_t flags,
160
0
      const isc_stdtime_t expire) {
161
0
  isc_mem_t *mctx = isc_loop_getmctx(loop);
162
0
  dns_bcentry_t *bad = isc_mem_get(mctx, sizeof(*bad));
163
0
  *bad = (dns_bcentry_t){
164
0
    .type = type,
165
0
    .flags = flags,
166
0
    .expire = expire,
167
0
    .loop = isc_loop_ref(loop),
168
0
    .lru_head = CDS_LIST_HEAD_INIT(bad->lru_head),
169
0
  };
170
171
0
  dns_name_init(&bad->name);
172
0
  dns_name_dup(name, mctx, &bad->name);
173
174
0
  return bad;
175
0
}
176
177
static void
178
0
bcentry_destroy(struct rcu_head *rcu_head) {
179
0
  dns_bcentry_t *bad = caa_container_of(rcu_head, dns_bcentry_t,
180
0
                rcu_head);
181
0
  isc_loop_t *loop = bad->loop;
182
0
  isc_mem_t *mctx = isc_loop_getmctx(loop);
183
184
0
  dns_name_free(&bad->name, mctx);
185
0
  isc_mem_put(mctx, bad, sizeof(*bad));
186
187
0
  isc_loop_unref(loop);
188
0
}
189
190
static void
191
0
bcentry_evict_async(void *arg) {
192
0
  dns_bcentry_t *bad = arg;
193
194
0
  RUNTIME_CHECK(bad->loop == isc_loop());
195
196
0
  cds_list_del(&bad->lru_head);
197
0
  call_rcu(&bad->rcu_head, bcentry_destroy);
198
0
}
199
200
static void
201
0
bcentry_evict(struct cds_lfht *ht, dns_bcentry_t *bad) {
202
0
  if (!cds_lfht_del(ht, &bad->ht_node)) {
203
0
    if (bad->loop == isc_loop()) {
204
0
      bcentry_evict_async(bad);
205
0
      return;
206
0
    }
207
208
0
    isc_async_run(bad->loop, bcentry_evict_async, bad);
209
0
  }
210
0
}
211
212
static bool
213
0
bcentry_alive(struct cds_lfht *ht, dns_bcentry_t *bad, isc_stdtime_t now) {
214
0
  if (cds_lfht_is_node_deleted(&bad->ht_node)) {
215
0
    return false;
216
0
  } else if (bad->expire < now) {
217
0
    bcentry_evict(ht, bad);
218
0
    return false;
219
0
  }
220
221
0
  return true;
222
0
}
223
224
#define cds_lfht_for_each_entry_next(ht, iter, pos, member)     \
225
  for (cds_lfht_next(ht, iter),                           \
226
       pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \
227
          __typeof__(*(pos)), member);  \
228
       pos != NULL; /**/                                  \
229
       cds_lfht_next(ht, iter),                           \
230
       pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \
231
          __typeof__(*(pos)), member))
232
233
static void
234
bcentry_purge(struct cds_lfht *ht, struct cds_list_head *lru,
235
0
        isc_stdtime_t now) {
236
0
  size_t count = 10;
237
0
  dns_bcentry_t *bad;
238
0
  cds_list_for_each_entry_rcu(bad, lru, lru_head) {
239
0
    if (bcentry_alive(ht, bad, now)) {
240
0
      break;
241
0
    }
242
0
    if (--count == 0) {
243
0
      break;
244
0
    }
245
0
  }
246
0
}
247
248
void
249
dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name,
250
0
     dns_rdatatype_t type, uint32_t flags, isc_stdtime_t expire) {
251
0
  REQUIRE(VALID_BADCACHE(bc));
252
0
  REQUIRE(name != NULL);
253
254
0
  isc_loop_t *loop = isc_loop();
255
0
  isc_tid_t tid = isc_tid();
256
0
  struct cds_list_head *lru = &bc->lru[tid];
257
258
0
  isc_stdtime_t now = isc_stdtime_now();
259
0
  if (expire < now) {
260
0
    expire = now;
261
0
  }
262
263
0
  rcu_read_lock();
264
0
  struct cds_lfht *ht = rcu_dereference(bc->ht);
265
0
  INSIST(ht != NULL);
266
267
0
  dns__bckey_t key = {
268
0
    .name = name,
269
0
    .type = type,
270
0
  };
271
0
  uint32_t hashval = bcentry_hash(&key);
272
273
  /* struct cds_lfht_iter iter; */
274
0
  dns_bcentry_t *bad = bcentry_new(loop, name, type, flags, expire);
275
0
  struct cds_lfht_node *ht_node;
276
0
  do {
277
0
    ht_node = cds_lfht_add_unique(ht, hashval, bcentry_match, &key,
278
0
                &bad->ht_node);
279
0
    if (ht_node != &bad->ht_node) {
280
0
      dns_bcentry_t *found = caa_container_of(
281
0
        ht_node, dns_bcentry_t, ht_node);
282
0
      bcentry_evict(ht, found);
283
0
    }
284
0
  } while (ht_node != &bad->ht_node);
285
286
  /* No locking, instead we are using per-thread lists */
287
0
  cds_list_add_tail_rcu(&bad->lru_head, lru);
288
289
0
  bcentry_purge(ht, lru, now);
290
291
0
  rcu_read_unlock();
292
0
}
293
294
isc_result_t
295
dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name,
296
0
      dns_rdatatype_t type, uint32_t *flagp, isc_stdtime_t now) {
297
0
  REQUIRE(VALID_BADCACHE(bc));
298
0
  REQUIRE(name != NULL);
299
300
0
  isc_result_t result = ISC_R_NOTFOUND;
301
302
0
  rcu_read_lock();
303
0
  struct cds_lfht *ht = rcu_dereference(bc->ht);
304
0
  INSIST(ht != NULL);
305
306
0
  dns__bckey_t key = {
307
0
    .name = name,
308
0
    .type = type,
309
0
  };
310
0
  uint32_t hashval = bcentry_hash(&key);
311
312
0
  dns_bcentry_t *found = bcentry_lookup(ht, hashval, &key);
313
314
0
  if (found != NULL && bcentry_alive(ht, found, now)) {
315
0
    result = ISC_R_SUCCESS;
316
0
    if (flagp != NULL) {
317
0
      *flagp = found->flags;
318
0
    }
319
0
  }
320
321
0
  isc_tid_t tid = isc_tid();
322
0
  struct cds_list_head *lru = &bc->lru[tid];
323
0
  bcentry_purge(ht, lru, now);
324
325
0
  rcu_read_unlock();
326
327
0
  return result;
328
0
}
329
330
void
331
0
dns_badcache_flush(dns_badcache_t *bc) {
332
0
  REQUIRE(VALID_BADCACHE(bc));
333
334
0
  rcu_read_lock();
335
0
  struct cds_lfht *ht = rcu_dereference(bc->ht);
336
0
  INSIST(ht != NULL);
337
338
  /* Flush the hash table */
339
0
  dns_bcentry_t *bad;
340
0
  struct cds_lfht_iter iter;
341
0
  cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
342
0
    bcentry_evict(ht, bad);
343
0
  }
344
345
0
  rcu_read_unlock();
346
0
}
347
348
void
349
0
dns_badcache_flushname(dns_badcache_t *bc, const dns_name_t *name) {
350
0
  REQUIRE(VALID_BADCACHE(bc));
351
0
  REQUIRE(name != NULL);
352
353
0
  isc_stdtime_t now = isc_stdtime_now();
354
355
0
  rcu_read_lock();
356
0
  struct cds_lfht *ht = rcu_dereference(bc->ht);
357
0
  INSIST(ht != NULL);
358
359
0
  dns_bcentry_t *bad;
360
0
  struct cds_lfht_iter iter;
361
0
  cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
362
0
    if (dns_name_equal(&bad->name, name)) {
363
0
      bcentry_evict(ht, bad);
364
0
      continue;
365
0
    }
366
367
    /* Flush all the expired entries */
368
0
    (void)bcentry_alive(ht, bad, now);
369
0
  }
370
371
0
  rcu_read_unlock();
372
0
}
373
374
void
375
0
dns_badcache_flushtree(dns_badcache_t *bc, const dns_name_t *name) {
376
0
  REQUIRE(VALID_BADCACHE(bc));
377
0
  REQUIRE(name != NULL);
378
379
0
  isc_stdtime_t now = isc_stdtime_now();
380
381
0
  rcu_read_lock();
382
0
  struct cds_lfht *ht = rcu_dereference(bc->ht);
383
0
  INSIST(ht != NULL);
384
385
0
  dns_bcentry_t *bad;
386
0
  struct cds_lfht_iter iter;
387
0
  cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
388
0
    if (dns_name_issubdomain(&bad->name, name)) {
389
0
      bcentry_evict(ht, bad);
390
0
      continue;
391
0
    }
392
393
    /* Flush all the expired entries */
394
0
    (void)bcentry_alive(ht, bad, now);
395
0
  }
396
397
0
  rcu_read_unlock();
398
0
}
399
400
static void
401
0
bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp) {
402
0
  char namebuf[DNS_NAME_FORMATSIZE];
403
0
  char typebuf[DNS_RDATATYPE_FORMATSIZE];
404
405
0
  dns_name_format(&bad->name, namebuf, sizeof(namebuf));
406
0
  dns_rdatatype_format(bad->type, typebuf, sizeof(typebuf));
407
0
  fprintf(fp, "; %s/%s [ttl %" PRIu32 "]\n", namebuf, typebuf,
408
0
    bad->expire - now);
409
0
}
410
411
void
412
0
dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) {
413
0
  dns_bcentry_t *bad;
414
0
  isc_stdtime_t now = isc_stdtime_now();
415
416
0
  REQUIRE(VALID_BADCACHE(bc));
417
0
  REQUIRE(fp != NULL);
418
419
0
  fprintf(fp, ";\n; %s\n;\n", cachename);
420
421
0
  rcu_read_lock();
422
0
  struct cds_lfht *ht = rcu_dereference(bc->ht);
423
0
  INSIST(ht != NULL);
424
425
0
  struct cds_lfht_iter iter;
426
0
  cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
427
0
    if (bcentry_alive(ht, bad, now)) {
428
0
      bcentry_print(bad, now, fp);
429
0
    }
430
0
  }
431
432
0
  rcu_read_unlock();
433
0
}