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