diff options
Diffstat (limited to 'lib/dns/dbtable.c')
-rw-r--r-- | lib/dns/dbtable.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/lib/dns/dbtable.c b/lib/dns/dbtable.c new file mode 100644 index 00000000..e21d5792 --- /dev/null +++ b/lib/dns/dbtable.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 1999, 2000 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * $Id: dbtable.c,v 1.13 2000/02/03 23:43:46 halley Exp $ + */ + +/* + * Principal Author: DCL + */ + +#include <config.h> + +#include <isc/assertions.h> +#include <isc/rwlock.h> +#include <isc/util.h> + +#include <dns/dbtable.h> +#include <dns/db.h> +#include <dns/rbt.h> + +struct dns_dbtable { + /* Unlocked. */ + unsigned int magic; + isc_mem_t * mctx; + dns_rdataclass_t rdclass; + isc_mutex_t lock; + isc_rwlock_t tree_lock; + /* Locked by lock. */ + unsigned int references; + /* Locked by tree_lock. */ + dns_rbt_t * rbt; + dns_db_t * default_db; +}; + +#define DBTABLE_MAGIC 0x44422D2DU /* DB--. */ +#define VALID_DBTABLE(dbtable) ((dbtable) != NULL && \ + (dbtable)->magic == DBTABLE_MAGIC) + +static void +dbdetach(void *data, void *arg) { + dns_db_t *db = data; + + (void)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; + isc_result_t iresult; + + REQUIRE(mctx != NULL); + REQUIRE(dbtablep != NULL && *dbtablep == NULL); + + dbtable = (dns_dbtable_t *)isc_mem_get(mctx, sizeof(*dbtable)); + if (dbtable == NULL) + return (DNS_R_NOMEMORY); + + dbtable->rbt = NULL; + result = dns_rbt_create(mctx, dbdetach, NULL, &dbtable->rbt); + if (result != DNS_R_SUCCESS) + goto clean1; + + iresult = isc_mutex_init(&dbtable->lock); + if (iresult != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_lock_init() failed: %s", + isc_result_totext(result)); + result = DNS_R_UNEXPECTED; + goto clean2; + } + + iresult = isc_rwlock_init(&dbtable->tree_lock, 0, 0); + if (iresult != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_rwlock_init() failed: %s", + isc_result_totext(result)); + result = DNS_R_UNEXPECTED; + goto clean3; + } + + dbtable->default_db = NULL; + dbtable->mctx = mctx; + dbtable->rdclass = rdclass; + dbtable->magic = DBTABLE_MAGIC; + dbtable->references = 1; + + *dbtablep = dbtable; + + return (DNS_R_SUCCESS); + + clean3: + (void)isc_mutex_destroy(&dbtable->lock); + + clean2: + dns_rbt_destroy(&dbtable->rbt); + + clean1: + isc_mem_put(mctx, dbtable, sizeof(*dbtable)); + + return (result); +} + +static inline 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_put(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); + + LOCK(&source->lock); + + INSIST(source->references > 0); + source->references++; + INSIST(source->references != 0); + + UNLOCK(&source->lock); + + *targetp = source; +} + +void +dns_dbtable_detach(dns_dbtable_t **dbtablep) { + dns_dbtable_t *dbtable; + isc_boolean_t free_dbtable = ISC_FALSE; + + REQUIRE(dbtablep != NULL); + dbtable = *dbtablep; + REQUIRE(VALID_DBTABLE(dbtable)); + + LOCK(&dbtable->lock); + + INSIST(dbtable->references > 0); + dbtable->references--; + if (dbtable->references == 0) + free_dbtable = ISC_TRUE; + + UNLOCK(&dbtable->lock); + + if (free_dbtable) + dbtable_free(dbtable); + + *dbtablep = NULL; +} + +isc_result_t +dns_dbtable_add(dns_dbtable_t *dbtable, dns_db_t *db) { + isc_result_t result; + dns_db_t *clone; + + REQUIRE(VALID_DBTABLE(dbtable)); + REQUIRE(dns_db_class(db) == dbtable->rdclass); + + clone = NULL; + dns_db_attach(db, &clone); + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + result = dns_rbt_addname(dbtable->rbt, dns_db_origin(clone), clone); + 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. + */ + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); + + result = dns_rbt_findname(dbtable->rbt, name, NULL, + (void **)&stored_data); + + if (result == DNS_R_SUCCESS) { + INSIST(stored_data == db); + + dns_rbt_deletename(dbtable->rbt, name, ISC_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, dns_name_t *name, dns_db_t **dbp) { + dns_db_t *stored_data = NULL; + isc_result_t result; + + REQUIRE(dbp != NULL && *dbp == NULL); + + RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read); + + result = dns_rbt_findname(dbtable->rbt, name, NULL, + (void **)&stored_data); + + if (result == DNS_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 = DNS_R_NOTFOUND; + + RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read); + + return (result); +} |