1/* 2 * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1999-2001 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* 19 * $Id: dbtable.c,v 1.33 2007/06/19 23:47:16 tbox Exp $ 20 */ 21 22/*! \file 23 * \author 24 * Principal Author: DCL 25 */ 26 27#include <config.h> 28 29#include <isc/mem.h> 30#include <isc/rwlock.h> 31#include <isc/util.h> 32 33#include <dns/dbtable.h> 34#include <dns/db.h> 35#include <dns/rbt.h> 36#include <dns/result.h> 37 38struct dns_dbtable { 39 /* Unlocked. */ 40 unsigned int magic; 41 isc_mem_t * mctx; 42 dns_rdataclass_t rdclass; 43 isc_mutex_t lock; 44 isc_rwlock_t tree_lock; 45 /* Locked by lock. */ 46 unsigned int references; 47 /* Locked by tree_lock. */ 48 dns_rbt_t * rbt; 49 dns_db_t * default_db; 50}; 51 52#define DBTABLE_MAGIC ISC_MAGIC('D', 'B', '-', '-') 53#define VALID_DBTABLE(dbtable) ISC_MAGIC_VALID(dbtable, DBTABLE_MAGIC) 54 55static void 56dbdetach(void *data, void *arg) { 57 dns_db_t *db = data; 58 59 UNUSED(arg); 60 61 dns_db_detach(&db); 62} 63 64isc_result_t 65dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, 66 dns_dbtable_t **dbtablep) 67{ 68 dns_dbtable_t *dbtable; 69 isc_result_t result; 70 71 REQUIRE(mctx != NULL); 72 REQUIRE(dbtablep != NULL && *dbtablep == NULL); 73 74 dbtable = (dns_dbtable_t *)isc_mem_get(mctx, sizeof(*dbtable)); 75 if (dbtable == NULL) 76 return (ISC_R_NOMEMORY); 77 78 dbtable->rbt = NULL; 79 result = dns_rbt_create(mctx, dbdetach, NULL, &dbtable->rbt); 80 if (result != ISC_R_SUCCESS) 81 goto clean1; 82 83 result = isc_mutex_init(&dbtable->lock); 84 if (result != ISC_R_SUCCESS) 85 goto clean2; 86 87 result = isc_rwlock_init(&dbtable->tree_lock, 0, 0); 88 if (result != ISC_R_SUCCESS) 89 goto clean3; 90 91 dbtable->default_db = NULL; 92 dbtable->mctx = mctx; 93 dbtable->rdclass = rdclass; 94 dbtable->magic = DBTABLE_MAGIC; 95 dbtable->references = 1; 96 97 *dbtablep = dbtable; 98 99 return (ISC_R_SUCCESS); 100 101 clean3: 102 DESTROYLOCK(&dbtable->lock); 103 104 clean2: 105 dns_rbt_destroy(&dbtable->rbt); 106 107 clean1: 108 isc_mem_put(mctx, dbtable, sizeof(*dbtable)); 109 110 return (result); 111} 112 113static inline void 114dbtable_free(dns_dbtable_t *dbtable) { 115 /* 116 * Caller must ensure that it is safe to call. 117 */ 118 119 RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 120 121 if (dbtable->default_db != NULL) 122 dns_db_detach(&dbtable->default_db); 123 124 dns_rbt_destroy(&dbtable->rbt); 125 126 RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 127 128 isc_rwlock_destroy(&dbtable->tree_lock); 129 130 dbtable->magic = 0; 131 132 isc_mem_put(dbtable->mctx, dbtable, sizeof(*dbtable)); 133} 134 135void 136dns_dbtable_attach(dns_dbtable_t *source, dns_dbtable_t **targetp) { 137 REQUIRE(VALID_DBTABLE(source)); 138 REQUIRE(targetp != NULL && *targetp == NULL); 139 140 LOCK(&source->lock); 141 142 INSIST(source->references > 0); 143 source->references++; 144 INSIST(source->references != 0); 145 146 UNLOCK(&source->lock); 147 148 *targetp = source; 149} 150 151void 152dns_dbtable_detach(dns_dbtable_t **dbtablep) { 153 dns_dbtable_t *dbtable; 154 isc_boolean_t free_dbtable = ISC_FALSE; 155 156 REQUIRE(dbtablep != NULL); 157 dbtable = *dbtablep; 158 REQUIRE(VALID_DBTABLE(dbtable)); 159 160 LOCK(&dbtable->lock); 161 162 INSIST(dbtable->references > 0); 163 dbtable->references--; 164 if (dbtable->references == 0) 165 free_dbtable = ISC_TRUE; 166 167 UNLOCK(&dbtable->lock); 168 169 if (free_dbtable) 170 dbtable_free(dbtable); 171 172 *dbtablep = NULL; 173} 174 175isc_result_t 176dns_dbtable_add(dns_dbtable_t *dbtable, dns_db_t *db) { 177 isc_result_t result; 178 dns_db_t *clone; 179 180 REQUIRE(VALID_DBTABLE(dbtable)); 181 REQUIRE(dns_db_class(db) == dbtable->rdclass); 182 183 clone = NULL; 184 dns_db_attach(db, &clone); 185 186 RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 187 result = dns_rbt_addname(dbtable->rbt, dns_db_origin(clone), clone); 188 RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 189 190 return (result); 191} 192 193void 194dns_dbtable_remove(dns_dbtable_t *dbtable, dns_db_t *db) { 195 dns_db_t *stored_data = NULL; 196 isc_result_t result; 197 dns_name_t *name; 198 199 REQUIRE(VALID_DBTABLE(dbtable)); 200 201 name = dns_db_origin(db); 202 203 /* 204 * There is a requirement that the association of name with db 205 * be verified. With the current rbt.c this is expensive to do, 206 * because effectively two find operations are being done, but 207 * deletion is relatively infrequent. 208 * XXXDCL ... this could be cheaper now with dns_rbt_deletenode. 209 */ 210 211 RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 212 213 result = dns_rbt_findname(dbtable->rbt, name, 0, NULL, 214 (void **) (void *)&stored_data); 215 216 if (result == ISC_R_SUCCESS) { 217 INSIST(stored_data == db); 218 219 (void)dns_rbt_deletename(dbtable->rbt, name, ISC_FALSE); 220 } 221 222 RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 223} 224 225void 226dns_dbtable_adddefault(dns_dbtable_t *dbtable, dns_db_t *db) { 227 REQUIRE(VALID_DBTABLE(dbtable)); 228 REQUIRE(dbtable->default_db == NULL); 229 REQUIRE(dns_name_compare(dns_db_origin(db), dns_rootname) == 0); 230 231 RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 232 233 dbtable->default_db = NULL; 234 dns_db_attach(db, &dbtable->default_db); 235 236 RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 237} 238 239void 240dns_dbtable_getdefault(dns_dbtable_t *dbtable, dns_db_t **dbp) { 241 REQUIRE(VALID_DBTABLE(dbtable)); 242 REQUIRE(dbp != NULL && *dbp == NULL); 243 244 RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read); 245 246 dns_db_attach(dbtable->default_db, dbp); 247 248 RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read); 249} 250 251void 252dns_dbtable_removedefault(dns_dbtable_t *dbtable) { 253 REQUIRE(VALID_DBTABLE(dbtable)); 254 255 RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 256 257 dns_db_detach(&dbtable->default_db); 258 259 RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write); 260} 261 262isc_result_t 263dns_dbtable_find(dns_dbtable_t *dbtable, dns_name_t *name, 264 unsigned int options, dns_db_t **dbp) 265{ 266 dns_db_t *stored_data = NULL; 267 isc_result_t result; 268 unsigned int rbtoptions = 0; 269 270 REQUIRE(dbp != NULL && *dbp == NULL); 271 272 if ((options & DNS_DBTABLEFIND_NOEXACT) != 0) 273 rbtoptions |= DNS_RBTFIND_NOEXACT; 274 275 RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read); 276 277 result = dns_rbt_findname(dbtable->rbt, name, rbtoptions, NULL, 278 (void **) (void *)&stored_data); 279 280 if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) 281 dns_db_attach(stored_data, dbp); 282 else if (dbtable->default_db != NULL) { 283 dns_db_attach(dbtable->default_db, dbp); 284 result = DNS_R_PARTIALMATCH; 285 } else 286 result = ISC_R_NOTFOUND; 287 288 RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read); 289 290 return (result); 291} 292