xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/dns/dbtable.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
1 /*	$NetBSD: dbtable.c,v 1.1 2024/02/18 20:57:31 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0.  If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #include <stdbool.h>
17 
18 #include <isc/mem.h>
19 #include <isc/rwlock.h>
20 #include <isc/util.h>
21 
22 #include <dns/db.h>
23 #include <dns/dbtable.h>
24 #include <dns/rbt.h>
25 #include <dns/result.h>
26 
27 struct dns_dbtable {
28 	/* Unlocked. */
29 	unsigned int magic;
30 	isc_mem_t *mctx;
31 	dns_rdataclass_t rdclass;
32 	isc_rwlock_t tree_lock;
33 	/* Protected by atomics */
34 	isc_refcount_t references;
35 	/* Locked by tree_lock. */
36 	dns_rbt_t *rbt;
37 	dns_db_t *default_db;
38 };
39 
40 #define DBTABLE_MAGIC	       ISC_MAGIC('D', 'B', '-', '-')
41 #define VALID_DBTABLE(dbtable) ISC_MAGIC_VALID(dbtable, DBTABLE_MAGIC)
42 
43 static void
dbdetach(void * data,void * arg)44 dbdetach(void *data, void *arg) {
45 	dns_db_t *db = data;
46 
47 	UNUSED(arg);
48 
49 	dns_db_detach(&db);
50 }
51 
52 isc_result_t
dns_dbtable_create(isc_mem_t * mctx,dns_rdataclass_t rdclass,dns_dbtable_t ** dbtablep)53 dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
54 		   dns_dbtable_t **dbtablep) {
55 	dns_dbtable_t *dbtable;
56 	isc_result_t result;
57 
58 	REQUIRE(mctx != NULL);
59 	REQUIRE(dbtablep != NULL && *dbtablep == NULL);
60 
61 	dbtable = isc_mem_get(mctx, sizeof(*dbtable));
62 
63 	dbtable->rbt = NULL;
64 	result = dns_rbt_create(mctx, dbdetach, NULL, &dbtable->rbt);
65 	if (result != ISC_R_SUCCESS) {
66 		goto clean1;
67 	}
68 
69 	isc_rwlock_init(&dbtable->tree_lock, 0, 0);
70 	dbtable->default_db = NULL;
71 	dbtable->mctx = NULL;
72 	isc_mem_attach(mctx, &dbtable->mctx);
73 	dbtable->rdclass = rdclass;
74 	dbtable->magic = DBTABLE_MAGIC;
75 	isc_refcount_init(&dbtable->references, 1);
76 
77 	*dbtablep = dbtable;
78 
79 	return (ISC_R_SUCCESS);
80 
81 clean1:
82 	isc_mem_putanddetach(&mctx, dbtable, sizeof(*dbtable));
83 
84 	return (result);
85 }
86 
87 static void
dbtable_free(dns_dbtable_t * dbtable)88 dbtable_free(dns_dbtable_t *dbtable) {
89 	/*
90 	 * Caller must ensure that it is safe to call.
91 	 */
92 
93 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
94 
95 	if (dbtable->default_db != NULL) {
96 		dns_db_detach(&dbtable->default_db);
97 	}
98 
99 	dns_rbt_destroy(&dbtable->rbt);
100 
101 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
102 
103 	isc_rwlock_destroy(&dbtable->tree_lock);
104 
105 	dbtable->magic = 0;
106 
107 	isc_mem_putanddetach(&dbtable->mctx, dbtable, sizeof(*dbtable));
108 }
109 
110 void
dns_dbtable_attach(dns_dbtable_t * source,dns_dbtable_t ** targetp)111 dns_dbtable_attach(dns_dbtable_t *source, dns_dbtable_t **targetp) {
112 	REQUIRE(VALID_DBTABLE(source));
113 	REQUIRE(targetp != NULL && *targetp == NULL);
114 
115 	isc_refcount_increment(&source->references);
116 
117 	*targetp = source;
118 }
119 
120 void
dns_dbtable_detach(dns_dbtable_t ** dbtablep)121 dns_dbtable_detach(dns_dbtable_t **dbtablep) {
122 	dns_dbtable_t *dbtable;
123 
124 	REQUIRE(dbtablep != NULL);
125 	dbtable = *dbtablep;
126 	*dbtablep = NULL;
127 	REQUIRE(VALID_DBTABLE(dbtable));
128 
129 	if (isc_refcount_decrement(&dbtable->references) == 1) {
130 		dbtable_free(dbtable);
131 	}
132 }
133 
134 isc_result_t
dns_dbtable_add(dns_dbtable_t * dbtable,dns_db_t * db)135 dns_dbtable_add(dns_dbtable_t *dbtable, dns_db_t *db) {
136 	isc_result_t result;
137 	dns_db_t *dbclone;
138 
139 	REQUIRE(VALID_DBTABLE(dbtable));
140 	REQUIRE(dns_db_class(db) == dbtable->rdclass);
141 
142 	dbclone = NULL;
143 	dns_db_attach(db, &dbclone);
144 
145 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
146 	result = dns_rbt_addname(dbtable->rbt, dns_db_origin(dbclone), dbclone);
147 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
148 
149 	return (result);
150 }
151 
152 void
dns_dbtable_remove(dns_dbtable_t * dbtable,dns_db_t * db)153 dns_dbtable_remove(dns_dbtable_t *dbtable, dns_db_t *db) {
154 	dns_db_t *stored_data = NULL;
155 	isc_result_t result;
156 	dns_name_t *name;
157 
158 	REQUIRE(VALID_DBTABLE(dbtable));
159 
160 	name = dns_db_origin(db);
161 
162 	/*
163 	 * There is a requirement that the association of name with db
164 	 * be verified.  With the current rbt.c this is expensive to do,
165 	 * because effectively two find operations are being done, but
166 	 * deletion is relatively infrequent.
167 	 * XXXDCL ... this could be cheaper now with dns_rbt_deletenode.
168 	 */
169 
170 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
171 
172 	result = dns_rbt_findname(dbtable->rbt, name, 0, NULL,
173 				  (void **)(void *)&stored_data);
174 
175 	if (result == ISC_R_SUCCESS) {
176 		INSIST(stored_data == db);
177 
178 		(void)dns_rbt_deletename(dbtable->rbt, name, false);
179 	}
180 
181 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
182 }
183 
184 void
dns_dbtable_adddefault(dns_dbtable_t * dbtable,dns_db_t * db)185 dns_dbtable_adddefault(dns_dbtable_t *dbtable, dns_db_t *db) {
186 	REQUIRE(VALID_DBTABLE(dbtable));
187 	REQUIRE(dbtable->default_db == NULL);
188 	REQUIRE(dns_name_compare(dns_db_origin(db), dns_rootname) == 0);
189 
190 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
191 
192 	dbtable->default_db = NULL;
193 	dns_db_attach(db, &dbtable->default_db);
194 
195 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
196 }
197 
198 void
dns_dbtable_getdefault(dns_dbtable_t * dbtable,dns_db_t ** dbp)199 dns_dbtable_getdefault(dns_dbtable_t *dbtable, dns_db_t **dbp) {
200 	REQUIRE(VALID_DBTABLE(dbtable));
201 	REQUIRE(dbp != NULL && *dbp == NULL);
202 
203 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
204 
205 	dns_db_attach(dbtable->default_db, dbp);
206 
207 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
208 }
209 
210 void
dns_dbtable_removedefault(dns_dbtable_t * dbtable)211 dns_dbtable_removedefault(dns_dbtable_t *dbtable) {
212 	REQUIRE(VALID_DBTABLE(dbtable));
213 
214 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
215 
216 	dns_db_detach(&dbtable->default_db);
217 
218 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
219 }
220 
221 isc_result_t
dns_dbtable_find(dns_dbtable_t * dbtable,const dns_name_t * name,unsigned int options,dns_db_t ** dbp)222 dns_dbtable_find(dns_dbtable_t *dbtable, const dns_name_t *name,
223 		 unsigned int options, dns_db_t **dbp) {
224 	dns_db_t *stored_data = NULL;
225 	isc_result_t result;
226 	unsigned int rbtoptions = 0;
227 
228 	REQUIRE(dbp != NULL && *dbp == NULL);
229 
230 	if ((options & DNS_DBTABLEFIND_NOEXACT) != 0) {
231 		rbtoptions |= DNS_RBTFIND_NOEXACT;
232 	}
233 
234 	RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
235 
236 	result = dns_rbt_findname(dbtable->rbt, name, rbtoptions, NULL,
237 				  (void **)(void *)&stored_data);
238 
239 	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
240 		dns_db_attach(stored_data, dbp);
241 	} else if (dbtable->default_db != NULL) {
242 		dns_db_attach(dbtable->default_db, dbp);
243 		result = DNS_R_PARTIALMATCH;
244 	} else {
245 		result = ISC_R_NOTFOUND;
246 	}
247 
248 	RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
249 
250 	return (result);
251 }
252