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