Blame lib/dns-pkcs11/dbtable.c

Packit e9919e
/*
Packit e9919e
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
Packit e9919e
 *
Packit e9919e
 * This Source Code Form is subject to the terms of the Mozilla Public
Packit e9919e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit e9919e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
Packit e9919e
 *
Packit e9919e
 * See the COPYRIGHT file distributed with this work for additional
Packit e9919e
 * information regarding copyright ownership.
Packit e9919e
 */
Packit e9919e
Packit e9919e
#include <config.h>
Packit e9919e
Packit e9919e
#include <stdbool.h>
Packit e9919e
Packit e9919e
#include <isc/mem.h>
Packit e9919e
#include <isc/rwlock.h>
Packit e9919e
#include <isc/util.h>
Packit e9919e
Packit e9919e
#include <dns/dbtable.h>
Packit e9919e
#include <dns/db.h>
Packit e9919e
#include <dns/rbt.h>
Packit e9919e
#include <dns/result.h>
Packit e9919e
Packit e9919e
struct dns_dbtable {
Packit e9919e
	/* Unlocked. */
Packit e9919e
	unsigned int		magic;
Packit e9919e
	isc_mem_t *		mctx;
Packit e9919e
	dns_rdataclass_t	rdclass;
Packit e9919e
	isc_mutex_t		lock;
Packit e9919e
	isc_rwlock_t		tree_lock;
Packit e9919e
	/* Locked by lock. */
Packit e9919e
	unsigned int		references;
Packit e9919e
	/* Locked by tree_lock. */
Packit e9919e
	dns_rbt_t *		rbt;
Packit e9919e
	dns_db_t *		default_db;
Packit e9919e
};
Packit e9919e
Packit e9919e
#define DBTABLE_MAGIC		ISC_MAGIC('D', 'B', '-', '-')
Packit e9919e
#define VALID_DBTABLE(dbtable)	ISC_MAGIC_VALID(dbtable, DBTABLE_MAGIC)
Packit e9919e
Packit e9919e
static void
Packit e9919e
dbdetach(void *data, void *arg) {
Packit e9919e
	dns_db_t *db = data;
Packit e9919e
Packit e9919e
	UNUSED(arg);
Packit e9919e
Packit e9919e
	dns_db_detach(&db);
Packit e9919e
}
Packit e9919e
Packit e9919e
isc_result_t
Packit e9919e
dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
Packit e9919e
		   dns_dbtable_t **dbtablep)
