dyndb.c revision 1.12 1 /* $NetBSD: dyndb.c,v 1.12 2025/01/26 16:25:22 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
18 #include <isc/buffer.h>
19 #include <isc/mem.h>
20 #include <isc/mutex.h>
21 #include <isc/once.h>
22 #include <isc/region.h>
23 #include <isc/result.h>
24 #include <isc/types.h>
25 #include <isc/util.h>
26 #include <isc/uv.h>
27
28 #include <dns/dyndb.h>
29 #include <dns/log.h>
30 #include <dns/types.h>
31 #include <dns/view.h>
32 #include <dns/zone.h>
33
34 #define CHECK(op) \
35 do { \
36 result = (op); \
37 if (result != ISC_R_SUCCESS) \
38 goto cleanup; \
39 } while (0)
40
41 typedef struct dyndb_implementation dyndb_implementation_t;
42 struct dyndb_implementation {
43 isc_mem_t *mctx;
44 uv_lib_t handle;
45 dns_dyndb_register_t *register_func;
46 dns_dyndb_destroy_t *destroy_func;
47 char *name;
48 void *inst;
49 LINK(dyndb_implementation_t) link;
50 };
51
52 /*
53 * List of dyndb implementations. Locked by dyndb_lock.
54 *
55 * These are stored here so they can be cleaned up on shutdown.
56 * (The order in which they are stored is not important.)
57 */
58 static LIST(dyndb_implementation_t) dyndb_implementations;
59
60 /* Locks dyndb_implementations. */
61 static isc_mutex_t dyndb_lock;
62 static isc_once_t once = ISC_ONCE_INIT;
63
64 static void
65 dyndb_initialize(void) {
66 isc_mutex_init(&dyndb_lock);
67 INIT_LIST(dyndb_implementations);
68 }
69
70 static dyndb_implementation_t *
71 impfind(const char *name) {
72 dyndb_implementation_t *imp;
73
74 for (imp = ISC_LIST_HEAD(dyndb_implementations); imp != NULL;
75 imp = ISC_LIST_NEXT(imp, link))
76 {
77 if (strcasecmp(name, imp->name) == 0) {
78 return imp;
79 }
80 }
81 return NULL;
82 }
83
84 static isc_result_t
85 load_symbol(uv_lib_t *handle, const char *filename, const char *symbol_name,
86 void **symbolp) {
87 void *symbol;
88 int r;
89
90 REQUIRE(handle != NULL);
91 REQUIRE(symbolp != NULL && *symbolp == NULL);
92
93 r = uv_dlsym(handle, symbol_name, &symbol);
94 if (r != 0) {
95 const char *errmsg = uv_dlerror(handle);
96 if (errmsg == NULL) {
97 errmsg = "returned function pointer is NULL";
98 }
99 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
100 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
101 "failed to lookup symbol %s in "
102 "DynDB module '%s': %s",
103 symbol_name, filename, errmsg);
104 return ISC_R_FAILURE;
105 }
106
107 *symbolp = symbol;
108
109 return ISC_R_SUCCESS;
110 }
111
112 static void
113 unload_library(dyndb_implementation_t **impp);
114
115 static isc_result_t
116 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
117 dyndb_implementation_t **impp) {
118 isc_result_t result;
119 dyndb_implementation_t *imp = NULL;
120 dns_dyndb_version_t *version_func = NULL;
121 int version;
122 int r;
123
124 REQUIRE(impp != NULL && *impp == NULL);
125
126 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
127 ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
128 instname, filename);
129
130 imp = isc_mem_get(mctx, sizeof(*imp));
131 *imp = (dyndb_implementation_t){
132 .name = isc_mem_strdup(mctx, instname),
133 };
134
135 isc_mem_attach(mctx, &imp->mctx);
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 isc_once_do(&once, dyndb_initialize);
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 isc_once_do(&once, dyndb_initialize);
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,
273 isc_loopmgr_t *loopmgr, 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 .loopmgr = loopmgr,
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
292 isc_mem_attach(mctx, &dctx->mctx);
293 dctx->magic = DNS_DYNDBCTX_MAGIC;
294
295 *dctxp = dctx;
296
297 return ISC_R_SUCCESS;
298 }
299
300 void
301 dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) {
302 dns_dyndbctx_t *dctx;
303
304 REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp));
305
306 dctx = *dctxp;
307 *dctxp = NULL;
308
309 dctx->magic = 0;
310
311 if (dctx->view != NULL) {
312 dns_view_detach(&dctx->view);
313 }
314 if (dctx->zmgr != NULL) {
315 dns_zonemgr_detach(&dctx->zmgr);
316 }
317 dctx->loopmgr = NULL;
318 dctx->lctx = NULL;
319
320 isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
321 }
322