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