dyndb.c revision 1.11 1 /* $NetBSD: dyndb.c,v 1.11 2024/02/21 22:52:06 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 <string.h>
17 #include <uv.h>
18
19 #include <isc/buffer.h>
20 #include <isc/mem.h>
21 #include <isc/mutex.h>
22 #include <isc/once.h>
23 #include <isc/region.h>
24 #include <isc/result.h>
25 #include <isc/task.h>
26 #include <isc/types.h>
27 #include <isc/util.h>
28
29 #include <dns/dyndb.h>
30 #include <dns/log.h>
31 #include <dns/types.h>
32 #include <dns/view.h>
33 #include <dns/zone.h>
34
35 #define CHECK(op) \
36 do { \
37 result = (op); \
38 if (result != ISC_R_SUCCESS) \
39 goto cleanup; \
40 } while (0)
41
42 typedef struct dyndb_implementation dyndb_implementation_t;
43 struct dyndb_implementation {
44 isc_mem_t *mctx;
45 uv_lib_t handle;
46 dns_dyndb_register_t *register_func;
47 dns_dyndb_destroy_t *destroy_func;
48 char *name;
49 void *inst;
50 LINK(dyndb_implementation_t) link;
51 };
52
53 /*
54 * List of dyndb implementations. Locked by dyndb_lock.
55 *
56 * These are stored here so they can be cleaned up on shutdown.
57 * (The order in which they are stored is not important.)
58 */
59 static LIST(dyndb_implementation_t) dyndb_implementations;
60
61 /* Locks dyndb_implementations. */
62 static isc_mutex_t dyndb_lock;
63 static isc_once_t once = ISC_ONCE_INIT;
64
65 static void
66 dyndb_initialize(void) {
67 isc_mutex_init(&dyndb_lock);
68 INIT_LIST(dyndb_implementations);
69 }
70
71 static dyndb_implementation_t *
72 impfind(const char *name) {
73 dyndb_implementation_t *imp;
74
75 for (imp = ISC_LIST_HEAD(dyndb_implementations); imp != NULL;
76 imp = ISC_LIST_NEXT(imp, link))
77 {
78 if (strcasecmp(name, imp->name) == 0) {
79 return (imp);
80 }
81 }
82 return (NULL);
83 }
84
85 static isc_result_t
86 load_symbol(uv_lib_t *handle, const char *filename, const char *symbol_name,
87 void **symbolp) {
88 void *symbol;
89 int r;
90
91 REQUIRE(handle != NULL);
92 REQUIRE(symbolp != NULL && *symbolp == NULL);
93
94 r = uv_dlsym(handle, symbol_name, &symbol);
95 if (r != 0) {
96 const char *errmsg = uv_dlerror(handle);
97 if (errmsg == NULL) {
98 errmsg = "returned function pointer is NULL";
99 }
100 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
101 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
102 "failed to lookup symbol %s in "
103 "DynDB module '%s': %s",
104 symbol_name, filename, errmsg);
105 return (ISC_R_FAILURE);
106 }
107
108 *symbolp = symbol;
109
110 return (ISC_R_SUCCESS);
111 }
112
113 static void
114 unload_library(dyndb_implementation_t **impp);
115
116 static isc_result_t
117 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
118 dyndb_implementation_t **impp) {
119 isc_result_t result;
120 dyndb_implementation_t *imp = NULL;
121 dns_dyndb_version_t *version_func = NULL;
122 int version;
123 int r;
124
125 REQUIRE(impp != NULL && *impp == NULL);
126
127 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
128 ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
129 instname, filename);
130
131 imp = isc_mem_get(mctx, sizeof(*imp));
132 memset(imp, 0, sizeof(*imp));
133 isc_mem_attach(mctx, &imp->mctx);
134
135 imp->name = isc_mem_strdup(imp->mctx, instname);
136
137 INIT_LINK(imp, link);
138
139 r = uv_dlopen(filename, &imp->handle);
140 if (r != 0) {
141 const char *errmsg = uv_dlerror(&imp->handle);
142 if (errmsg == NULL) {
143 errmsg = "unknown error";
144 }
145 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
146 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
147 "failed to dlopen() DynDB instance '%s' driver "
148 "'%s': %s",
149 instname, filename, errmsg);
150 CHECK(ISC_R_FAILURE);
151 }
152
153 CHECK(load_symbol(&imp->handle, filename, "dyndb_version",
154 (void **)&version_func));
155
156 version = version_func(NULL);
157 if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
158 version > DNS_DYNDB_VERSION)
159 {
160 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
161 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
162 "driver API version mismatch: %d/%d", version,
163 DNS_DYNDB_VERSION);
164 CHECK(ISC_R_FAILURE);
165 }
166
167 CHECK(load_symbol(&imp->handle, filename, "dyndb_init",
168 (void **)&imp->register_func));
169 CHECK(load_symbol(&imp->handle, filename, "dyndb_destroy",
170 (void **)&imp->destroy_func));
171
172 *impp = imp;
173
174 return (ISC_R_SUCCESS);
175
176 cleanup:
177 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
178 ISC_LOG_ERROR,
179 "failed to dynamically load DynDB instance '%s' driver "
180 "'%s': %s",
181 instname, filename, isc_result_totext(result));
182
183 unload_library(&imp);
184
185 return (result);
186 }
187
188 static void
189 unload_library(dyndb_implementation_t **impp) {
190 dyndb_implementation_t *imp;
191
192 REQUIRE(impp != NULL && *impp != NULL);
193
194 imp = *impp;
195 *impp = NULL;
196
197 /*
198 * This is a resource leak, but there is nothing we can currently do
199 * about it due to how configuration loading/reloading is designed.
200 */
201 /* uv_dlclose(&imp->handle); */
202 isc_mem_free(imp->mctx, imp->name);
203 isc_mem_putanddetach(&imp->mctx, imp, sizeof(*imp));
204 }
205
206 isc_result_t
207 dns_dyndb_load(const char *libname, const char *name, const char *parameters,
208 const char *file, unsigned long line, isc_mem_t *mctx,
209 const dns_dyndbctx_t *dctx) {
210 isc_result_t result;
211 dyndb_implementation_t *implementation = NULL;
212
213 REQUIRE(DNS_DYNDBCTX_VALID(dctx));
214 REQUIRE(name != NULL);
215
216 RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
217
218 LOCK(&dyndb_lock);
219
220 /* duplicate instance names are not allowed */
221 if (impfind(name) != NULL) {
222 CHECK(ISC_R_EXISTS);
223 }
224
225 CHECK(load_library(mctx, libname, name, &implementation));
226 CHECK(implementation->register_func(mctx, name, parameters, file, line,
227 dctx, &implementation->inst));
228
229 APPEND(dyndb_implementations, implementation, link);
230 result = ISC_R_SUCCESS;
231
232 cleanup:
233 if (result != ISC_R_SUCCESS) {
234 if (implementation != NULL) {
235 unload_library(&implementation);
236 }
237 }
238
239 UNLOCK(&dyndb_lock);
240 return (result);
241 }
242
243 void
244 dns_dyndb_cleanup(bool exiting) {
245 dyndb_implementation_t *elem;
246 dyndb_implementation_t *prev;
247
248 RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
249
250 LOCK(&dyndb_lock);
251 elem = TAIL(dyndb_implementations);
252 while (elem != NULL) {
253 prev = PREV(elem, link);
254 UNLINK(dyndb_implementations, elem, link);
255 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
256 DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
257 "unloading DynDB instance '%s'", elem->name);
258 elem->destroy_func(&elem->inst);
259 ENSURE(elem->inst == NULL);
260 unload_library(&elem);
261 elem = prev;
262 }
263 UNLOCK(&dyndb_lock);
264
265 if (exiting) {
266 isc_mutex_destroy(&dyndb_lock);
267 }
268 }
269
270 isc_result_t
271 dns_dyndb_createctx(isc_mem_t *mctx, const void *hashinit, isc_log_t *lctx,
272 dns_view_t *view, dns_zonemgr_t *zmgr, isc_task_t *task,
273 isc_timermgr_t *tmgr, dns_dyndbctx_t **dctxp) {
274 dns_dyndbctx_t *dctx;
275
276 REQUIRE(dctxp != NULL && *dctxp == NULL);
277
278 dctx = isc_mem_get(mctx, sizeof(*dctx));
279 *dctx = (dns_dyndbctx_t){
280 .timermgr = tmgr,
281 .hashinit = hashinit,
282 .lctx = lctx,
283 };
284
285 if (view != NULL) {
286 dns_view_attach(view, &dctx->view);
287 }
288 if (zmgr != NULL) {
289 dns_zonemgr_attach(zmgr, &dctx->zmgr);
290 }
291 if (task != NULL) {
292 isc_task_attach(task, &dctx->task);
293 }
294
295 isc_mem_attach(mctx, &dctx->mctx);
296 dctx->magic = DNS_DYNDBCTX_MAGIC;
297
298 *dctxp = dctx;
299
300 return (ISC_R_SUCCESS);
301 }
302
303 void
304 dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) {
305 dns_dyndbctx_t *dctx;
306
307 REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp));
308
309 dctx = *dctxp;
310 *dctxp = NULL;
311
312 dctx->magic = 0;
313
314 if (dctx->view != NULL) {
315 dns_view_detach(&dctx->view);
316 }
317 if (dctx->zmgr != NULL) {
318 dns_zonemgr_detach(&dctx->zmgr);
319 }
320 if (dctx->task != NULL) {
321 isc_task_detach(&dctx->task);
322 }
323 dctx->timermgr = NULL;
324 dctx->lctx = NULL;
325
326 isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
327 }
328