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