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