bind9/lib/dns/zt.c
Evan Hunt a52b17d39b
remove isc_task completely
as there is no further use of isc_task in BIND, this commit removes
it, along with isc_taskmgr, isc_event, and all other related types.

functions that accepted taskmgr as a parameter have been cleaned up.
as a result of this change, some functions can no longer fail, so
they've been changed to type void, and their callers have been
updated accordingly.

the tasks table has been removed from the statistics channel and
the stats version has been updated. dns_dyndbctx has been changed
to reference the loopmgr instead of taskmgr, and DNS_DYNDB_VERSION
has been udpated as well.
2023-02-16 18:35:32 +01:00

613 lines
14 KiB
C

/*
* 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.
*/
/*! \file */
#include <inttypes.h>
#include <stdbool.h>
#include <isc/atomic.h>
#include <isc/file.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/result.h>
#include <isc/string.h>
#include <isc/util.h>
#include <dns/log.h>
#include <dns/name.h>
#include <dns/rbt.h>
#include <dns/rdataclass.h>
#include <dns/view.h>
#include <dns/zone.h>
#include <dns/zt.h>
struct zt_load_params {
dns_zt_zoneloaded_t dl;
bool newonly;
};
struct dns_zt {
/* Unlocked. */
unsigned int magic;
isc_mem_t *mctx;
dns_rdataclass_t rdclass;
isc_rwlock_t rwlock;
dns_zt_allloaded_t loaddone;
void *loaddone_arg;
struct zt_load_params *loadparams;
/* Atomic */
atomic_bool flush;
isc_refcount_t references;
isc_refcount_t loads_pending;
/* Locked by lock. */
dns_rbt_t *table;
};
struct zt_freeze_params {
dns_view_t *view;
bool freeze;
};
#define ZTMAGIC ISC_MAGIC('Z', 'T', 'b', 'l')
#define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC)
static void
auto_detach(void *, void *);
static isc_result_t
load(dns_zone_t *zone, void *uap);
static isc_result_t
asyncload(dns_zone_t *zone, void *callback);
static isc_result_t
freezezones(dns_zone_t *zone, void *uap);
static isc_result_t
doneloading(dns_zt_t *zt, dns_zone_t *zone);
isc_result_t
dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) {
dns_zt_t *zt;
isc_result_t result;
REQUIRE(ztp != NULL && *ztp == NULL);
zt = isc_mem_get(mctx, sizeof(*zt));
zt->table = NULL;
result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
if (result != ISC_R_SUCCESS) {
goto cleanup_zt;
}
isc_rwlock_init(&zt->rwlock);
zt->mctx = NULL;
isc_mem_attach(mctx, &zt->mctx);
isc_refcount_init(&zt->references, 1);
atomic_init(&zt->flush, false);
zt->rdclass = rdclass;
zt->magic = ZTMAGIC;
zt->loaddone = NULL;
zt->loaddone_arg = NULL;
zt->loadparams = NULL;
isc_refcount_init(&zt->loads_pending, 0);
*ztp = zt;
return (ISC_R_SUCCESS);
cleanup_zt:
isc_mem_put(mctx, zt, sizeof(*zt));
return (result);
}
isc_result_t
dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
isc_result_t result;
dns_zone_t *dummy = NULL;
dns_name_t *name;
REQUIRE(VALID_ZT(zt));
name = dns_zone_getorigin(zone);
RWLOCK(&zt->rwlock, isc_rwlocktype_write);
result = dns_rbt_addname(zt->table, name, zone);
if (result == ISC_R_SUCCESS) {
dns_zone_attach(zone, &dummy);
}
RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
return (result);
}
isc_result_t
dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
isc_result_t result;
dns_name_t *name;
REQUIRE(VALID_ZT(zt));
name = dns_zone_getorigin(zone);
RWLOCK(&zt->rwlock, isc_rwlocktype_write);
result = dns_rbt_deletename(zt->table, name, false);
RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
return (result);
}
isc_result_t
dns_zt_find(dns_zt_t *zt, const dns_name_t *name, unsigned int options,
dns_name_t *foundname, dns_zone_t **zonep) {
isc_result_t result;
dns_zone_t *dummy = NULL;
unsigned int rbtoptions = 0;
REQUIRE(VALID_ZT(zt));
if ((options & DNS_ZTFIND_NOEXACT) != 0) {
rbtoptions |= DNS_RBTFIND_NOEXACT;
}
RWLOCK(&zt->rwlock, isc_rwlocktype_read);
result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
(void **)(void *)&dummy);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
/*
* If DNS_ZTFIND_MIRROR is set and the zone which was
* determined to be the deepest match for the supplied name is
* a mirror zone which is expired or not yet loaded, treat it
* as non-existent. This will trigger a fallback to recursion
* instead of returning a SERVFAIL.
*
* Note that currently only the deepest match in the zone table
* is checked. Consider a server configured with two mirror
* zones: "bar" and its child, "foo.bar". If zone data is
* available for "bar" but not for "foo.bar", a query with
* QNAME equal to or below "foo.bar" will cause ISC_R_NOTFOUND
* to be returned, not DNS_R_PARTIALMATCH, despite zone data
* being available for "bar". This is considered to be an edge
* case, handling which more appropriately is possible, but
* arguably not worth the added complexity.
*/
if ((options & DNS_ZTFIND_MIRROR) != 0 &&
dns_zone_gettype(dummy) == dns_zone_mirror &&
!dns_zone_isloaded(dummy))
{
result = ISC_R_NOTFOUND;
} else {
dns_zone_attach(dummy, zonep);
}
}
RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
return (result);
}
void
dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {
REQUIRE(VALID_ZT(zt));
REQUIRE(ztp != NULL && *ztp == NULL);
isc_refcount_increment(&zt->references);
*ztp = zt;
}
static isc_result_t
flush(dns_zone_t *zone, void *uap) {
UNUSED(uap);
return (dns_zone_flush(zone));
}
static void
zt_destroy(dns_zt_t *zt) {
isc_refcount_destroy(&zt->references);
isc_refcount_destroy(&zt->loads_pending);
if (atomic_load_acquire(&zt->flush)) {
(void)dns_zt_apply(zt, isc_rwlocktype_none, false, NULL, flush,
NULL);
}
dns_rbt_destroy(&zt->table);
isc_rwlock_destroy(&zt->rwlock);
zt->magic = 0;
isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
}
void
dns_zt_detach(dns_zt_t **ztp) {
dns_zt_t *zt;
REQUIRE(ztp != NULL && VALID_ZT(*ztp));
zt = *ztp;
*ztp = NULL;
if (isc_refcount_decrement(&zt->references) == 1) {
zt_destroy(zt);
}
}
void
dns_zt_flush(dns_zt_t *zt) {
REQUIRE(VALID_ZT(zt));
atomic_store_release(&zt->flush, true);
}
isc_result_t
dns_zt_load(dns_zt_t *zt, bool stop, bool newonly) {
isc_result_t result;
struct zt_load_params params;
REQUIRE(VALID_ZT(zt));
params.newonly = newonly;
result = dns_zt_apply(zt, isc_rwlocktype_read, stop, NULL, load,
&params);
return (result);
}
static isc_result_t
load(dns_zone_t *zone, void *paramsv) {
isc_result_t result;
struct zt_load_params *params = (struct zt_load_params *)paramsv;
result = dns_zone_load(zone, params->newonly);
if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
result == DNS_R_DYNAMIC)
{
result = ISC_R_SUCCESS;
}
return (result);
}
static void
call_loaddone(dns_zt_t *zt) {
dns_zt_allloaded_t loaddone = zt->loaddone;
void *loaddone_arg = zt->loaddone_arg;
/*
* Set zt->loaddone, zt->loaddone_arg and zt->loadparams to NULL
* before calling loaddone.
*/
zt->loaddone = NULL;
zt->loaddone_arg = NULL;
isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
zt->loadparams = NULL;
/*
* Call the callback last.
*/
if (loaddone != NULL) {
loaddone(loaddone_arg);
}
}
isc_result_t
dns_zt_asyncload(dns_zt_t *zt, bool newonly, dns_zt_allloaded_t alldone,
void *arg) {
isc_result_t result;
uint_fast32_t loads_pending;
REQUIRE(VALID_ZT(zt));
/*
* Obtain a reference to zt->loads_pending so that asyncload can
* safely decrement both zt->references and zt->loads_pending
* without going to zero.
*/
loads_pending = isc_refcount_increment0(&zt->loads_pending);
INSIST(loads_pending == 0);
/*
* Only one dns_zt_asyncload call at a time should be active so
* these pointers should be NULL. They are set back to NULL
* before the zt->loaddone (alldone) is called in call_loaddone.
*/
INSIST(zt->loadparams == NULL);
INSIST(zt->loaddone == NULL);
INSIST(zt->loaddone_arg == NULL);
zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params));
zt->loadparams->dl = doneloading;
zt->loadparams->newonly = newonly;
zt->loaddone = alldone;
zt->loaddone_arg = arg;
result = dns_zt_apply(zt, isc_rwlocktype_read, false, NULL, asyncload,
zt);
/*
* Have all the loads completed?
*/
if (isc_refcount_decrement(&zt->loads_pending) == 1) {
call_loaddone(zt);
}
return (result);
}
/*
* Initiates asynchronous loading of zone 'zone'. 'callback' is a
* pointer to a function which will be used to inform the caller when
* the zone loading is complete.
*/
static isc_result_t
asyncload(dns_zone_t *zone, void *zt_) {
isc_result_t result;
struct dns_zt *zt = (dns_zt_t *)zt_;
REQUIRE(zone != NULL);
isc_refcount_increment(&zt->references);
isc_refcount_increment(&zt->loads_pending);
result = dns_zone_asyncload(zone, zt->loadparams->newonly,
*zt->loadparams->dl, zt);
if (result != ISC_R_SUCCESS) {
/*
* Caller is holding a reference to zt->loads_pending
* and zt->references so these can't decrement to zero.
*/
isc_refcount_decrement1(&zt->references);
isc_refcount_decrement1(&zt->loads_pending);
}
return (ISC_R_SUCCESS);
}
isc_result_t
dns_zt_freezezones(dns_zt_t *zt, dns_view_t *view, bool freeze) {
isc_result_t result, tresult;
struct zt_freeze_params params = { view, freeze };
REQUIRE(VALID_ZT(zt));
result = dns_zt_apply(zt, isc_rwlocktype_read, false, &tresult,
freezezones, &params);
if (tresult == ISC_R_NOTFOUND) {
tresult = ISC_R_SUCCESS;
}
return ((result == ISC_R_SUCCESS) ? tresult : result);
}
static isc_result_t
freezezones(dns_zone_t *zone, void *uap) {
struct zt_freeze_params *params = uap;
bool frozen;
isc_result_t result = ISC_R_SUCCESS;
char classstr[DNS_RDATACLASS_FORMATSIZE];
char zonename[DNS_NAME_FORMATSIZE];
dns_zone_t *raw = NULL;
dns_view_t *view;
const char *vname;
const char *sep;
int level;
dns_zone_getraw(zone, &raw);
if (raw != NULL) {
zone = raw;
}
if (params->view != dns_zone_getview(zone)) {
if (raw != NULL) {
dns_zone_detach(&raw);
}
return (ISC_R_SUCCESS);
}
if (dns_zone_gettype(zone) != dns_zone_primary) {
if (raw != NULL) {
dns_zone_detach(&raw);
}
return (ISC_R_SUCCESS);
}
if (!dns_zone_isdynamic(zone, true)) {
if (raw != NULL) {
dns_zone_detach(&raw);
}
return (ISC_R_SUCCESS);
}
frozen = dns_zone_getupdatedisabled(zone);
if (params->freeze) {
if (frozen) {
result = DNS_R_FROZEN;
}
if (result == ISC_R_SUCCESS) {
result = dns_zone_flush(zone);
}
if (result == ISC_R_SUCCESS) {
dns_zone_setupdatedisabled(zone, params->freeze);
}
} else {
if (frozen) {
result = dns_zone_loadandthaw(zone);
if (result == DNS_R_CONTINUE ||
result == DNS_R_UPTODATE)
{
result = ISC_R_SUCCESS;
}
}
}
view = dns_zone_getview(zone);
if (strcmp(view->name, "_bind") == 0 || strcmp(view->name, "_defaul"
"t") == 0)
{
vname = "";
sep = "";
} else {
vname = view->name;
sep = " ";
}
dns_rdataclass_format(dns_zone_getclass(zone), classstr,
sizeof(classstr));
dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
level, "%s zone '%s/%s'%s%s: %s",
params->freeze ? "freezing" : "thawing", zonename,
classstr, sep, vname, isc_result_totext(result));
if (raw != NULL) {
dns_zone_detach(&raw);
}
return (result);
}
void
dns_zt_setviewcommit(dns_zt_t *zt) {
dns_rbtnode_t *node;
dns_rbtnodechain_t chain;
isc_result_t result;
REQUIRE(VALID_ZT(zt));
RWLOCK(&zt->rwlock, isc_rwlocktype_read);
dns_rbtnodechain_init(&chain);
result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
if (result == ISC_R_SUCCESS && node->data != NULL) {
dns_zone_setviewcommit(node->data);
}
result = dns_rbtnodechain_next(&chain, NULL, NULL);
}
dns_rbtnodechain_invalidate(&chain);
RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
}
void
dns_zt_setviewrevert(dns_zt_t *zt) {
dns_rbtnode_t *node;
dns_rbtnodechain_t chain;
isc_result_t result;
REQUIRE(VALID_ZT(zt));
dns_rbtnodechain_init(&chain);
result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
if (result == ISC_R_SUCCESS && node->data != NULL) {
dns_zone_setviewrevert(node->data);
}
result = dns_rbtnodechain_next(&chain, NULL, NULL);
}
dns_rbtnodechain_invalidate(&chain);
}
isc_result_t
dns_zt_apply(dns_zt_t *zt, isc_rwlocktype_t lock, bool stop, isc_result_t *sub,
isc_result_t (*action)(dns_zone_t *, void *), void *uap) {
dns_rbtnode_t *node;
dns_rbtnodechain_t chain;
isc_result_t result, tresult = ISC_R_SUCCESS;
dns_zone_t *zone;
REQUIRE(VALID_ZT(zt));
REQUIRE(action != NULL);
if (lock != isc_rwlocktype_none) {
RWLOCK(&zt->rwlock, lock);
}
dns_rbtnodechain_init(&chain);
result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
if (result == ISC_R_NOTFOUND) {
/*
* The tree is empty.
*/
tresult = result;
result = ISC_R_NOMORE;
}
while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
if (result == ISC_R_SUCCESS) {
zone = node->data;
if (zone != NULL) {
result = (action)(zone, uap);
}
if (result != ISC_R_SUCCESS && stop) {
tresult = result;
goto cleanup; /* don't break */
} else if (result != ISC_R_SUCCESS &&
tresult == ISC_R_SUCCESS)
{
tresult = result;
}
}
result = dns_rbtnodechain_next(&chain, NULL, NULL);
}
if (result == ISC_R_NOMORE) {
result = ISC_R_SUCCESS;
}
cleanup:
dns_rbtnodechain_invalidate(&chain);
if (sub != NULL) {
*sub = tresult;
}
if (lock != isc_rwlocktype_none) {
RWUNLOCK(&zt->rwlock, lock);
}
return (result);
}
/*
* Decrement the loads_pending counter; when counter reaches
* zero, call the loaddone callback that was initially set by
* dns_zt_asyncload().
*/
static isc_result_t
doneloading(dns_zt_t *zt, dns_zone_t *zone) {
REQUIRE(VALID_ZT(zt));
UNUSED(zone);
if (isc_refcount_decrement(&zt->loads_pending) == 1) {
call_loaddone(zt);
}
if (isc_refcount_decrement(&zt->references) == 1) {
zt_destroy(zt);
}
return (ISC_R_SUCCESS);
}
/***
*** Private
***/
static void
auto_detach(void *data, void *arg) {
dns_zone_t *zone = data;
UNUSED(arg);
dns_zone_detach(&zone);
}