BIND - Update BIND to 9.5.2
[dragonfly.git] / contrib / bind-9.5.2 / lib / dns / dbtable.c
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
38 struct 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
55 static void
56 dbdetach(void *data, void *arg) {
57         dns_db_t *db = data;
58
59         UNUSED(arg);
60
61         dns_db_detach(&db);
62 }
63
64 isc_result_t
65 dns_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
113 static inline void
114 dbtable_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
135 void
136 dns_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
151 void
152 dns_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
175 isc_result_t
176 dns_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
193 void
194 dns_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
225 void
226 dns_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
239 void
240 dns_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
251 void
252 dns_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
262 isc_result_t
263 dns_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 }