Coverage Report

Created: 2026-06-15 06:55

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