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