Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: dbtable.c,v 1.1 2024/02/18 20:57:31 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0.  If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #include <stdbool.h>
     17 
     18 #include <isc/mem.h>
     19 #include <isc/rwlock.h>
     20 #include <isc/util.h>
     21 
     22 #include <dns/db.h>
     23 #include <dns/dbtable.h>
     24 #include <dns/rbt.h>
     25 #include <dns/result.h>
     26 
     27 struct dns_dbtable {
     28 	/* Unlocked. */
     29 	unsigned int magic;
     30 	isc_mem_t *mctx;
     31 	dns_rdataclass_t rdclass;
     32 	isc_rwlock_t tree_lock;
     33 	/* Protected by atomics */
     34 	isc_refcount_t references;
     35 	/* Locked by tree_lock. */
     36 	dns_rbt_t *rbt;
     37 	dns_db_t *default_db;
     38 };
     39 
     40 #define DBTABLE_MAGIC	       ISC_MAGIC('D', 'B', '-', '-')
     41 #define VALID_DBTABLE(dbtable) ISC_MAGIC_VALID(dbtable, DBTABLE_MAGIC)
     42 
     43 static void
     44 dbdetach(void *data, void *arg) {
     45 	dns_db_t *db = data;
     46 
     47 	UNUSED(arg);
     48 
     49 	dns_db_detach(&db);
     50 }
     51 
     52 isc_result_t
     53 dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
     54 		   dns_dbtable_t **dbtablep) {
     55 	dns_dbtable_t *dbtable;
     56 	isc_result_t result;
     57 
     58 	REQUIRE(mctx != NULL);
     59 	REQUIRE(dbtablep != NULL && *dbtablep == NULL);
     60 
     61 	dbtable = isc_mem_get(mctx, sizeof(*dbtable));
     62 
     63 	dbtable->rbt = NULL;
     64 	result = dns_rbt_create(mctx, dbdetach, NULL, &dbtable->rbt);
     65 	if (result != ISC_R_SUCCESS) {
     66 		goto clean1;
     67 	}
     68 
     69 	isc_rwlock_init(&dbtable->tree_lock, 0, 0);
     70 	dbtable->default_db = NULL;
     71 	dbtable->mctx = NULL;
     72 	isc_mem_attach(mctx, &dbtable->mctx);
     73 	dbtable->rdclass = rdclass;
     74 	dbtable->magic = DBTABLE_MAGIC;
     75 	isc_refcount_init(&dbtable->references, 1);
     76 
     77 	*dbtablep = dbtable;
     78 
     79 	return (ISC_R_SUCCESS);
     80 
     81 clean1:
     82 	isc_mem_putanddetach(&mctx, dbtable, sizeof(*dbtable));
     83 
     84 	return (result);
     85 }
     86 
     87 static void
     88 dbtable_free(dns_dbtable_t *dbtable) {
     89 	/*
     90 	 * Caller must ensure that it is safe to call.
     91 	 */
     92 
     93 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
     94 
     95 	if (dbtable->default_db != NULL) {
     96 		dns_db_detach(&dbtable->default_db);
     97 	}
     98 
     99 	dns_rbt_destroy(&dbtable->rbt);
    100 
    101 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    102 
    103 	isc_rwlock_destroy(&dbtable->tree_lock);
    104 
    105 	dbtable->magic = 0;
    106 
    107 	isc_mem_putanddetach(&dbtable->mctx, dbtable, sizeof(*dbtable));
    108 }
    109 
    110 void
    111 dns_dbtable_attach(dns_dbtable_t *source, dns_dbtable_t **targetp) {
    112 	REQUIRE(VALID_DBTABLE(source));
    113 	REQUIRE(targetp != NULL && *targetp == NULL);
    114 
    115 	isc_refcount_increment(&source->references);
    116 
    117 	*targetp = source;
    118 }
    119 
    120 void
    121 dns_dbtable_detach(dns_dbtable_t **dbtablep) {
    122 	dns_dbtable_t *dbtable;
    123 
    124 	REQUIRE(dbtablep != NULL);
    125 	dbtable = *dbtablep;
    126 	*dbtablep = NULL;
    127 	REQUIRE(VALID_DBTABLE(dbtable));
    128 
    129 	if (isc_refcount_decrement(&dbtable->references) == 1) {
    130 		dbtable_free(dbtable);
    131 	}
    132 }
    133 
    134 isc_result_t
    135 dns_dbtable_add(dns_dbtable_t *dbtable, dns_db_t *db) {
    136 	isc_result_t result;
    137 	dns_db_t *dbclone;
    138 
    139 	REQUIRE(VALID_DBTABLE(dbtable));
    140 	REQUIRE(dns_db_class(db) == dbtable->rdclass);
    141 
    142 	dbclone = NULL;
    143 	dns_db_attach(db, &dbclone);
    144 
    145 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    146 	result = dns_rbt_addname(dbtable->rbt, dns_db_origin(dbclone), dbclone);
    147 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    148 
    149 	return (result);
    150 }
    151 
    152 void
    153 dns_dbtable_remove(dns_dbtable_t *dbtable, dns_db_t *db) {
    154 	dns_db_t *stored_data = NULL;
    155 	isc_result_t result;
    156 	dns_name_t *name;
    157 
    158 	REQUIRE(VALID_DBTABLE(dbtable));
    159 
    160 	name = dns_db_origin(db);
    161 
    162 	/*
    163 	 * There is a requirement that the association of name with db
    164 	 * be verified.  With the current rbt.c this is expensive to do,
    165 	 * because effectively two find operations are being done, but
    166 	 * deletion is relatively infrequent.
    167 	 * XXXDCL ... this could be cheaper now with dns_rbt_deletenode.
    168 	 */
    169 
    170 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    171 
    172 	result = dns_rbt_findname(dbtable->rbt, name, 0, NULL,
    173 				  (void **)(void *)&stored_data);
    174 
    175 	if (result == ISC_R_SUCCESS) {
    176 		INSIST(stored_data == db);
    177 
    178 		(void)dns_rbt_deletename(dbtable->rbt, name, false);
    179 	}
    180 
    181 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    182 }
    183 
    184 void
    185 dns_dbtable_adddefault(dns_dbtable_t *dbtable, dns_db_t *db) {
    186 	REQUIRE(VALID_DBTABLE(dbtable));
    187 	REQUIRE(dbtable->default_db == NULL);
    188 	REQUIRE(dns_name_compare(dns_db_origin(db), dns_rootname) == 0);
    189 
    190 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    191 
    192 	dbtable->default_db = NULL;
    193 	dns_db_attach(db, &dbtable->default_db);
    194 
    195 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    196 }
    197 
    198 void
    199 dns_dbtable_getdefault(dns_dbtable_t *dbtable, dns_db_t **dbp) {
    200 	REQUIRE(VALID_DBTABLE(dbtable));
    201 	REQUIRE(dbp != NULL && *dbp == NULL);
    202 
    203 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
    204 
    205 	dns_db_attach(dbtable->default_db, dbp);
    206 
    207 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
    208 }
    209 
    210 void
    211 dns_dbtable_removedefault(dns_dbtable_t *dbtable) {
    212 	REQUIRE(VALID_DBTABLE(dbtable));
    213 
    214 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    215 
    216 	dns_db_detach(&dbtable->default_db);
    217 
    218 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
    219 }
    220 
    221 isc_result_t
    222 dns_dbtable_find(dns_dbtable_t *dbtable, const dns_name_t *name,
    223 		 unsigned int options, dns_db_t **dbp) {
    224 	dns_db_t *stored_data = NULL;
    225 	isc_result_t result;
    226 	unsigned int rbtoptions = 0;
    227 
    228 	REQUIRE(dbp != NULL && *dbp == NULL);
    229 
    230 	if ((options & DNS_DBTABLEFIND_NOEXACT) != 0) {
    231 		rbtoptions |= DNS_RBTFIND_NOEXACT;
    232 	}
    233 
    234 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
    235 
    236 	result = dns_rbt_findname(dbtable->rbt, name, rbtoptions, NULL,
    237 				  (void **)(void *)&stored_data);
    238 
    239 	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
    240 		dns_db_attach(stored_data, dbp);
    241 	} else if (dbtable->default_db != NULL) {
    242 		dns_db_attach(dbtable->default_db, dbp);
    243 		result = DNS_R_PARTIALMATCH;
    244 	} else {
    245 		result = ISC_R_NOTFOUND;
    246 	}
    247 
    248 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
    249 
    250 	return (result);
    251 }
    252