xref: /minix/external/bsd/bind/dist/lib/dns/dbtable.c (revision bb9622b5)
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