1 1.1 christos /* $NetBSD: badcache.c,v 1.1 2024/02/18 20:57:30 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.1 christos * SPDX-License-Identifier: MPL-2.0 7 1.1 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.1 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos /*! \file */ 17 1.1 christos 18 1.1 christos #include <inttypes.h> 19 1.1 christos #include <stdbool.h> 20 1.1 christos 21 1.1 christos #include <isc/buffer.h> 22 1.1 christos #include <isc/hash.h> 23 1.1 christos #include <isc/log.h> 24 1.1 christos #include <isc/mem.h> 25 1.1 christos #include <isc/mutex.h> 26 1.1 christos #include <isc/platform.h> 27 1.1 christos #include <isc/print.h> 28 1.1 christos #include <isc/rwlock.h> 29 1.1 christos #include <isc/string.h> 30 1.1 christos #include <isc/time.h> 31 1.1 christos #include <isc/util.h> 32 1.1 christos 33 1.1 christos #include <dns/badcache.h> 34 1.1 christos #include <dns/name.h> 35 1.1 christos #include <dns/rdatatype.h> 36 1.1 christos #include <dns/types.h> 37 1.1 christos 38 1.1 christos typedef struct dns_bcentry dns_bcentry_t; 39 1.1 christos 40 1.1 christos struct dns_badcache { 41 1.1 christos unsigned int magic; 42 1.1 christos isc_rwlock_t lock; 43 1.1 christos isc_mem_t *mctx; 44 1.1 christos 45 1.1 christos isc_mutex_t *tlocks; 46 1.1 christos dns_bcentry_t **table; 47 1.1 christos 48 1.1 christos atomic_uint_fast32_t count; 49 1.1 christos atomic_uint_fast32_t sweep; 50 1.1 christos 51 1.1 christos unsigned int minsize; 52 1.1 christos unsigned int size; 53 1.1 christos }; 54 1.1 christos 55 1.1 christos #define BADCACHE_MAGIC ISC_MAGIC('B', 'd', 'C', 'a') 56 1.1 christos #define VALID_BADCACHE(m) ISC_MAGIC_VALID(m, BADCACHE_MAGIC) 57 1.1 christos 58 1.1 christos struct dns_bcentry { 59 1.1 christos dns_bcentry_t *next; 60 1.1 christos dns_rdatatype_t type; 61 1.1 christos isc_time_t expire; 62 1.1 christos uint32_t flags; 63 1.1 christos unsigned int hashval; 64 1.1 christos dns_name_t name; 65 1.1 christos }; 66 1.1 christos 67 1.1 christos static void 68 1.1 christos badcache_resize(dns_badcache_t *bc, isc_time_t *now); 69 1.1 christos 70 1.1 christos isc_result_t 71 1.1 christos dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp) { 72 1.1 christos dns_badcache_t *bc = NULL; 73 1.1 christos unsigned int i; 74 1.1 christos 75 1.1 christos REQUIRE(bcp != NULL && *bcp == NULL); 76 1.1 christos REQUIRE(mctx != NULL); 77 1.1 christos 78 1.1 christos bc = isc_mem_get(mctx, sizeof(dns_badcache_t)); 79 1.1 christos memset(bc, 0, sizeof(dns_badcache_t)); 80 1.1 christos 81 1.1 christos isc_mem_attach(mctx, &bc->mctx); 82 1.1 christos isc_rwlock_init(&bc->lock, 0, 0); 83 1.1 christos 84 1.1 christos bc->table = isc_mem_get(bc->mctx, sizeof(*bc->table) * size); 85 1.1 christos bc->tlocks = isc_mem_get(bc->mctx, sizeof(isc_mutex_t) * size); 86 1.1 christos for (i = 0; i < size; i++) { 87 1.1 christos isc_mutex_init(&bc->tlocks[i]); 88 1.1 christos } 89 1.1 christos bc->size = bc->minsize = size; 90 1.1 christos memset(bc->table, 0, bc->size * sizeof(dns_bcentry_t *)); 91 1.1 christos 92 1.1 christos atomic_init(&bc->count, 0); 93 1.1 christos atomic_init(&bc->sweep, 0); 94 1.1 christos bc->magic = BADCACHE_MAGIC; 95 1.1 christos 96 1.1 christos *bcp = bc; 97 1.1 christos return (ISC_R_SUCCESS); 98 1.1 christos } 99 1.1 christos 100 1.1 christos void 101 1.1 christos dns_badcache_destroy(dns_badcache_t **bcp) { 102 1.1 christos dns_badcache_t *bc; 103 1.1 christos unsigned int i; 104 1.1 christos 105 1.1 christos REQUIRE(bcp != NULL && *bcp != NULL); 106 1.1 christos bc = *bcp; 107 1.1 christos *bcp = NULL; 108 1.1 christos 109 1.1 christos dns_badcache_flush(bc); 110 1.1 christos 111 1.1 christos bc->magic = 0; 112 1.1 christos isc_rwlock_destroy(&bc->lock); 113 1.1 christos for (i = 0; i < bc->size; i++) { 114 1.1 christos isc_mutex_destroy(&bc->tlocks[i]); 115 1.1 christos } 116 1.1 christos isc_mem_put(bc->mctx, bc->table, sizeof(dns_bcentry_t *) * bc->size); 117 1.1 christos isc_mem_put(bc->mctx, bc->tlocks, sizeof(isc_mutex_t) * bc->size); 118 1.1 christos isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t)); 119 1.1 christos } 120 1.1 christos 121 1.1 christos static void 122 1.1 christos badcache_resize(dns_badcache_t *bc, isc_time_t *now) { 123 1.1 christos dns_bcentry_t **newtable, *bad, *next; 124 1.1 christos isc_mutex_t *newlocks; 125 1.1 christos unsigned int newsize, i; 126 1.1 christos bool grow; 127 1.1 christos 128 1.1 christos RWLOCK(&bc->lock, isc_rwlocktype_write); 129 1.1 christos 130 1.1 christos /* 131 1.1 christos * XXXWPK we will have a thundering herd problem here, 132 1.1 christos * as all threads will wait on the RWLOCK when there's 133 1.1 christos * a need to resize badcache. 134 1.1 christos * However, it happens so rarely it should not be a 135 1.1 christos * performance issue. This is because we double the 136 1.1 christos * size every time we grow it, and we don't shrink 137 1.1 christos * unless the number of entries really shrunk. In a 138 1.1 christos * high load situation, the number of badcache entries 139 1.1 christos * will eventually stabilize. 140 1.1 christos */ 141 1.1 christos if (atomic_load_relaxed(&bc->count) > bc->size * 8) { 142 1.1 christos grow = true; 143 1.1 christos } else if (atomic_load_relaxed(&bc->count) < bc->size * 2 && 144 1.1 christos bc->size > bc->minsize) 145 1.1 christos { 146 1.1 christos grow = false; 147 1.1 christos } else { 148 1.1 christos /* Someone resized it already, bail. */ 149 1.1 christos RWUNLOCK(&bc->lock, isc_rwlocktype_write); 150 1.1 christos return; 151 1.1 christos } 152 1.1 christos 153 1.1 christos if (grow) { 154 1.1 christos newsize = bc->size * 2 + 1; 155 1.1 christos } else { 156 1.1 christos newsize = (bc->size - 1) / 2; 157 1.1 christos #ifdef __clang_analyzer__ 158 1.1 christos /* 159 1.1 christos * XXXWPK there's a bug in clang static analyzer - 160 1.1 christos * `value % newsize` is considered undefined even though 161 1.1 christos * we check if newsize is larger than 0. This helps. 162 1.1 christos */ 163 1.1 christos newsize += 1; 164 1.1 christos #endif 165 1.1 christos } 166 1.1 christos RUNTIME_CHECK(newsize > 0); 167 1.1 christos 168 1.1 christos newtable = isc_mem_get(bc->mctx, sizeof(dns_bcentry_t *) * newsize); 169 1.1 christos memset(newtable, 0, sizeof(dns_bcentry_t *) * newsize); 170 1.1 christos 171 1.1 christos newlocks = isc_mem_get(bc->mctx, sizeof(isc_mutex_t) * newsize); 172 1.1 christos 173 1.1 christos /* Copy existing mutexes */ 174 1.1 christos for (i = 0; i < newsize && i < bc->size; i++) { 175 1.1 christos newlocks[i] = bc->tlocks[i]; 176 1.1 christos } 177 1.1 christos /* Initialize additional mutexes if we're growing */ 178 1.1 christos for (i = bc->size; i < newsize; i++) { 179 1.1 christos isc_mutex_init(&newlocks[i]); 180 1.1 christos } 181 1.1 christos /* Destroy extra mutexes if we're shrinking */ 182 1.1 christos for (i = newsize; i < bc->size; i++) { 183 1.1 christos isc_mutex_destroy(&bc->tlocks[i]); 184 1.1 christos } 185 1.1 christos 186 1.1 christos for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) { 187 1.1 christos for (bad = bc->table[i]; bad != NULL; bad = next) { 188 1.1 christos next = bad->next; 189 1.1 christos if (isc_time_compare(&bad->expire, now) < 0) { 190 1.1 christos isc_mem_put(bc->mctx, bad, 191 1.1 christos sizeof(*bad) + bad->name.length); 192 1.1 christos atomic_fetch_sub_relaxed(&bc->count, 1); 193 1.1 christos } else { 194 1.1 christos bad->next = newtable[bad->hashval % newsize]; 195 1.1 christos newtable[bad->hashval % newsize] = bad; 196 1.1 christos } 197 1.1 christos } 198 1.1 christos bc->table[i] = NULL; 199 1.1 christos } 200 1.1 christos 201 1.1 christos isc_mem_put(bc->mctx, bc->tlocks, sizeof(isc_mutex_t) * bc->size); 202 1.1 christos bc->tlocks = newlocks; 203 1.1 christos 204 1.1 christos isc_mem_put(bc->mctx, bc->table, sizeof(*bc->table) * bc->size); 205 1.1 christos bc->size = newsize; 206 1.1 christos bc->table = newtable; 207 1.1 christos 208 1.1 christos RWUNLOCK(&bc->lock, isc_rwlocktype_write); 209 1.1 christos } 210 1.1 christos 211 1.1 christos void 212 1.1 christos dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name, 213 1.1 christos dns_rdatatype_t type, bool update, uint32_t flags, 214 1.1 christos isc_time_t *expire) { 215 1.1 christos isc_result_t result; 216 1.1 christos unsigned int hashval, hash; 217 1.1 christos dns_bcentry_t *bad, *prev, *next; 218 1.1 christos isc_time_t now; 219 1.1 christos bool resize = false; 220 1.1 christos 221 1.1 christos REQUIRE(VALID_BADCACHE(bc)); 222 1.1 christos REQUIRE(name != NULL); 223 1.1 christos REQUIRE(expire != NULL); 224 1.1 christos 225 1.1 christos RWLOCK(&bc->lock, isc_rwlocktype_read); 226 1.1 christos 227 1.1 christos result = isc_time_now(&now); 228 1.1 christos if (result != ISC_R_SUCCESS) { 229 1.1 christos isc_time_settoepoch(&now); 230 1.1 christos } 231 1.1 christos 232 1.1 christos hashval = dns_name_hash(name, false); 233 1.1 christos hash = hashval % bc->size; 234 1.1 christos LOCK(&bc->tlocks[hash]); 235 1.1 christos prev = NULL; 236 1.1 christos for (bad = bc->table[hash]; bad != NULL; bad = next) { 237 1.1 christos next = bad->next; 238 1.1 christos if (bad->type == type && dns_name_equal(name, &bad->name)) { 239 1.1 christos if (update) { 240 1.1 christos bad->expire = *expire; 241 1.1 christos bad->flags = flags; 242 1.1 christos } 243 1.1 christos break; 244 1.1 christos } 245 1.1 christos if (isc_time_compare(&bad->expire, &now) < 0) { 246 1.1 christos if (prev == NULL) { 247 1.1 christos bc->table[hash] = bad->next; 248 1.1 christos } else { 249 1.1 christos prev->next = bad->next; 250 1.1 christos } 251 1.1 christos isc_mem_put(bc->mctx, bad, 252 1.1 christos sizeof(*bad) + bad->name.length); 253 1.1 christos atomic_fetch_sub_relaxed(&bc->count, 1); 254 1.1 christos } else { 255 1.1 christos prev = bad; 256 1.1 christos } 257 1.1 christos } 258 1.1 christos 259 1.1 christos if (bad == NULL) { 260 1.1 christos isc_buffer_t buffer; 261 1.1 christos bad = isc_mem_get(bc->mctx, sizeof(*bad) + name->length); 262 1.1 christos bad->type = type; 263 1.1 christos bad->hashval = hashval; 264 1.1 christos bad->expire = *expire; 265 1.1 christos bad->flags = flags; 266 1.1 christos isc_buffer_init(&buffer, bad + 1, name->length); 267 1.1 christos dns_name_init(&bad->name, NULL); 268 1.1 christos dns_name_copy(name, &bad->name, &buffer); 269 1.1 christos bad->next = bc->table[hash]; 270 1.1 christos bc->table[hash] = bad; 271 1.1 christos unsigned count = atomic_fetch_add_relaxed(&bc->count, 1); 272 1.1 christos if ((count > bc->size * 8) || 273 1.1 christos (count < bc->size * 2 && bc->size > bc->minsize)) 274 1.1 christos { 275 1.1 christos resize = true; 276 1.1 christos } 277 1.1 christos } else { 278 1.1 christos bad->expire = *expire; 279 1.1 christos } 280 1.1 christos 281 1.1 christos UNLOCK(&bc->tlocks[hash]); 282 1.1 christos RWUNLOCK(&bc->lock, isc_rwlocktype_read); 283 1.1 christos if (resize) { 284 1.1 christos badcache_resize(bc, &now); 285 1.1 christos } 286 1.1 christos } 287 1.1 christos 288 1.1 christos bool 289 1.1 christos dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name, 290 1.1 christos dns_rdatatype_t type, uint32_t *flagp, isc_time_t *now) { 291 1.1 christos dns_bcentry_t *bad, *prev, *next; 292 1.1 christos bool answer = false; 293 1.1 christos unsigned int i; 294 1.1 christos unsigned int hash; 295 1.1 christos 296 1.1 christos REQUIRE(VALID_BADCACHE(bc)); 297 1.1 christos REQUIRE(name != NULL); 298 1.1 christos REQUIRE(now != NULL); 299 1.1 christos 300 1.1 christos RWLOCK(&bc->lock, isc_rwlocktype_read); 301 1.1 christos 302 1.1 christos /* 303 1.1 christos * XXXMUKS: dns_name_equal() is expensive as it does a 304 1.1 christos * octet-by-octet comparison, and it can be made better in two 305 1.1 christos * ways here. First, lowercase the names (use 306 1.1 christos * dns_name_downcase() instead of dns_name_copy() in 307 1.1 christos * dns_badcache_add()) so that dns_name_caseequal() can be used 308 1.1 christos * which the compiler will emit as SIMD instructions. Second, 309 1.1 christos * don't put multiple copies of the same name in the chain (or 310 1.1 christos * multiple names will have to be matched for equality), but use 311 1.1 christos * name->link to store the type specific part. 312 1.1 christos */ 313 1.1 christos 314 1.1 christos if (atomic_load_relaxed(&bc->count) == 0) { 315 1.1 christos goto skip; 316 1.1 christos } 317 1.1 christos 318 1.1 christos hash = dns_name_hash(name, false) % bc->size; 319 1.1 christos prev = NULL; 320 1.1 christos LOCK(&bc->tlocks[hash]); 321 1.1 christos for (bad = bc->table[hash]; bad != NULL; bad = next) { 322 1.1 christos next = bad->next; 323 1.1 christos /* 324 1.1 christos * Search the hash list. Clean out expired records as we go. 325 1.1 christos */ 326 1.1 christos if (isc_time_compare(&bad->expire, now) < 0) { 327 1.1 christos if (prev != NULL) { 328 1.1 christos prev->next = bad->next; 329 1.1 christos } else { 330 1.1 christos bc->table[hash] = bad->next; 331 1.1 christos } 332 1.1 christos 333 1.1 christos isc_mem_put(bc->mctx, bad, 334 1.1 christos sizeof(*bad) + bad->name.length); 335 1.1 christos atomic_fetch_sub(&bc->count, 1); 336 1.1 christos continue; 337 1.1 christos } 338 1.1 christos if (bad->type == type && dns_name_equal(name, &bad->name)) { 339 1.1 christos if (flagp != NULL) { 340 1.1 christos *flagp = bad->flags; 341 1.1 christos } 342 1.1 christos answer = true; 343 1.1 christos break; 344 1.1 christos } 345 1.1 christos prev = bad; 346 1.1 christos } 347 1.1 christos UNLOCK(&bc->tlocks[hash]); 348 1.1 christos skip: 349 1.1 christos 350 1.1 christos /* 351 1.1 christos * Slow sweep to clean out stale records. 352 1.1 christos */ 353 1.1 christos i = atomic_fetch_add(&bc->sweep, 1) % bc->size; 354 1.1 christos if (isc_mutex_trylock(&bc->tlocks[i]) == ISC_R_SUCCESS) { 355 1.1 christos bad = bc->table[i]; 356 1.1 christos if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) { 357 1.1 christos bc->table[i] = bad->next; 358 1.1 christos isc_mem_put(bc->mctx, bad, 359 1.1 christos sizeof(*bad) + bad->name.length); 360 1.1 christos atomic_fetch_sub_relaxed(&bc->count, 1); 361 1.1 christos } 362 1.1 christos UNLOCK(&bc->tlocks[i]); 363 1.1 christos } 364 1.1 christos 365 1.1 christos RWUNLOCK(&bc->lock, isc_rwlocktype_read); 366 1.1 christos return (answer); 367 1.1 christos } 368 1.1 christos 369 1.1 christos void 370 1.1 christos dns_badcache_flush(dns_badcache_t *bc) { 371 1.1 christos dns_bcentry_t *entry, *next; 372 1.1 christos unsigned int i; 373 1.1 christos 374 1.1 christos RWLOCK(&bc->lock, isc_rwlocktype_write); 375 1.1 christos REQUIRE(VALID_BADCACHE(bc)); 376 1.1 christos 377 1.1 christos for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) { 378 1.1 christos for (entry = bc->table[i]; entry != NULL; entry = next) { 379 1.1 christos next = entry->next; 380 1.1 christos isc_mem_put(bc->mctx, entry, 381 1.1 christos sizeof(*entry) + entry->name.length); 382 1.1 christos atomic_fetch_sub_relaxed(&bc->count, 1); 383 1.1 christos } 384 1.1 christos bc->table[i] = NULL; 385 1.1 christos } 386 1.1 christos RWUNLOCK(&bc->lock, isc_rwlocktype_write); 387 1.1 christos } 388 1.1 christos 389 1.1 christos void 390 1.1 christos dns_badcache_flushname(dns_badcache_t *bc, const dns_name_t *name) { 391 1.1 christos dns_bcentry_t *bad, *prev, *next; 392 1.1 christos isc_result_t result; 393 1.1 christos isc_time_t now; 394 1.1 christos unsigned int hash; 395 1.1 christos 396 1.1 christos REQUIRE(VALID_BADCACHE(bc)); 397 1.1 christos REQUIRE(name != NULL); 398 1.1 christos 399 1.1 christos RWLOCK(&bc->lock, isc_rwlocktype_read); 400 1.1 christos 401 1.1 christos result = isc_time_now(&now); 402 1.1 christos if (result != ISC_R_SUCCESS) { 403 1.1 christos isc_time_settoepoch(&now); 404 1.1 christos } 405 1.1 christos hash = dns_name_hash(name, false) % bc->size; 406 1.1 christos LOCK(&bc->tlocks[hash]); 407 1.1 christos prev = NULL; 408 1.1 christos for (bad = bc->table[hash]; bad != NULL; bad = next) { 409 1.1 christos int n; 410 1.1 christos next = bad->next; 411 1.1 christos n = isc_time_compare(&bad->expire, &now); 412 1.1 christos if (n < 0 || dns_name_equal(name, &bad->name)) { 413 1.1 christos if (prev == NULL) { 414 1.1 christos bc->table[hash] = bad->next; 415 1.1 christos } else { 416 1.1 christos prev->next = bad->next; 417 1.1 christos } 418 1.1 christos 419 1.1 christos isc_mem_put(bc->mctx, bad, 420 1.1 christos sizeof(*bad) + bad->name.length); 421 1.1 christos atomic_fetch_sub_relaxed(&bc->count, 1); 422 1.1 christos } else { 423 1.1 christos prev = bad; 424 1.1 christos } 425 1.1 christos } 426 1.1 christos UNLOCK(&bc->tlocks[hash]); 427 1.1 christos 428 1.1 christos RWUNLOCK(&bc->lock, isc_rwlocktype_read); 429 1.1 christos } 430 1.1 christos 431 1.1 christos void 432 1.1 christos dns_badcache_flushtree(dns_badcache_t *bc, const dns_name_t *name) { 433 1.1 christos dns_bcentry_t *bad, *prev, *next; 434 1.1 christos unsigned int i; 435 1.1 christos int n; 436 1.1 christos isc_time_t now; 437 1.1 christos isc_result_t result; 438 1.1 christos 439 1.1 christos REQUIRE(VALID_BADCACHE(bc)); 440 1.1 christos REQUIRE(name != NULL); 441 1.1 christos 442 1.1 christos /* 443 1.1 christos * We write lock the tree to avoid relocking every node 444 1.1 christos * individually. 445 1.1 christos */ 446 1.1 christos RWLOCK(&bc->lock, isc_rwlocktype_write); 447 1.1 christos 448 1.1 christos result = isc_time_now(&now); 449 1.1 christos if (result != ISC_R_SUCCESS) { 450 1.1 christos isc_time_settoepoch(&now); 451 1.1 christos } 452 1.1 christos 453 1.1 christos for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) { 454 1.1 christos prev = NULL; 455 1.1 christos for (bad = bc->table[i]; bad != NULL; bad = next) { 456 1.1 christos next = bad->next; 457 1.1 christos n = isc_time_compare(&bad->expire, &now); 458 1.1 christos if (n < 0 || dns_name_issubdomain(&bad->name, name)) { 459 1.1 christos if (prev == NULL) { 460 1.1 christos bc->table[i] = bad->next; 461 1.1 christos } else { 462 1.1 christos prev->next = bad->next; 463 1.1 christos } 464 1.1 christos 465 1.1 christos isc_mem_put(bc->mctx, bad, 466 1.1 christos sizeof(*bad) + bad->name.length); 467 1.1 christos atomic_fetch_sub_relaxed(&bc->count, 1); 468 1.1 christos } else { 469 1.1 christos prev = bad; 470 1.1 christos } 471 1.1 christos } 472 1.1 christos } 473 1.1 christos 474 1.1 christos RWUNLOCK(&bc->lock, isc_rwlocktype_write); 475 1.1 christos } 476 1.1 christos 477 1.1 christos void 478 1.1 christos dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) { 479 1.1 christos char namebuf[DNS_NAME_FORMATSIZE]; 480 1.1 christos char typebuf[DNS_RDATATYPE_FORMATSIZE]; 481 1.1 christos dns_bcentry_t *bad, *next, *prev; 482 1.1 christos isc_time_t now; 483 1.1 christos unsigned int i; 484 1.1 christos uint64_t t; 485 1.1 christos 486 1.1 christos REQUIRE(VALID_BADCACHE(bc)); 487 1.1 christos REQUIRE(cachename != NULL); 488 1.1 christos REQUIRE(fp != NULL); 489 1.1 christos 490 1.1 christos /* 491 1.1 christos * We write lock the tree to avoid relocking every node 492 1.1 christos * individually. 493 1.1 christos */ 494 1.1 christos RWLOCK(&bc->lock, isc_rwlocktype_write); 495 1.1 christos fprintf(fp, ";\n; %s\n;\n", cachename); 496 1.1 christos 497 1.1 christos TIME_NOW(&now); 498 1.1 christos for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) { 499 1.1 christos prev = NULL; 500 1.1 christos for (bad = bc->table[i]; bad != NULL; bad = next) { 501 1.1 christos next = bad->next; 502 1.1 christos if (isc_time_compare(&bad->expire, &now) < 0) { 503 1.1 christos if (prev != NULL) { 504 1.1 christos prev->next = bad->next; 505 1.1 christos } else { 506 1.1 christos bc->table[i] = bad->next; 507 1.1 christos } 508 1.1 christos 509 1.1 christos isc_mem_put(bc->mctx, bad, 510 1.1 christos sizeof(*bad) + bad->name.length); 511 1.1 christos atomic_fetch_sub_relaxed(&bc->count, 1); 512 1.1 christos continue; 513 1.1 christos } 514 1.1 christos prev = bad; 515 1.1 christos dns_name_format(&bad->name, namebuf, sizeof(namebuf)); 516 1.1 christos dns_rdatatype_format(bad->type, typebuf, 517 1.1 christos sizeof(typebuf)); 518 1.1 christos t = isc_time_microdiff(&bad->expire, &now); 519 1.1 christos t /= 1000; 520 1.1 christos fprintf(fp, 521 1.1 christos "; %s/%s [ttl " 522 1.1 christos "%" PRIu64 "]\n", 523 1.1 christos namebuf, typebuf, t); 524 1.1 christos } 525 1.1 christos } 526 1.1 christos RWUNLOCK(&bc->lock, isc_rwlocktype_write); 527 1.1 christos } 528