Packit e9919e
{
Packit e9919e
	dns_dbtable_t *dbtable;
Packit e9919e
	isc_result_t result;
Packit e9919e
Packit e9919e
	REQUIRE(mctx != NULL);
Packit e9919e
	REQUIRE(dbtablep != NULL && *dbtablep == NULL);
Packit e9919e
Packit e9919e
	dbtable = (dns_dbtable_t *)isc_mem_get(mctx, sizeof(*dbtable));
Packit e9919e
	if (dbtable == NULL)
Packit e9919e
		return (ISC_R_NOMEMORY);
Packit e9919e
Packit e9919e
	dbtable->rbt = NULL;
Packit e9919e
	result = dns_rbt_create(mctx, dbdetach, NULL, &dbtable->rbt);
Packit e9919e
	if (result != ISC_R_SUCCESS)
Packit e9919e
		goto clean1;
Packit e9919e
Packit e9919e
	result = isc_mutex_init(&dbtable->lock);
Packit e9919e
	if (result != ISC_R_SUCCESS)
Packit e9919e
		goto clean2;
Packit e9919e
Packit e9919e
	result = isc_rwlock_init(&dbtable->tree_lock, 0, 0);
Packit e9919e
	if (result != ISC_R_SUCCESS)
Packit e9919e
		goto clean3;
Packit e9919e
Packit e9919e
	dbtable->default_db = NULL;
Packit e9919e
	dbtable->mctx = NULL;
Packit e9919e
	isc_mem_attach(mctx, &dbtable->mctx);
Packit e9919e
	dbtable->rdclass = rdclass;
Packit e9919e
	dbtable->magic = DBTABLE_MAGIC;
Packit e9919e
	dbtable->references = 1;
Packit e9919e
Packit e9919e
	*dbtablep = dbtable;
Packit e9919e
Packit e9919e
	return (ISC_R_SUCCESS);
Packit e9919e
Packit e9919e
 clean3:
Packit e9919e
	DESTROYLOCK(&dbtable->lock);
Packit e9919e
Packit e9919e
 clean2:
Packit e9919e
	dns_rbt_destroy(&dbtable->rbt);
Packit e9919e
Packit e9919e
 clean1:
Packit e9919e
	isc_mem_putanddetach(&mctx, dbtable, sizeof(*dbtable));
Packit e9919e
Packit e9919e
	return (result);
Packit e9919e
}
Packit e9919e
Packit e9919e
static inline void
Packit e9919e
dbtable_free(dns_dbtable_t *dbtable) {
Packit e9919e
	/*
Packit e9919e
	 * Caller must ensure that it is safe to call.
Packit e9919e
	 */
Packit e9919e
Packit e9919e
	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
Packit e9919e
	if (dbtable->default_db != NULL)
Packit e9919e
		dns_db_detach(&dbtable->default_db);
Packit e9919e
Packit e9919e
	dns_rbt_destroy(&dbtable->rbt);
Packit e9919e
Packit e9919e
	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
Packit e9919e
	isc_rwlock_destroy(&dbtable->tree_lock);
Packit e9919e
Packit e9919e
	dbtable->magic = 0;
Packit e9919e
Packit e9919e
	isc_mem_putanddetach(&dbtable->mctx, dbtable, sizeof(*dbtable));
Packit e9919e
}
Packit e9919e
Packit e9919e
void
Packit e9919e
dns_dbtable_attach(dns_dbtable_t *source, dns_dbtable_t **targetp) {
Packit e9919e
	REQUIRE(VALID_DBTABLE(source));
Packit e9919e
	REQUIRE(targetp != NULL && *targetp == NULL);
Packit e9919e
Packit e9919e
	LOCK(&source->lock);
Packit e9919e
Packit e9919e
	INSIST(source->references > 0);
Packit e9919e
	source->references++;
Packit e9919e
	INSIST(source->references != 0);
Packit e9919e
Packit e9919e
	UNLOCK(&source->lock);
Packit e9919e
Packit e9919e
	*targetp = source;
Packit e9919e
}
Packit e9919e
Packit e9919e
void
Packit e9919e
dns_dbtable_detach(dns_dbtable_t **dbtablep) {
Packit e9919e
	dns_dbtable_t *dbtable;
Packit e9919e
	bool free_dbtable = false;
Packit e9919e
Packit e9919e
	REQUIRE(dbtablep != NULL);
Packit e9919e
	dbtable = *dbtablep;
Packit e9919e
	REQUIRE(VALID_DBTABLE(dbtable));
Packit e9919e
Packit e9919e
	LOCK(&dbtable->lock);
Packit e9919e
Packit e9919e
	INSIST(dbtable->references > 0);
Packit e9919e
	dbtable->references--;
Packit e9919e
	if (dbtable->references == 0)
Packit e9919e
		free_dbtable = true;
Packit e9919e
Packit e9919e
	UNLOCK(&dbtable->lock);
Packit e9919e
Packit e9919e
	if (free_dbtable)
Packit e9919e
		dbtable_free(dbtable);
Packit e9919e
Packit e9919e
	*dbtablep = NULL;
Packit e9919e
}
Packit e9919e
Packit e9919e
isc_result_t
Packit e9919e
dns_dbtable_add(dns_dbtable_t *dbtable, dns_db_t *db) {
Packit e9919e
	isc_result_t result;
Packit e9919e
	dns_db_t *dbclone;
Packit e9919e
Packit e9919e
	REQUIRE(VALID_DBTABLE(dbtable));
Packit e9919e
	REQUIRE(dns_db_class(db) == dbtable->rdclass);
Packit e9919e
Packit e9919e
	dbclone = NULL;
Packit e9919e
	dns_db_attach(db, &dbclone);
Packit e9919e
Packit e9919e
	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
	result = dns_rbt_addname(dbtable->rbt, dns_db_origin(dbclone), dbclone);
Packit e9919e
	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
Packit e9919e
	return (result);
Packit e9919e
}
Packit e9919e
Packit e9919e
void
Packit e9919e
dns_dbtable_remove(dns_dbtable_t *dbtable, dns_db_t *db) {
Packit e9919e
	dns_db_t *stored_data = NULL;
Packit e9919e
	isc_result_t result;
Packit e9919e
	dns_name_t *name;
Packit e9919e
Packit e9919e
	REQUIRE(VALID_DBTABLE(dbtable));
Packit e9919e
Packit e9919e
	name = dns_db_origin(db);
Packit e9919e
Packit e9919e
	/*
Packit e9919e
	 * There is a requirement that the association of name with db
Packit e9919e
	 * be verified.  With the current rbt.c this is expensive to do,
Packit e9919e
	 * because effectively two find operations are being done, but
Packit e9919e
	 * deletion is relatively infrequent.
Packit e9919e
	 * XXXDCL ... this could be cheaper now with dns_rbt_deletenode.
Packit e9919e
	 */
Packit e9919e
Packit e9919e
	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
Packit e9919e
	result = dns_rbt_findname(dbtable->rbt, name, 0, NULL,
Packit e9919e
				  (void **) (void *)&stored_data);
Packit e9919e
Packit e9919e
	if (result == ISC_R_SUCCESS) {
Packit e9919e
		INSIST(stored_data == db);
Packit e9919e
Packit e9919e
		(void)dns_rbt_deletename(dbtable->rbt, name, false);
Packit e9919e
	}
Packit e9919e
Packit e9919e
	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
}
Packit e9919e
Packit e9919e
void
Packit e9919e
dns_dbtable_adddefault(dns_dbtable_t *dbtable, dns_db_t *db) {
Packit e9919e
	REQUIRE(VALID_DBTABLE(dbtable));
Packit e9919e
	REQUIRE(dbtable->default_db == NULL);
Packit e9919e
	REQUIRE(dns_name_compare(dns_db_origin(db), dns_rootname) == 0);
Packit e9919e
Packit e9919e
	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
Packit e9919e
	dbtable->default_db = NULL;
Packit e9919e
	dns_db_attach(db, &dbtable->default_db);
Packit e9919e
Packit e9919e
	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
}
Packit e9919e
Packit e9919e
void
Packit e9919e
dns_dbtable_getdefault(dns_dbtable_t *dbtable, dns_db_t **dbp) {
Packit e9919e
	REQUIRE(VALID_DBTABLE(dbtable));
Packit e9919e
	REQUIRE(dbp != NULL && *dbp == NULL);
Packit e9919e
Packit e9919e
	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
Packit e9919e
Packit e9919e
	dns_db_attach(dbtable->default_db, dbp);
Packit e9919e
Packit e9919e
	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
Packit e9919e
}
Packit e9919e
Packit e9919e
void
Packit e9919e
dns_dbtable_removedefault(dns_dbtable_t *dbtable) {
Packit e9919e
	REQUIRE(VALID_DBTABLE(dbtable));
Packit e9919e
Packit e9919e
	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
Packit e9919e
	dns_db_detach(&dbtable->default_db);
Packit e9919e
Packit e9919e
	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
Packit e9919e
}
Packit e9919e
Packit e9919e
isc_result_t
Packit e9919e
dns_dbtable_find(dns_dbtable_t *dbtable, dns_name_t *name,
Packit e9919e
		 unsigned int options, dns_db_t **dbp)
