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