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