diff options
Diffstat (limited to 'lib/dns/dbtable.c')
-rw-r--r-- | lib/dns/dbtable.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/lib/dns/dbtable.c b/lib/dns/dbtable.c new file mode 100644 index 0000000..e36594b --- /dev/null +++ b/lib/dns/dbtable.c @@ -0,0 +1,249 @@ +/* + * 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 <stdbool.h> + +#include <isc/mem.h> +#include <isc/rwlock.h> +#include <isc/util.h> + +#include <dns/db.h> +#include <dns/dbtable.h> +#include <dns/rbt.h> +#include <dns/result.h> + +struct dns_dbtable { + /* Unlocked. */ + unsigned int magic; + isc_mem_t *mctx; + dns_rdataclass_t rdclass; + isc_rwlock_t tree_lock; + /* Protected by atomics */ + isc_refcount_t references; + /* Locked by tree_lock. */ + dns_rbt_t *rbt; + dns_db_t *default_db; +}; + +#define DBTABLE_MAGIC ISC_MAGIC('D', 'B', '-', '-') +#define VALID_DBTABLE(dbtable) ISC_MAGIC_VALID(dbtable, DBTABLE_MAGIC) + +static void +dbdetach(void *data, void *arg) { + dns_db_t *db = data; + + UNUSED(arg); + + dns_db_detach(&db); +} + +isc_result_t +dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, + dns_dbtable_t **dbtablep) { + dns_dbtable_t *dbtable; + isc_result_t result; + + REQUIRE(mctx != NULL); + REQUIRE(dbtablep != NULL && *dbtablep == NULL); + + dbtable = isc_mem_get(mctx, sizeof(*dbtable)); + + dbtable->rbt = NULL; + result = dns_rbt_create(mctx, dbdetach, NULL, &dbtable->rbt); + if (result != ISC_R_SUCCESS) { + goto clean1; + } + + isc_rwlock_init(&dbtable->tree_lock, 0, 0); + dbtable->default_db = NULL; + dbtable->mctx = NULL; + isc_mem_attach(mctx, &dbtable->mctx); + dbtable->rdclass = rdclass; + dbtable->magic = DBTABLE_MAGIC; + isc_refcount_init(&dbtable->references, 1); + + *dbtablep = dbtable; + + return (ISC_R_SUCCESS); + +clean1: + isc_mem_putanddetach(&mctx, dbtable, sizeof(*dbtable)); + + return (result); +} + +static void +dbtable_free(dns_dbtable_t *dbtable) { + /* + * Caller must ensure that it is safe to call. + */ + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + + if (dbtable->default_db != NULL) { + dns_db_detach(&dbtable->default_db); + } + + dns_rbt_destroy(&dbtable->rbt); + + RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + + isc_rwlock_destroy(&dbtable->tree_lock); + + dbtable->magic = 0; + + isc_mem_putanddetach(&dbtable->mctx, dbtable, sizeof(*dbtable)); +} + +void +dns_dbtable_attach(dns_dbtable_t *source, dns_dbtable_t **targetp) { + REQUIRE(VALID_DBTABLE(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + isc_refcount_increment(&source->references); + + *targetp = source; +} + +void +dns_dbtable_detach(dns_dbtable_t **dbtablep) { + dns_dbtable_t *dbtable; + + REQUIRE(dbtablep != NULL); + dbtable = *dbtablep; + *dbtablep = NULL; + REQUIRE(VALID_DBTABLE(dbtable)); + + if (isc_refcount_decrement(&dbtable->references) == 1) { + dbtable_free(dbtable); + } +} + +isc_result_t +dns_dbtable_add(dns_dbtable_t *dbtable, dns_db_t *db) { + isc_result_t result; + dns_db_t *dbclone; + + REQUIRE(VALID_DBTABLE(dbtable)); + REQUIRE(dns_db_class(db) == dbtable->rdclass); + + dbclone = NULL; + dns_db_attach(db, &dbclone); + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + result = dns_rbt_addname(dbtable->rbt, dns_db_origin(dbclone), dbclone); + RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + + return (result); +} + +void +dns_dbtable_remove(dns_dbtable_t *dbtable, dns_db_t *db) { + dns_db_t *stored_data = NULL; + isc_result_t result; + dns_name_t *name; + + REQUIRE(VALID_DBTABLE(dbtable)); + + name = dns_db_origin(db); + + /* + * There is a requirement that the association of name with db + * be verified. With the current rbt.c this is expensive to do, + * because effectively two find operations are being done, but + * deletion is relatively infrequent. + * XXXDCL ... this could be cheaper now with dns_rbt_deletenode. + */ + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + + result = dns_rbt_findname(dbtable->rbt, name, 0, NULL, + (void **)(void *)&stored_data); + + if (result == ISC_R_SUCCESS) { + INSIST(stored_data == db); + + (void)dns_rbt_deletename(dbtable->rbt, name, false); + } + + RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); +} + +void +dns_dbtable_adddefault(dns_dbtable_t *dbtable, dns_db_t *db) { + REQUIRE(VALID_DBTABLE(dbtable)); + REQUIRE(dbtable->default_db == NULL); + REQUIRE(dns_name_compare(dns_db_origin(db), dns_rootname) == 0); + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + + dbtable->default_db = NULL; + dns_db_attach(db, &dbtable->default_db); + + RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); +} + +void +dns_dbtable_getdefault(dns_dbtable_t *dbtable, dns_db_t **dbp) { + REQUIRE(VALID_DBTABLE(dbtable)); + REQUIRE(dbp != NULL && *dbp == NULL); + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read); + + dns_db_attach(dbtable->default_db, dbp); + + RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read); +} + +void +dns_dbtable_removedefault(dns_dbtable_t *dbtable) { + REQUIRE(VALID_DBTABLE(dbtable)); + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + + dns_db_detach(&dbtable->default_db); + + RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); +} + +isc_result_t +dns_dbtable_find(dns_dbtable_t *dbtable, const dns_name_t *name, + unsigned int options, dns_db_t **dbp) { + dns_db_t *stored_data = NULL; + isc_result_t result; + unsigned int rbtoptions = 0; + + REQUIRE(dbp != NULL && *dbp == NULL); + + if ((options & DNS_DBTABLEFIND_NOEXACT) != 0) { + rbtoptions |= DNS_RBTFIND_NOEXACT; + } + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read); + + result = dns_rbt_findname(dbtable->rbt, name, rbtoptions, NULL, + (void **)(void *)&stored_data); + + if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { + dns_db_attach(stored_data, dbp); + } else if (dbtable->default_db != NULL) { + dns_db_attach(dbtable->default_db, dbp); + result = DNS_R_PARTIALMATCH; + } else { + result = ISC_R_NOTFOUND; + } + + RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read); + + return (result); +} |