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
dbdetach(void * data,void * arg)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
dns_dbtable_create(isc_mem_t * mctx,dns_rdataclass_t rdclass,dns_dbtable_t ** dbtablep)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
dbtable_free(dns_dbtable_t * dbtable)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
dns_dbtable_attach(dns_dbtable_t * source,dns_dbtable_t ** targetp)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
dns_dbtable_detach(dns_dbtable_t ** dbtablep)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
dns_dbtable_add(dns_dbtable_t * dbtable,dns_db_t * db)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
dns_dbtable_remove(dns_dbtable_t * dbtable,dns_db_t * db)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
dns_dbtable_adddefault(dns_dbtable_t * dbtable,dns_db_t * db)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
dns_dbtable_getdefault(dns_dbtable_t * dbtable,dns_db_t ** dbp)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
dns_dbtable_removedefault(dns_dbtable_t * dbtable)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
dns_dbtable_find(dns_dbtable_t * dbtable,dns_name_t * name,unsigned int options,dns_db_t ** dbp)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