# not add it automatically - we need to add it explicitly in such case.
#
PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR >= 0.13.0 liburcu-cds >= 0.13.0], [],
- [PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR liburcu-cds],
+ [PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR >= 0.10.0 liburcu-cds >= 0.10.0],
[LIBURCU_LIBS="$LIBURCU_LIBS -lurcu-common"])])
AC_DEFINE_UNQUOTED([RCU_FLAVOR], ["$RCU_FLAVOR"], [Chosen Userspace-RCU flavor])
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/mutex.h>
+#include <isc/refcount.h>
#include <isc/rwlock.h>
+#include <isc/spinlock.h>
+#include <isc/stdtime.h>
#include <isc/string.h>
+#include <isc/thread.h>
#include <isc/time.h>
+#include <isc/urcu.h>
#include <isc/util.h>
#include <dns/badcache.h>
struct dns_badcache {
unsigned int magic;
- isc_rwlock_t lock;
isc_mem_t *mctx;
-
- isc_mutex_t *tlocks;
- dns_bcentry_t **table;
-
- atomic_uint_fast32_t count;
- atomic_uint_fast32_t sweep;
-
- unsigned int minsize;
- unsigned int size;
+ struct cds_lfht *ht;
+ atomic_bool purge_in_progress;
};
#define BADCACHE_MAGIC ISC_MAGIC('B', 'd', 'C', 'a')
#define VALID_BADCACHE(m) ISC_MAGIC_VALID(m, BADCACHE_MAGIC)
+#define BADCACHE_INIT_SIZE (1 << 10) /* Must be power of 2 */
+#define BADCACHE_MIN_SIZE (1 << 8) /* Must be power of 2 */
+
struct dns_bcentry {
- dns_bcentry_t *next;
+ isc_mem_t *mctx;
dns_rdatatype_t type;
- isc_time_t expire;
- uint32_t flags;
- unsigned int hashval;
+ _Atomic(isc_stdtime_t) expire;
+ atomic_uint_fast32_t flags;
dns_fixedname_t fname;
dns_name_t *name;
+
+ struct cds_lfht_node ht_node;
+ struct rcu_head rcu_head;
};
static void
-badcache_resize(dns_badcache_t *bc, isc_time_t *now);
+bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp);
-isc_result_t
-dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp) {
- dns_badcache_t *bc = NULL;
- unsigned int i;
+static void
+bcentry_destroy(struct rcu_head *rcu_head);
- REQUIRE(bcp != NULL && *bcp == NULL);
+dns_badcache_t *
+dns_badcache_new(isc_mem_t *mctx) {
REQUIRE(mctx != NULL);
- bc = isc_mem_get(mctx, sizeof(*bc));
-
+ dns_badcache_t *bc = isc_mem_get(mctx, sizeof(*bc));
*bc = (dns_badcache_t){
- .size = size,
- .minsize = size,
+ .magic = BADCACHE_MAGIC,
};
- isc_mem_attach(mctx, &bc->mctx);
- isc_rwlock_init(&bc->lock);
-
- bc->table = isc_mem_getx(bc->mctx, sizeof(bc->table[0]) * size,
- ISC_MEM_ZERO);
- bc->tlocks = isc_mem_getx(bc->mctx, sizeof(bc->tlocks[0]) * size,
- ISC_MEM_ZERO);
- for (i = 0; i < size; i++) {
- isc_mutex_init(&bc->tlocks[i]);
- }
+ bc->ht = cds_lfht_new(BADCACHE_INIT_SIZE, BADCACHE_MIN_SIZE, 0,
+ CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+ INSIST(bc->ht != NULL);
- bc->magic = BADCACHE_MAGIC;
+ isc_mem_attach(mctx, &bc->mctx);
- *bcp = bc;
- return (ISC_R_SUCCESS);
+ return (bc);
}
void
dns_badcache_destroy(dns_badcache_t **bcp) {
- dns_badcache_t *bc;
- unsigned int i;
-
REQUIRE(bcp != NULL && *bcp != NULL);
- bc = *bcp;
- *bcp = NULL;
-
- dns_badcache_flush(bc);
+ REQUIRE(VALID_BADCACHE(*bcp));
+ dns_badcache_t *bc = *bcp;
+ *bcp = NULL;
bc->magic = 0;
- isc_rwlock_destroy(&bc->lock);
- for (i = 0; i < bc->size; i++) {
- isc_mutex_destroy(&bc->tlocks[i]);
+
+ dns_bcentry_t *bad = NULL;
+ struct cds_lfht_iter iter;
+ cds_lfht_for_each_entry(bc->ht, &iter, bad, ht_node) {
+ INSIST(!cds_lfht_del(bc->ht, &bad->ht_node));
+ bcentry_destroy(&bad->rcu_head);
}
- isc_mem_put(bc->mctx, bc->table, sizeof(bc->table[0]) * bc->size);
- isc_mem_put(bc->mctx, bc->tlocks, sizeof(bc->tlocks[0]) * bc->size);
+ RUNTIME_CHECK(!cds_lfht_destroy(bc->ht, NULL));
+
isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t));
}
+static int
+bcentry_match(struct cds_lfht_node *ht_node, const void *key) {
+ const dns_name_t *name = key;
+ dns_bcentry_t *bad = caa_container_of(ht_node, dns_bcentry_t, ht_node);
+
+ return (dns_name_equal(bad->name, name));
+}
+
+static dns_bcentry_t *
+bcentry_new(dns_badcache_t *bc, const dns_name_t *name,
+ const dns_rdatatype_t type, const uint32_t flags,
+ const isc_stdtime_t expire) {
+ dns_bcentry_t *bad = isc_mem_get(bc->mctx, sizeof(*bad));
+ *bad = (dns_bcentry_t){
+ .type = type,
+ .flags = flags,
+ .expire = expire,
+ };
+ isc_mem_attach(bc->mctx, &bad->mctx);
+
+ bad->name = dns_fixedname_initname(&bad->fname);
+ dns_name_copy(name, bad->name);
+
+ return (bad);
+}
+
static void
-badcache_resize(dns_badcache_t *bc, isc_time_t *now) {
- dns_bcentry_t **newtable, *bad, *next;
- isc_mutex_t *newlocks;
- unsigned int newsize, i;
- bool grow;
+bcentry_destroy(struct rcu_head *rcu_head) {
+ dns_bcentry_t *bad = caa_container_of(rcu_head, dns_bcentry_t,
+ rcu_head);
- RWLOCK(&bc->lock, isc_rwlocktype_write);
+ isc_mem_putanddetach(&bad->mctx, bad, sizeof(*bad));
+}
+static void
+bcentry_evict(struct cds_lfht *ht, dns_bcentry_t *bad) {
/*
- * XXXWPK we will have a thundering herd problem here,
- * as all threads will wait on the RWLOCK when there's
- * a need to resize badcache.
- * However, it happens so rarely it should not be a
- * performance issue. This is because we double the
- * size every time we grow it, and we don't shrink
- * unless the number of entries really shrunk. In a
- * high load situation, the number of badcache entries
- * will eventually stabilize.
+ * The hashtable isn't locked in a traditional sense, so multiple
+ * threads can lookup and evict the same record at the same time.
+ *
+ * This is amplified by the bcentry_purge_next() that walks a few more
+ * records in the hashtable and evicts them if they are expired.
+ *
+ * We need to destroy the bcentry only once - from the thread that has
+ * deleted the entry from the hashtable, all other calls to this
+ * function were redundant.
*/
- if (atomic_load_relaxed(&bc->count) > bc->size * 8) {
- grow = true;
- } else if (atomic_load_relaxed(&bc->count) < bc->size * 2 &&
- bc->size > bc->minsize)
- {
- grow = false;
- } else {
- /* Someone resized it already, bail. */
- RWUNLOCK(&bc->lock, isc_rwlocktype_write);
- return;
+ if (!cds_lfht_del(ht, &bad->ht_node)) {
+ call_rcu(&bad->rcu_head, bcentry_destroy);
}
+}
- if (grow) {
- newsize = bc->size * 2 + 1;
- } else {
- newsize = (bc->size - 1) / 2;
-#ifdef __clang_analyzer__
- /*
- * XXXWPK there's a bug in clang static analyzer -
- * `value % newsize` is considered undefined even though
- * we check if newsize is larger than 0. This helps.
- */
- newsize += 1;
-#endif
+static bool
+bcentry_alive(struct cds_lfht *ht, dns_bcentry_t *bad, isc_stdtime_t now) {
+ if (cds_lfht_is_node_deleted(&bad->ht_node)) {
+ return (false);
+ } else if (atomic_load_relaxed(&bad->expire) < now) {
+ bcentry_evict(ht, bad);
+ return (false);
}
- RUNTIME_CHECK(newsize > 0);
- newtable = isc_mem_getx(bc->mctx, sizeof(dns_bcentry_t *) * newsize,
- ISC_MEM_ZERO);
+ return (true);
+}
- newlocks = isc_mem_get(bc->mctx, sizeof(isc_mutex_t) * newsize);
+#define cds_lfht_for_each_entry_next(ht, iter, pos, member) \
+ for (cds_lfht_next(ht, iter), \
+ pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \
+ __typeof__(*(pos)), member); \
+ pos != NULL; /**/ \
+ cds_lfht_next(ht, iter), \
+ pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \
+ __typeof__(*(pos)), member))
- /* Copy existing mutexes */
- for (i = 0; i < newsize && i < bc->size; i++) {
- newlocks[i] = bc->tlocks[i];
- }
- /* Initialize additional mutexes if we're growing */
- for (i = bc->size; i < newsize; i++) {
- isc_mutex_init(&newlocks[i]);
- }
- /* Destroy extra mutexes if we're shrinking */
- for (i = newsize; i < bc->size; i++) {
- isc_mutex_destroy(&bc->tlocks[i]);
- }
-
- for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) {
- for (bad = bc->table[i]; bad != NULL; bad = next) {
- next = bad->next;
- if (isc_time_compare(&bad->expire, now) < 0) {
- isc_mem_put(bc->mctx, bad, sizeof(*bad));
- atomic_fetch_sub_relaxed(&bc->count, 1);
- } else {
- bad->next = newtable[bad->hashval % newsize];
- newtable[bad->hashval % newsize] = bad;
- }
+static void
+bcentry_purge_next(struct cds_lfht *ht, struct cds_lfht_iter *iter,
+ isc_stdtime_t now) {
+ /* Lazy-purge the table */
+ size_t count = 10;
+ dns_bcentry_t *bad;
+ cds_lfht_for_each_entry_next(ht, iter, bad, ht_node) {
+ if (!bcentry_alive(ht, bad, now)) {
+ break;
+ }
+ if (--count == 0) {
+ break;
}
- bc->table[i] = NULL;
}
-
- isc_mem_put(bc->mctx, bc->tlocks, sizeof(isc_mutex_t) * bc->size);
- bc->tlocks = newlocks;
-
- isc_mem_put(bc->mctx, bc->table, sizeof(*bc->table) * bc->size);
- bc->size = newsize;
- bc->table = newtable;
-
- RWUNLOCK(&bc->lock, isc_rwlocktype_write);
}
void
dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name,
dns_rdatatype_t type, bool update, uint32_t flags,
- isc_time_t *expire) {
- unsigned int hashval, hash;
- dns_bcentry_t *bad, *prev, *next;
- isc_time_t now;
- bool resize = false;
-
+ isc_stdtime_t expire) {
REQUIRE(VALID_BADCACHE(bc));
REQUIRE(name != NULL);
- REQUIRE(expire != NULL);
-
- RWLOCK(&bc->lock, isc_rwlocktype_read);
-
- now = isc_time_now();
-
- hashval = dns_name_hash(name);
- hash = hashval % bc->size;
- LOCK(&bc->tlocks[hash]);
- prev = NULL;
- for (bad = bc->table[hash]; bad != NULL; bad = next) {
- next = bad->next;
- if (bad->type == type && dns_name_equal(name, bad->name)) {
- if (update) {
- bad->expire = *expire;
- bad->flags = flags;
- }
- break;
- }
- if (isc_time_compare(&bad->expire, &now) < 0) {
- if (prev == NULL) {
- bc->table[hash] = bad->next;
- } else {
- prev->next = bad->next;
- }
- isc_mem_put(bc->mctx, bad, sizeof(*bad));
- atomic_fetch_sub_relaxed(&bc->count, 1);
- } else {
- prev = bad;
- }
+
+ isc_stdtime_t now = isc_stdtime_now();
+ if (expire < now) {
+ expire = now;
}
- if (bad == NULL) {
- unsigned int count;
- isc_buffer_t buffer;
-
- bad = isc_mem_get(bc->mctx, sizeof(*bad));
- *bad = (dns_bcentry_t){ .type = type,
- .hashval = hashval,
- .expire = *expire,
- .flags = flags,
- .next = bc->table[hash] };
-
- isc_buffer_init(&buffer, bad + 1, name->length);
- bad->name = dns_fixedname_initname(&bad->fname);
- dns_name_copy(name, bad->name);
- bc->table[hash] = bad;
-
- count = atomic_fetch_add_relaxed(&bc->count, 1);
- if ((count > bc->size * 8) ||
- (count < bc->size * 2 && bc->size > bc->minsize))
- {
- resize = true;
+ rcu_read_lock();
+ struct cds_lfht *ht = rcu_dereference(bc->ht);
+ INSIST(ht != NULL);
+
+ dns_bcentry_t *bad = NULL;
+ uint32_t hashval = dns_name_hash(name);
+
+ struct cds_lfht_iter iter;
+ dns_bcentry_t *found = NULL;
+ cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name,
+ &iter, bad, ht_node) {
+ if (bcentry_alive(ht, bad, now) && bad->type == type) {
+ found = bad;
+ /*
+ * We could bail-out on first match, but:
+ * 1. there could be duplicate .type entries
+ * 2. we want to check expire for all entries
+ */
}
- } else {
- bad->expire = *expire;
}
- UNLOCK(&bc->tlocks[hash]);
- RWUNLOCK(&bc->lock, isc_rwlocktype_read);
- if (resize) {
- badcache_resize(bc, &now);
+ if (found == NULL) {
+ /*
+ * In theory, this could result in multiple entries for the same
+ * type, but we don't care much, as they are going to be roughly
+ * the same, and the last will always trump and the former
+ * entries will expire (see above).
+ */
+ bad = bcentry_new(bc, name, type, flags, expire);
+ cds_lfht_add(ht, hashval, &bad->ht_node);
+ } else if (update) {
+ atomic_store_relaxed(&found->expire, expire);
+ atomic_store_relaxed(&found->flags, flags);
}
+
+ rcu_read_unlock();
}
-bool
+isc_result_t
dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name,
- dns_rdatatype_t type, uint32_t *flagp, isc_time_t *now) {
- dns_bcentry_t *bad, *prev, *next;
- bool answer = false;
- unsigned int i;
- unsigned int hash;
-
+ dns_rdatatype_t type, uint32_t *flagp, isc_stdtime_t now) {
REQUIRE(VALID_BADCACHE(bc));
REQUIRE(name != NULL);
- REQUIRE(now != NULL);
- RWLOCK(&bc->lock, isc_rwlocktype_read);
+ isc_result_t result = ISC_R_NOTFOUND;
- /*
- * XXXMUKS: dns_name_equal() is expensive as it does a
- * octet-by-octet comparison, and it can be made better in two
- * ways here. First, lowercase the names (use
- * dns_name_downcase() instead of dns_name_copy() in
- * dns_badcache_add()) so that dns_name_caseequal() can be used
- * which the compiler will emit as SIMD instructions. Second,
- * don't put multiple copies of the same name in the chain (or
- * multiple names will have to be matched for equality), but use
- * name->link to store the type specific part.
- */
+ rcu_read_lock();
+ struct cds_lfht *ht = rcu_dereference(bc->ht);
+ INSIST(ht != NULL);
- if (atomic_load_relaxed(&bc->count) == 0) {
- goto skip;
- }
+ dns_bcentry_t *bad = NULL;
+ uint32_t hashval = dns_name_hash(name);
- hash = dns_name_hash(name) % bc->size;
- prev = NULL;
- LOCK(&bc->tlocks[hash]);
- for (bad = bc->table[hash]; bad != NULL; bad = next) {
- next = bad->next;
- /*
- * Search the hash list. Clean out expired records as we go.
- */
- if (isc_time_compare(&bad->expire, now) < 0) {
- if (prev != NULL) {
- prev->next = bad->next;
- } else {
- bc->table[hash] = bad->next;
- }
-
- isc_mem_put(bc->mctx, bad, sizeof(*bad));
- atomic_fetch_sub(&bc->count, 1);
- continue;
- }
- if (bad->type == type && dns_name_equal(name, bad->name)) {
- if (flagp != NULL) {
- *flagp = bad->flags;
- }
- answer = true;
- break;
+ struct cds_lfht_iter iter;
+ dns_bcentry_t *found = NULL;
+ cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name,
+ &iter, bad, ht_node) {
+ if (bad->type == type && bcentry_alive(ht, bad, now)) {
+ found = bad;
}
- prev = bad;
}
- UNLOCK(&bc->tlocks[hash]);
-skip:
- /*
- * Slow sweep to clean out stale records.
- */
- i = atomic_fetch_add(&bc->sweep, 1) % bc->size;
- if (isc_mutex_trylock(&bc->tlocks[i]) == ISC_R_SUCCESS) {
- bad = bc->table[i];
- if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) {
- bc->table[i] = bad->next;
- isc_mem_put(bc->mctx, bad, sizeof(*bad));
- atomic_fetch_sub_relaxed(&bc->count, 1);
+ if (found) {
+ result = ISC_R_SUCCESS;
+ if (flagp != NULL) {
+ *flagp = atomic_load_relaxed(&found->flags);
}
- UNLOCK(&bc->tlocks[i]);
+
+ bcentry_purge_next(ht, &iter, now);
}
- RWUNLOCK(&bc->lock, isc_rwlocktype_read);
- return (answer);
+ rcu_read_unlock();
+
+ return (result);
}
void
dns_badcache_flush(dns_badcache_t *bc) {
- dns_bcentry_t *entry, *next;
- unsigned int i;
-
- RWLOCK(&bc->lock, isc_rwlocktype_write);
REQUIRE(VALID_BADCACHE(bc));
- for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) {
- for (entry = bc->table[i]; entry != NULL; entry = next) {
- next = entry->next;
- isc_mem_put(bc->mctx, entry, sizeof(*entry));
- atomic_fetch_sub_relaxed(&bc->count, 1);
- }
- bc->table[i] = NULL;
+ struct cds_lfht *ht =
+ cds_lfht_new(BADCACHE_INIT_SIZE, BADCACHE_MIN_SIZE, 0,
+ CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+ INSIST(ht != NULL);
+
+ /* First swap the hashtables */
+ rcu_read_lock();
+ ht = rcu_xchg_pointer(&bc->ht, ht);
+ rcu_read_unlock();
+
+ /* Make sure nobody is using the old hash table */
+ synchronize_rcu();
+
+ /* Flush the old hash table */
+ dns_bcentry_t *bad = NULL;
+ struct cds_lfht_iter iter;
+ cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
+ INSIST(!cds_lfht_del(ht, &bad->ht_node));
+ bcentry_destroy(&bad->rcu_head);
}
- RWUNLOCK(&bc->lock, isc_rwlocktype_write);
+ RUNTIME_CHECK(!cds_lfht_destroy(ht, NULL));
}
void
dns_badcache_flushname(dns_badcache_t *bc, const dns_name_t *name) {
- dns_bcentry_t *bad, *prev, *next;
- isc_time_t now;
- unsigned int hash;
-
REQUIRE(VALID_BADCACHE(bc));
REQUIRE(name != NULL);
- RWLOCK(&bc->lock, isc_rwlocktype_read);
-
- now = isc_time_now();
- hash = dns_name_hash(name) % bc->size;
- LOCK(&bc->tlocks[hash]);
- prev = NULL;
- for (bad = bc->table[hash]; bad != NULL; bad = next) {
- int n;
- next = bad->next;
- n = isc_time_compare(&bad->expire, &now);
- if (n < 0 || dns_name_equal(name, bad->name)) {
- if (prev == NULL) {
- bc->table[hash] = bad->next;
- } else {
- prev->next = bad->next;
- }
-
- isc_mem_put(bc->mctx, bad, sizeof(*bad));
- atomic_fetch_sub_relaxed(&bc->count, 1);
- } else {
- prev = bad;
- }
+ rcu_read_lock();
+ struct cds_lfht *ht = rcu_dereference(bc->ht);
+ INSIST(ht != NULL);
+
+ dns_bcentry_t *bad = NULL;
+ uint32_t hashval = dns_name_hash(name);
+
+ struct cds_lfht_iter iter;
+ cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name,
+ &iter, bad, ht_node) {
+ bcentry_evict(ht, bad);
}
- UNLOCK(&bc->tlocks[hash]);
- RWUNLOCK(&bc->lock, isc_rwlocktype_read);
+ rcu_read_unlock();
}
void
dns_badcache_flushtree(dns_badcache_t *bc, const dns_name_t *name) {
- dns_bcentry_t *bad, *prev, *next;
- unsigned int i;
- int n;
- isc_time_t now;
+ dns_bcentry_t *bad;
+ isc_stdtime_t now = isc_stdtime_now();
REQUIRE(VALID_BADCACHE(bc));
REQUIRE(name != NULL);
- /*
- * We write lock the tree to avoid relocking every node
- * individually.
- */
- RWLOCK(&bc->lock, isc_rwlocktype_write);
-
- now = isc_time_now();
-
- for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) {
- prev = NULL;
- for (bad = bc->table[i]; bad != NULL; bad = next) {
- next = bad->next;
- n = isc_time_compare(&bad->expire, &now);
- if (n < 0 || dns_name_issubdomain(bad->name, name)) {
- if (prev == NULL) {
- bc->table[i] = bad->next;
- } else {
- prev->next = bad->next;
- }
-
- isc_mem_put(bc->mctx, bad, sizeof(*bad));
- atomic_fetch_sub_relaxed(&bc->count, 1);
- } else {
- prev = bad;
- }
+ rcu_read_lock();
+ struct cds_lfht *ht = rcu_dereference(bc->ht);
+ INSIST(ht != NULL);
+
+ struct cds_lfht_iter iter;
+ cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
+ if (dns_name_issubdomain(bad->name, name)) {
+ bcentry_evict(ht, bad);
+ } else if (!bcentry_alive(ht, bad, now)) {
+ /* Flush all the expired entries */
}
}
- RWUNLOCK(&bc->lock, isc_rwlocktype_write);
+ rcu_read_unlock();
}
-void
-dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) {
+static void
+bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp) {
char namebuf[DNS_NAME_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
- dns_bcentry_t *bad, *next, *prev;
- isc_time_t now;
- unsigned int i;
- uint64_t t;
+
+ dns_name_format(bad->name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(bad->type, typebuf, sizeof(typebuf));
+ fprintf(fp, "; %s/%s [ttl %" PRIu32 "]\n", namebuf, typebuf,
+ atomic_load_relaxed(&bad->expire) - now);
+}
+
+void
+dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) {
+ dns_bcentry_t *bad;
+ isc_stdtime_t now = isc_stdtime_now();
REQUIRE(VALID_BADCACHE(bc));
- REQUIRE(cachename != NULL);
REQUIRE(fp != NULL);
- /*
- * We write lock the tree to avoid relocking every node
- * individually.
- */
- RWLOCK(&bc->lock, isc_rwlocktype_write);
fprintf(fp, ";\n; %s\n;\n", cachename);
- now = isc_time_now();
- for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) {
- prev = NULL;
- for (bad = bc->table[i]; bad != NULL; bad = next) {
- next = bad->next;
- if (isc_time_compare(&bad->expire, &now) < 0) {
- if (prev != NULL) {
- prev->next = bad->next;
- } else {
- bc->table[i] = bad->next;
- }
-
- isc_mem_put(bc->mctx, bad, sizeof(*bad));
- atomic_fetch_sub_relaxed(&bc->count, 1);
- continue;
- }
- prev = bad;
- dns_name_format(bad->name, namebuf, sizeof(namebuf));
- dns_rdatatype_format(bad->type, typebuf,
- sizeof(typebuf));
- t = isc_time_microdiff(&bad->expire, &now);
- t /= 1000;
- fprintf(fp,
- "; %s/%s [ttl "
- "%" PRIu64 "]\n",
- namebuf, typebuf, t);
+ rcu_read_lock();
+ struct cds_lfht *ht = rcu_dereference(bc->ht);
+ INSIST(ht != NULL);
+
+ struct cds_lfht_iter iter;
+ cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
+ if (bcentry_alive(ht, bad, now)) {
+ bcentry_print(bad, now, fp);
}
}
- RWUNLOCK(&bc->lock, isc_rwlocktype_write);
+
+ rcu_read_unlock();
}
#include <inttypes.h>
#include <stdbool.h>
+#include <isc/mem.h>
+#include <isc/stdtime.h>
+
#include <dns/types.h>
ISC_LANG_BEGINDECLS
*** Functions
***/
-isc_result_t
-dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp);
+dns_badcache_t *
+dns_badcache_new(isc_mem_t *mctx);
/*%
* Allocate and initialize a badcache and store it in '*bcp'.
*
void
dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name,
dns_rdatatype_t type, bool update, uint32_t flags,
- isc_time_t *expire);
+ isc_stdtime_t expire);
/*%
* Adds a badcache entry to the badcache 'bc' for name 'name' and
* type 'type'. If an entry already exists, then it will be updated if
* \li expire != NULL
*/
-bool
+isc_result_t
dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name,
- dns_rdatatype_t type, uint32_t *flagp, isc_time_t *now);
+ dns_rdatatype_t type, uint32_t *flagp, isc_stdtime_t now);
/*%
- * Returns true if a record is found in the badcache 'bc' matching
+ * Returns ISC_R_SUCCESS if a record is found in the badcache 'bc' matching
* 'name' and 'type', with an expiration date later than 'now'.
* If 'flagp' is not NULL, then '*flagp' is updated to the flags
- * that were stored in the badcache entry. Returns false if
+ * that were stored in the badcache entry. Returns ISC_R_NOTFOUND if
* no matching record is found.
*
* Requires:
* \li name to be valid.
*/
-bool
+isc_result_t
dns_resolver_getbadcache(dns_resolver_t *resolver, const dns_name_t *name,
dns_rdatatype_t type, isc_time_t *now);
/*%<
*/
#define MAX_EDNS0_TIMEOUTS 3
-#define DNS_RESOLVER_BADCACHESIZE 1021
#define DNS_RESOLVER_BADCACHETTL(fctx) \
(((fctx)->res->lame_ttl > 30) ? (fctx)->res->lame_ttl : 30)
isc_tlsctx_cache_t *tlsctx_cache,
dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6,
dns_resolver_t **resp) {
- isc_result_t result = ISC_R_SUCCESS;
dns_resolver_t *res = NULL;
/*
#endif
isc_refcount_init(&res->references, 1);
- result = dns_badcache_init(res->mctx, DNS_RESOLVER_BADCACHESIZE,
- &res->badcache);
- if (result != ISC_R_SUCCESS) {
- goto cleanup;
- }
+ res->badcache = dns_badcache_new(res->mctx);
/* This needs to be case sensitive to not lowercase options and type */
isc_hashmap_create(view->mctx, RES_DOMAIN_HASH_BITS,
*resp = res;
return (ISC_R_SUCCESS);
-
-cleanup:
- dns_view_weakdetach(&res->view);
- isc_mem_putanddetach(&res->mctx, res, sizeof(*res));
- return (result);
}
static void
#endif /* ifdef ENABLE_AFL */
{
dns_badcache_add(resolver->badcache, name, type, false, 0,
- expire);
+ isc_time_seconds(expire));
}
}
-bool
+isc_result_t
dns_resolver_getbadcache(dns_resolver_t *resolver, const dns_name_t *name,
dns_rdatatype_t type, isc_time_t *now) {
- return (dns_badcache_find(resolver->badcache, name, type, NULL, now));
+ return (dns_badcache_find(resolver->badcache, name, type, NULL,
+ isc_time_seconds(now)));
}
void
disassociate_rdatasets(val);
- if (dns_resolver_getbadcache(val->view->resolver, name, type, &now)) {
+ result = dns_resolver_getbadcache(val->view->resolver, name, type,
+ &now);
+ if (result == ISC_R_SUCCESS) {
dns_name_format(name, namebuf, sizeof(namebuf));
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
validator_log(val, ISC_LOG_INFO, "bad cache hit (%s/%s)",
goto cleanup; \
} while (0)
-#define DNS_VIEW_DELONLYHASH 111
-#define DNS_VIEW_FAILCACHESIZE 1021
+#define DNS_VIEW_DELONLYHASH 111
/*%
* Default EDNS0 buffer size
dns_tsigkeyring_create(view->mctx, &view->dynamickeys);
- result = dns_badcache_init(view->mctx, DNS_VIEW_FAILCACHESIZE,
- &view->failcache);
- if (result != ISC_R_SUCCESS) {
- goto cleanup_dynkeys;
- }
+ view->failcache = dns_badcache_new(view->mctx);
isc_mutex_init(&view->new_zone_lock);
isc_mutex_destroy(&view->new_zone_lock);
dns_badcache_destroy(&view->failcache);
-cleanup_dynkeys:
if (view->dynamickeys != NULL) {
dns_tsigkeyring_detach(&view->dynamickeys);
}
isc_interval_set(&i, client->view->fail_ttl, 0);
result = isc_time_nowplusinterval(&expire, &i);
if (result == ISC_R_SUCCESS) {
- dns_badcache_add(
- client->view->failcache, client->query.qname,
- client->query.qtype, true, flags, &expire);
+ dns_badcache_add(client->view->failcache,
+ client->query.qname,
+ client->query.qtype, true, flags,
+ isc_time_seconds(&expire));
}
}
*/
isc_result_t
ns__query_sfcache(query_ctx_t *qctx) {
- bool failcache;
+ isc_result_t failcache;
uint32_t flags;
/*
flags = 0;
#ifdef ENABLE_AFL
if (qctx->client->manager->sctx->fuzztype == isc_fuzz_resolver) {
- failcache = false;
- } else {
+ failcache = ISC_R_NOTFOUND;
+ } else
+#endif /* ifdef ENABLE_AFL */
+ {
failcache = dns_badcache_find(
qctx->view->failcache, qctx->client->query.qname,
- qctx->qtype, &flags, &qctx->client->tnow);
+ qctx->qtype, &flags,
+ isc_time_seconds(&qctx->client->tnow));
}
-#else /* ifdef ENABLE_AFL */
- failcache = dns_badcache_find(qctx->view->failcache,
- qctx->client->query.qname, qctx->qtype,
- &flags, &qctx->client->tnow);
-#endif /* ifdef ENABLE_AFL */
- if (failcache &&
- (((flags & NS_FAILCACHE_CD) != 0) ||
- ((qctx->client->message->flags & DNS_MESSAGEFLAG_CD) == 0)))
+
+ if (failcache != ISC_R_SUCCESS) {
+ return (ISC_R_COMPLETE);
+ }
+
+ if (((flags & NS_FAILCACHE_CD) != 0) ||
+ ((qctx->client->message->flags & DNS_MESSAGEFLAG_CD) == 0))
{
if (isc_log_wouldlog(ns_lctx, ISC_LOG_DEBUG(1))) {
char namebuf[DNS_NAME_FORMATSIZE];
check_PROGRAMS = \
acl_test \
+ badcache_test \
db_test \
dbdiff_test \
dbiterator_test \
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/md.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/thread.h>
+#include <isc/urcu.h>
+#include <isc/util.h>
+#include <isc/uv.h>
+
+#include <dns/badcache.h>
+#include <dns/compress.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdatatype.h>
+
+#include <tests/dns.h>
+
+static uint32_t
+crc32(const uint8_t *buf, size_t size) {
+ uint32_t crc;
+ crc = 0xFFFFFFFF;
+ while (size--) {
+ crc = crc ^ *buf++;
+ for (size_t j = 0; j < 8; j++) {
+ uint32_t mask = -(crc & 1);
+ crc = (crc >> 1) ^ (0xedb88320 & mask);
+ }
+ }
+ return (~crc);
+}
+
+#define BADCACHE_TEST_FLAG 1 << 3
+
+ISC_LOOP_TEST_IMPL(basic) {
+ dns_badcache_t *bc = NULL;
+ dns_fixedname_t fname = { 0 };
+ dns_name_t *name = dns_fixedname_initname(&fname);
+ isc_stdtime_t now = isc_stdtime_now();
+ isc_result_t result;
+ uint32_t flags = BADCACHE_TEST_FLAG;
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+
+ bc = dns_badcache_new(mctx);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+
+ flags = 0;
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(flags, BADCACHE_TEST_FLAG);
+
+ flags = 0;
+ result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+ assert_int_equal(flags, 0);
+
+ dns_badcache_destroy(&bc);
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+ISC_LOOP_TEST_IMPL(expire) {
+ dns_badcache_t *bc = NULL;
+ dns_fixedname_t fname = { 0 };
+ dns_name_t *name = dns_fixedname_initname(&fname);
+ isc_stdtime_t now = isc_stdtime_now();
+ isc_result_t result;
+ uint32_t flags = BADCACHE_TEST_FLAG;
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+
+ bc = dns_badcache_new(mctx);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+ dns_badcache_add(bc, name, dns_rdatatype_a, false, flags, now + 60);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(flags, BADCACHE_TEST_FLAG);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
+ now + 61);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(flags, BADCACHE_TEST_FLAG);
+
+ dns_badcache_add(bc, name, dns_rdatatype_a, true, flags, now + 120);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now + 61);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(flags, BADCACHE_TEST_FLAG);
+
+ dns_badcache_destroy(&bc);
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+ISC_LOOP_TEST_IMPL(print) {
+ dns_badcache_t *bc = NULL;
+ dns_fixedname_t fname = { 0 };
+ dns_name_t *name = dns_fixedname_initname(&fname);
+ isc_stdtime_t now = isc_stdtime_now();
+ isc_stdtime_t expire = now + 60;
+ uint32_t flags = BADCACHE_TEST_FLAG;
+ FILE *file = NULL;
+ uint8_t buf[4096];
+ size_t len;
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+
+ bc = dns_badcache_new(mctx);
+ dns_badcache_add(bc, name, dns_rdatatype_a, false, flags, expire);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, expire);
+
+ file = fopen("./badcache.out", "w");
+ dns_badcache_print(bc, "badcache", file);
+ fclose(file);
+
+ file = fopen("./badcache.out", "r");
+ len = fread(buf, sizeof(buf[0]), ARRAY_SIZE(buf), file);
+ assert_int_equal(len, 68);
+ fclose(file);
+
+ /* Calculated as crc32 ./badcache.out */
+ assert_int_equal(crc32(buf, len), 0x7c96678f);
+
+ dns_badcache_destroy(&bc);
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+ISC_LOOP_TEST_IMPL(flush) {
+ dns_badcache_t *bc = NULL;
+ dns_fixedname_t fname = { 0 };
+ dns_name_t *name = dns_fixedname_initname(&fname);
+ isc_stdtime_t now = isc_stdtime_now();
+ isc_result_t result;
+ uint32_t flags = BADCACHE_TEST_FLAG;
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+
+ bc = dns_badcache_new(mctx);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ dns_badcache_flush(bc);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ dns_badcache_destroy(&bc);
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+ISC_LOOP_TEST_IMPL(flushname) {
+ dns_badcache_t *bc = NULL;
+ dns_fixedname_t fname = { 0 };
+ dns_name_t *name = dns_fixedname_initname(&fname);
+ isc_stdtime_t now = isc_stdtime_now();
+ isc_result_t result;
+ uint32_t flags = BADCACHE_TEST_FLAG;
+
+ bc = dns_badcache_new(mctx);
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
+ dns_badcache_flushname(bc, name);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+ result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ dns_badcache_destroy(&bc);
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+ISC_LOOP_TEST_IMPL(flushtree) {
+ dns_badcache_t *bc = NULL;
+ dns_fixedname_t fname = { 0 };
+ dns_name_t *name = dns_fixedname_initname(&fname);
+ isc_stdtime_t now = isc_stdtime_now();
+ isc_result_t result;
+ uint32_t flags = BADCACHE_TEST_FLAG;
+
+ bc = dns_badcache_new(mctx);
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(flags, BADCACHE_TEST_FLAG);
+
+ dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(flags, BADCACHE_TEST_FLAG);
+
+ dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(flags, BADCACHE_TEST_FLAG);
+
+ dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
+ dns_badcache_flushtree(bc, name);
+
+ dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(flags, BADCACHE_TEST_FLAG);
+
+ dns_badcache_destroy(&bc);
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+ISC_LOOP_TEST_IMPL(purge) {
+ dns_badcache_t *bc = NULL;
+ dns_fixedname_t fname = { 0 };
+ dns_name_t *name = dns_fixedname_initname(&fname);
+ isc_stdtime_t now = isc_stdtime_now();
+ isc_result_t result;
+ uint32_t flags = BADCACHE_TEST_FLAG;
+
+ bc = dns_badcache_new(mctx);
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
+ now - 60);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
+ now - 60);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
+ dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
+ now - 60);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
+ now + 30);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
+ now + 30);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
+ now + 30);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
+ result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
+ now + 30);
+ assert_int_equal(result, ISC_R_NOTFOUND);
+
+ dns_badcache_destroy(&bc);
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY_CUSTOM(basic, setup_managers, teardown_managers)
+ISC_TEST_ENTRY_CUSTOM(expire, setup_managers, teardown_managers)
+ISC_TEST_ENTRY_CUSTOM(print, setup_managers, teardown_managers)
+ISC_TEST_ENTRY_CUSTOM(flush, setup_managers, teardown_managers)
+ISC_TEST_ENTRY_CUSTOM(flushname, setup_managers, teardown_managers)
+ISC_TEST_ENTRY_CUSTOM(flushtree, setup_managers, teardown_managers)
+ISC_TEST_ENTRY_CUSTOM(purge, setup_managers, teardown_managers)
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN
dns_badcache_add(qctx->client->view->failcache, dns_rootname,
dns_rdatatype_ns, true,
- test->cache_entry_flags, &expire);
+ test->cache_entry_flags,
+ isc_time_seconds(&expire));
}
/*