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