dyndb.c revision 1.5 1 1.4 christos /* $NetBSD: dyndb.c,v 1.5 2020/05/24 19:46:22 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 christos * This Source Code Form is subject to the terms of the Mozilla Public
7 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this
8 1.1 christos * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 1.1 christos *
10 1.1 christos * See the COPYRIGHT file distributed with this work for additional
11 1.1 christos * information regarding copyright ownership.
12 1.1 christos */
13 1.1 christos
14 1.1 christos #if HAVE_DLFCN_H
15 1.1 christos #include <dlfcn.h>
16 1.1 christos #elif _WIN32
17 1.1 christos #include <windows.h>
18 1.5 christos #endif /* if HAVE_DLFCN_H */
19 1.5 christos
20 1.5 christos #include <string.h>
21 1.1 christos
22 1.1 christos #include <isc/buffer.h>
23 1.1 christos #include <isc/mem.h>
24 1.1 christos #include <isc/mutex.h>
25 1.1 christos #include <isc/once.h>
26 1.5 christos #include <isc/region.h>
27 1.1 christos #include <isc/result.h>
28 1.1 christos #include <isc/task.h>
29 1.1 christos #include <isc/types.h>
30 1.1 christos #include <isc/util.h>
31 1.1 christos
32 1.1 christos #include <dns/dyndb.h>
33 1.1 christos #include <dns/log.h>
34 1.1 christos #include <dns/types.h>
35 1.1 christos #include <dns/view.h>
36 1.1 christos #include <dns/zone.h>
37 1.1 christos
38 1.5 christos #define CHECK(op) \
39 1.5 christos do { \
40 1.5 christos result = (op); \
41 1.5 christos if (result != ISC_R_SUCCESS) \
42 1.5 christos goto cleanup; \
43 1.5 christos } while (/*CONSTCOND*/0)
44 1.1 christos
45 1.1 christos typedef struct dyndb_implementation dyndb_implementation_t;
46 1.1 christos struct dyndb_implementation {
47 1.5 christos isc_mem_t *mctx;
48 1.5 christos void *handle;
49 1.5 christos dns_dyndb_register_t *register_func;
50 1.5 christos dns_dyndb_destroy_t *destroy_func;
51 1.5 christos char *name;
52 1.5 christos void *inst;
53 1.5 christos LINK(dyndb_implementation_t) link;
54 1.1 christos };
55 1.1 christos
56 1.1 christos /*
57 1.1 christos * List of dyndb implementations. Locked by dyndb_lock.
58 1.1 christos *
59 1.1 christos * These are stored here so they can be cleaned up on shutdown.
60 1.1 christos * (The order in which they are stored is not important.)
61 1.1 christos */
62 1.1 christos static LIST(dyndb_implementation_t) dyndb_implementations;
63 1.1 christos
64 1.1 christos /* Locks dyndb_implementations. */
65 1.1 christos static isc_mutex_t dyndb_lock;
66 1.1 christos static isc_once_t once = ISC_ONCE_INIT;
67 1.1 christos
68 1.1 christos static void
69 1.1 christos dyndb_initialize(void) {
70 1.3 christos isc_mutex_init(&dyndb_lock);
71 1.1 christos INIT_LIST(dyndb_implementations);
72 1.1 christos }
73 1.1 christos
74 1.1 christos static dyndb_implementation_t *
75 1.1 christos impfind(const char *name) {
76 1.1 christos dyndb_implementation_t *imp;
77 1.1 christos
78 1.5 christos for (imp = ISC_LIST_HEAD(dyndb_implementations); imp != NULL;
79 1.1 christos imp = ISC_LIST_NEXT(imp, link))
80 1.5 christos {
81 1.5 christos if (strcasecmp(name, imp->name) == 0) {
82 1.1 christos return (imp);
83 1.5 christos }
84 1.5 christos }
85 1.1 christos return (NULL);
86 1.1 christos }
87 1.1 christos
88 1.1 christos #if HAVE_DLFCN_H && HAVE_DLOPEN
89 1.1 christos static isc_result_t
90 1.5 christos load_symbol(void *handle, const char *filename, const char *symbol_name,
91 1.5 christos void **symbolp) {
92 1.1 christos const char *errmsg;
93 1.1 christos void *symbol;
94 1.1 christos
95 1.1 christos REQUIRE(handle != NULL);
96 1.1 christos REQUIRE(symbolp != NULL && *symbolp == NULL);
97 1.1 christos
98 1.1 christos symbol = dlsym(handle, symbol_name);
99 1.1 christos if (symbol == NULL) {
100 1.1 christos errmsg = dlerror();
101 1.5 christos if (errmsg == NULL) {
102 1.1 christos errmsg = "returned function pointer is NULL";
103 1.5 christos }
104 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
105 1.1 christos DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
106 1.1 christos "failed to lookup symbol %s in "
107 1.1 christos "dyndb module '%s': %s",
108 1.1 christos symbol_name, filename, errmsg);
109 1.1 christos return (ISC_R_FAILURE);
110 1.1 christos }
111 1.1 christos dlerror();
112 1.1 christos
113 1.1 christos *symbolp = symbol;
114 1.1 christos
115 1.1 christos return (ISC_R_SUCCESS);
116 1.1 christos }
117 1.1 christos
118 1.1 christos static isc_result_t
119 1.1 christos load_library(isc_mem_t *mctx, const char *filename, const char *instname,
120 1.5 christos dyndb_implementation_t **impp) {
121 1.1 christos isc_result_t result;
122 1.1 christos void *handle = NULL;
123 1.1 christos dyndb_implementation_t *imp = NULL;
124 1.1 christos dns_dyndb_register_t *register_func = NULL;
125 1.1 christos dns_dyndb_destroy_t *destroy_func = NULL;
126 1.1 christos dns_dyndb_version_t *version_func = NULL;
127 1.1 christos int version, flags;
128 1.1 christos
129 1.1 christos REQUIRE(impp != NULL && *impp == NULL);
130 1.1 christos
131 1.5 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
132 1.5 christos ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
133 1.1 christos instname, filename);
134 1.1 christos
135 1.5 christos flags = RTLD_NOW | RTLD_LOCAL;
136 1.4 christos #if defined(RTLD_DEEPBIND) && !__SANITIZE_ADDRESS__
137 1.1 christos flags |= RTLD_DEEPBIND;
138 1.5 christos #endif /* if defined(RTLD_DEEPBIND) && !__SANITIZE_ADDRESS__ */
139 1.1 christos
140 1.1 christos handle = dlopen(filename, flags);
141 1.5 christos if (handle == NULL) {
142 1.1 christos CHECK(ISC_R_FAILURE);
143 1.5 christos }
144 1.1 christos
145 1.1 christos /* Clear dlerror */
146 1.1 christos dlerror();
147 1.1 christos
148 1.1 christos CHECK(load_symbol(handle, filename, "dyndb_version",
149 1.1 christos (void **)&version_func));
150 1.1 christos
151 1.1 christos version = version_func(NULL);
152 1.1 christos if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
153 1.1 christos version > DNS_DYNDB_VERSION)
154 1.1 christos {
155 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
156 1.1 christos DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
157 1.5 christos "driver API version mismatch: %d/%d", version,
158 1.5 christos DNS_DYNDB_VERSION);
159 1.1 christos CHECK(ISC_R_FAILURE);
160 1.1 christos }
161 1.1 christos
162 1.1 christos CHECK(load_symbol(handle, filename, "dyndb_init",
163 1.1 christos (void **)®ister_func));
164 1.1 christos CHECK(load_symbol(handle, filename, "dyndb_destroy",
165 1.1 christos (void **)&destroy_func));
166 1.1 christos
167 1.1 christos imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
168 1.1 christos
169 1.1 christos imp->mctx = NULL;
170 1.1 christos isc_mem_attach(mctx, &imp->mctx);
171 1.1 christos imp->handle = handle;
172 1.1 christos imp->register_func = register_func;
173 1.1 christos imp->destroy_func = destroy_func;
174 1.1 christos imp->name = isc_mem_strdup(mctx, instname);
175 1.1 christos
176 1.1 christos imp->inst = NULL;
177 1.1 christos INIT_LINK(imp, link);
178 1.1 christos
179 1.1 christos *impp = imp;
180 1.1 christos imp = NULL;
181 1.1 christos
182 1.1 christos cleanup:
183 1.5 christos if (result != ISC_R_SUCCESS) {
184 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
185 1.1 christos DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
186 1.1 christos "failed to dynamically load instance '%s' "
187 1.5 christos "driver '%s': %s (%s)",
188 1.5 christos instname, filename, dlerror(),
189 1.5 christos isc_result_totext(result));
190 1.5 christos }
191 1.5 christos if (imp != NULL) {
192 1.3 christos isc_mem_putanddetach(&imp->mctx, imp,
193 1.3 christos sizeof(dyndb_implementation_t));
194 1.5 christos }
195 1.5 christos if (result != ISC_R_SUCCESS && handle != NULL) {
196 1.1 christos dlclose(handle);
197 1.5 christos }
198 1.1 christos
199 1.1 christos return (result);
200 1.1 christos }
201 1.1 christos
202 1.1 christos static void
203 1.1 christos unload_library(dyndb_implementation_t **impp) {
204 1.1 christos dyndb_implementation_t *imp;
205 1.1 christos
206 1.1 christos REQUIRE(impp != NULL && *impp != NULL);
207 1.1 christos
208 1.1 christos imp = *impp;
209 1.5 christos *impp = NULL;
210 1.1 christos
211 1.1 christos isc_mem_free(imp->mctx, imp->name);
212 1.1 christos isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
213 1.1 christos }
214 1.1 christos #elif _WIN32
215 1.1 christos static isc_result_t
216 1.5 christos load_symbol(HMODULE handle, const char *filename, const char *symbol_name,
217 1.5 christos void **symbolp) {
218 1.1 christos void *symbol;
219 1.1 christos
220 1.1 christos REQUIRE(handle != NULL);
221 1.1 christos REQUIRE(symbolp != NULL && *symbolp == NULL);
222 1.1 christos
223 1.1 christos symbol = GetProcAddress(handle, symbol_name);
224 1.1 christos if (symbol == NULL) {
225 1.1 christos int errstatus = GetLastError();
226 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
227 1.1 christos DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
228 1.1 christos "failed to lookup symbol %s in "
229 1.1 christos "dyndb module '%s': %d",
230 1.1 christos symbol_name, filename, errstatus);
231 1.1 christos return (ISC_R_FAILURE);
232 1.1 christos }
233 1.1 christos
234 1.1 christos *symbolp = symbol;
235 1.1 christos
236 1.1 christos return (ISC_R_SUCCESS);
237 1.1 christos }
238 1.1 christos
239 1.1 christos static isc_result_t
240 1.1 christos load_library(isc_mem_t *mctx, const char *filename, const char *instname,
241 1.5 christos dyndb_implementation_t **impp) {
242 1.1 christos isc_result_t result;
243 1.1 christos HMODULE handle;
244 1.1 christos dyndb_implementation_t *imp = NULL;
245 1.1 christos dns_dyndb_register_t *register_func = NULL;
246 1.1 christos dns_dyndb_destroy_t *destroy_func = NULL;
247 1.1 christos dns_dyndb_version_t *version_func = NULL;
248 1.1 christos int version;
249 1.1 christos
250 1.1 christos REQUIRE(impp != NULL && *impp == NULL);
251 1.1 christos
252 1.5 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
253 1.5 christos ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
254 1.1 christos instname, filename);
255 1.1 christos
256 1.1 christos handle = LoadLibraryA(filename);
257 1.5 christos if (handle == NULL) {
258 1.1 christos CHECK(ISC_R_FAILURE);
259 1.5 christos }
260 1.1 christos
261 1.1 christos CHECK(load_symbol(handle, filename, "dyndb_version",
262 1.1 christos (void **)&version_func));
263 1.1 christos
264 1.1 christos version = version_func(NULL);
265 1.1 christos if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
266 1.1 christos version > DNS_DYNDB_VERSION)
267 1.1 christos {
268 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
269 1.1 christos DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
270 1.5 christos "driver API version mismatch: %d/%d", version,
271 1.5 christos DNS_DYNDB_VERSION);
272 1.1 christos CHECK(ISC_R_FAILURE);
273 1.1 christos }
274 1.1 christos
275 1.1 christos CHECK(load_symbol(handle, filename, "dyndb_init",
276 1.1 christos (void **)®ister_func));
277 1.1 christos CHECK(load_symbol(handle, filename, "dyndb_destroy",
278 1.1 christos (void **)&destroy_func));
279 1.1 christos
280 1.1 christos imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
281 1.1 christos
282 1.1 christos imp->mctx = NULL;
283 1.1 christos isc_mem_attach(mctx, &imp->mctx);
284 1.1 christos imp->handle = handle;
285 1.1 christos imp->register_func = register_func;
286 1.1 christos imp->destroy_func = destroy_func;
287 1.1 christos imp->name = isc_mem_strdup(mctx, instname);
288 1.1 christos
289 1.1 christos imp->inst = NULL;
290 1.1 christos INIT_LINK(imp, link);
291 1.1 christos
292 1.1 christos *impp = imp;
293 1.1 christos imp = NULL;
294 1.1 christos
295 1.1 christos cleanup:
296 1.5 christos if (result != ISC_R_SUCCESS) {
297 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
298 1.1 christos DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
299 1.1 christos "failed to dynamically load instance '%s' "
300 1.5 christos "driver '%s': %d (%s)",
301 1.5 christos instname, filename, GetLastError(),
302 1.5 christos isc_result_totext(result));
303 1.5 christos }
304 1.5 christos if (imp != NULL) {
305 1.3 christos isc_mem_putanddetach(&imp->mctx, imp,
306 1.3 christos sizeof(dyndb_implementation_t));
307 1.5 christos }
308 1.5 christos if (result != ISC_R_SUCCESS && handle != NULL) {
309 1.1 christos FreeLibrary(handle);
310 1.5 christos }
311 1.1 christos
312 1.1 christos return (result);
313 1.1 christos }
314 1.1 christos
315 1.1 christos static void
316 1.1 christos unload_library(dyndb_implementation_t **impp) {
317 1.1 christos dyndb_implementation_t *imp;
318 1.1 christos
319 1.1 christos REQUIRE(impp != NULL && *impp != NULL);
320 1.1 christos
321 1.1 christos imp = *impp;
322 1.5 christos *impp = NULL;
323 1.1 christos
324 1.1 christos isc_mem_free(imp->mctx, imp->name);
325 1.1 christos isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
326 1.1 christos }
327 1.5 christos #else /* HAVE_DLFCN_H || _WIN32 */
328 1.1 christos static isc_result_t
329 1.1 christos load_library(isc_mem_t *mctx, const char *filename, const char *instname,
330 1.5 christos dyndb_implementation_t **impp) {
331 1.1 christos UNUSED(mctx);
332 1.1 christos UNUSED(filename);
333 1.1 christos UNUSED(instname);
334 1.1 christos UNUSED(impp);
335 1.1 christos
336 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
337 1.1 christos ISC_LOG_ERROR,
338 1.1 christos "dynamic database support is not implemented");
339 1.1 christos
340 1.1 christos return (ISC_R_NOTIMPLEMENTED);
341 1.1 christos }
342 1.1 christos
343 1.1 christos static void
344 1.5 christos unload_library(dyndb_implementation_t **impp) {
345 1.1 christos UNUSED(impp);
346 1.1 christos }
347 1.5 christos #endif /* HAVE_DLFCN_H */
348 1.1 christos
349 1.1 christos isc_result_t
350 1.1 christos dns_dyndb_load(const char *libname, const char *name, const char *parameters,
351 1.1 christos const char *file, unsigned long line, isc_mem_t *mctx,
352 1.5 christos const dns_dyndbctx_t *dctx) {
353 1.1 christos isc_result_t result;
354 1.1 christos dyndb_implementation_t *implementation = NULL;
355 1.1 christos
356 1.1 christos REQUIRE(DNS_DYNDBCTX_VALID(dctx));
357 1.1 christos REQUIRE(name != NULL);
358 1.1 christos
359 1.1 christos RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
360 1.1 christos
361 1.1 christos LOCK(&dyndb_lock);
362 1.1 christos
363 1.1 christos /* duplicate instance names are not allowed */
364 1.5 christos if (impfind(name) != NULL) {
365 1.1 christos CHECK(ISC_R_EXISTS);
366 1.5 christos }
367 1.1 christos
368 1.1 christos CHECK(load_library(mctx, libname, name, &implementation));
369 1.1 christos CHECK(implementation->register_func(mctx, name, parameters, file, line,
370 1.1 christos dctx, &implementation->inst));
371 1.1 christos
372 1.1 christos APPEND(dyndb_implementations, implementation, link);
373 1.1 christos result = ISC_R_SUCCESS;
374 1.1 christos
375 1.1 christos cleanup:
376 1.5 christos if (result != ISC_R_SUCCESS) {
377 1.5 christos if (implementation != NULL) {
378 1.1 christos unload_library(&implementation);
379 1.5 christos }
380 1.5 christos }
381 1.1 christos
382 1.1 christos UNLOCK(&dyndb_lock);
383 1.1 christos return (result);
384 1.1 christos }
385 1.1 christos
386 1.1 christos void
387 1.3 christos dns_dyndb_cleanup(bool exiting) {
388 1.1 christos dyndb_implementation_t *elem;
389 1.1 christos dyndb_implementation_t *prev;
390 1.1 christos
391 1.1 christos RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
392 1.1 christos
393 1.1 christos LOCK(&dyndb_lock);
394 1.1 christos elem = TAIL(dyndb_implementations);
395 1.1 christos while (elem != NULL) {
396 1.1 christos prev = PREV(elem, link);
397 1.1 christos UNLINK(dyndb_implementations, elem, link);
398 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
399 1.1 christos DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
400 1.1 christos "unloading DynDB instance '%s'", elem->name);
401 1.1 christos elem->destroy_func(&elem->inst);
402 1.1 christos ENSURE(elem->inst == NULL);
403 1.1 christos unload_library(&elem);
404 1.1 christos elem = prev;
405 1.1 christos }
406 1.1 christos UNLOCK(&dyndb_lock);
407 1.1 christos
408 1.5 christos if (exiting == true) {
409 1.1 christos isc_mutex_destroy(&dyndb_lock);
410 1.5 christos }
411 1.1 christos }
412 1.1 christos
413 1.1 christos isc_result_t
414 1.1 christos dns_dyndb_createctx(isc_mem_t *mctx, const void *hashinit, isc_log_t *lctx,
415 1.1 christos dns_view_t *view, dns_zonemgr_t *zmgr, isc_task_t *task,
416 1.5 christos isc_timermgr_t *tmgr, dns_dyndbctx_t **dctxp) {
417 1.1 christos dns_dyndbctx_t *dctx;
418 1.1 christos
419 1.1 christos REQUIRE(dctxp != NULL && *dctxp == NULL);
420 1.1 christos
421 1.1 christos dctx = isc_mem_get(mctx, sizeof(*dctx));
422 1.1 christos
423 1.1 christos memset(dctx, 0, sizeof(*dctx));
424 1.5 christos if (view != NULL) {
425 1.1 christos dns_view_attach(view, &dctx->view);
426 1.5 christos }
427 1.5 christos if (zmgr != NULL) {
428 1.1 christos dns_zonemgr_attach(zmgr, &dctx->zmgr);
429 1.5 christos }
430 1.5 christos if (task != NULL) {
431 1.1 christos isc_task_attach(task, &dctx->task);
432 1.5 christos }
433 1.1 christos dctx->timermgr = tmgr;
434 1.1 christos dctx->hashinit = hashinit;
435 1.1 christos dctx->lctx = lctx;
436 1.1 christos dctx->refvar = &isc_bind9;
437 1.1 christos
438 1.1 christos isc_mem_attach(mctx, &dctx->mctx);
439 1.1 christos dctx->magic = DNS_DYNDBCTX_MAGIC;
440 1.1 christos
441 1.1 christos *dctxp = dctx;
442 1.1 christos
443 1.1 christos return (ISC_R_SUCCESS);
444 1.1 christos }
445 1.1 christos
446 1.1 christos void
447 1.1 christos dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) {
448 1.1 christos dns_dyndbctx_t *dctx;
449 1.1 christos
450 1.1 christos REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp));
451 1.1 christos
452 1.1 christos dctx = *dctxp;
453 1.1 christos *dctxp = NULL;
454 1.1 christos
455 1.1 christos dctx->magic = 0;
456 1.1 christos
457 1.5 christos if (dctx->view != NULL) {
458 1.1 christos dns_view_detach(&dctx->view);
459 1.5 christos }
460 1.5 christos if (dctx->zmgr != NULL) {
461 1.1 christos dns_zonemgr_detach(&dctx->zmgr);
462 1.5 christos }
463 1.5 christos if (dctx->task != NULL) {
464 1.1 christos isc_task_detach(&dctx->task);
465 1.5 christos }
466 1.1 christos dctx->timermgr = NULL;
467 1.1 christos dctx->lctx = NULL;
468 1.1 christos
469 1.1 christos isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
470 1.1 christos }
471