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