Packit e9919e
{
Packit e9919e
	dns_db_t *stored_data = NULL;
Packit e9919e
	isc_result_t result;
Packit e9919e
	unsigned int rbtoptions = 0;
Packit e9919e
Packit e9919e
	REQUIRE(dbp != NULL && *dbp == NULL);
Packit e9919e
Packit e9919e
	if ((options & DNS_DBTABLEFIND_NOEXACT) != 0)
Packit e9919e
		rbtoptions |= DNS_RBTFIND_NOEXACT;
Packit e9919e
Packit e9919e
	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
Packit e9919e
Packit e9919e
	result = dns_rbt_findname(dbtable->rbt, name, rbtoptions, NULL,
Packit e9919e
				  (void **) (void *)&stored_data);
Packit e9919e
Packit e9919e
	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
Packit e9919e
		dns_db_attach(stored_data, dbp);
Packit e9919e
	else if (dbtable->default_db != NULL) {
Packit e9919e
		dns_db_attach(dbtable->default_db, dbp);
Packit e9919e
		result = DNS_R_PARTIALMATCH;
Packit e9919e
	} else
Packit e9919e
		result = ISC_R_NOTFOUND;
Packit e9919e
Packit e9919e
	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
Packit e9919e
Packit e9919e
	return (result);
Packit e9919e
}