summaryrefslogtreecommitdiff
path: root/lib/dns/dbtable.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/dbtable.c')
-rw-r--r--lib/dns/dbtable.c295
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);
+}