1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * SPDX-License-Identifier: MPL-2.0
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 https://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
dyndb_initialize(void)69 dyndb_initialize(void) {
70 isc_mutex_init(&dyndb_lock);
71 INIT_LIST(dyndb_implementations);
72 }
73
74 static dyndb_implementation_t *
impfind(const char * name)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
load_symbol(void * handle,const char * filename,const char * symbol_name,void ** symbolp)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
load_library(isc_mem_t * mctx,const char * filename,const char * instname,dyndb_implementation_t ** impp)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;
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 handle = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
136 if (handle == NULL) {
137 CHECK(ISC_R_FAILURE);
138 }
139
140 /* Clear dlerror */
141 dlerror();
142
143 CHECK(load_symbol(handle, filename, "dyndb_version",
144 (void **)&version_func));
145
146 version = version_func(NULL);
147 if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
148 version > DNS_DYNDB_VERSION)
149 {
150 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
151 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
152 "driver API version mismatch: %d/%d", version,
153 DNS_DYNDB_VERSION);
154 CHECK(ISC_R_FAILURE);
155 }
156
157 CHECK(load_symbol(handle, filename, "dyndb_init",
158 (void **)®ister_func));
159 CHECK(load_symbol(handle, filename, "dyndb_destroy",
160 (void **)&destroy_func));
161
162 imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
163
164 imp->mctx = NULL;
165 isc_mem_attach(mctx, &imp->mctx);
166 imp->handle = handle;
167 imp->register_func = register_func;
168 imp->destroy_func = destroy_func;
169 imp->name = isc_mem_strdup(mctx, instname);
170
171 imp->inst = NULL;
172 INIT_LINK(imp, link);
173
174 *impp = imp;
175 imp = NULL;
176
177 cleanup:
178 if (result != ISC_R_SUCCESS) {
179 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
180 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
181 "failed to dynamically load instance '%s' "
182 "driver '%s': %s (%s)",
183 instname, filename, dlerror(),
184 isc_result_totext(result));
185 }
186 if (imp != NULL) {
187 isc_mem_putanddetach(&imp->mctx, imp,
188 sizeof(dyndb_implementation_t));
189 }
190 if (result != ISC_R_SUCCESS && handle != NULL) {
191 dlclose(handle);
192 }
193
194 return (result);
195 }
196
197 static void
unload_library(dyndb_implementation_t ** impp)198 unload_library(dyndb_implementation_t **impp) {
199 dyndb_implementation_t *imp;
200
201 REQUIRE(impp != NULL && *impp != NULL);
202
203 imp = *impp;
204 *impp = NULL;
205
206 isc_mem_free(imp->mctx, imp->name);
207 isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
208 }
209 #elif _WIN32
210 static isc_result_t
load_symbol(HMODULE handle,const char * filename,const char * symbol_name,void ** symbolp)211 load_symbol(HMODULE handle, const char *filename, const char *symbol_name,
212 void **symbolp) {
213 void *symbol;
214
215 REQUIRE(handle != NULL);
216 REQUIRE(symbolp != NULL && *symbolp == NULL);
217
218 symbol = GetProcAddress(handle, symbol_name);
219 if (symbol == NULL) {
220 int errstatus = GetLastError();
221 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
222 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
223 "failed to lookup symbol %s in "
224 "dyndb module '%s': %d",
225 symbol_name, filename, errstatus);
226 return (ISC_R_FAILURE);
227 }
228
229 *symbolp = symbol;
230
231 return (ISC_R_SUCCESS);
232 }
233
234 static isc_result_t
load_library(isc_mem_t * mctx,const char * filename,const char * instname,dyndb_implementation_t ** impp)235 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
236 dyndb_implementation_t **impp) {
237 isc_result_t result;
238 HMODULE handle;
239 dyndb_implementation_t *imp = NULL;
240 dns_dyndb_register_t *register_func = NULL;
241 dns_dyndb_destroy_t *destroy_func = NULL;
242 dns_dyndb_version_t *version_func = NULL;
243 int version;
244
245 REQUIRE(impp != NULL && *impp == NULL);
246
247 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
248 ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
249 instname, filename);
250
251 handle = LoadLibraryA(filename);
252 if (handle == NULL) {
253 CHECK(ISC_R_FAILURE);
254 }
255
256 CHECK(load_symbol(handle, filename, "dyndb_version",
257 (void **)&version_func));
258
259 version = version_func(NULL);
260 if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
261 version > DNS_DYNDB_VERSION)
262 {
263 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
264 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
265 "driver API version mismatch: %d/%d", version,
266 DNS_DYNDB_VERSION);
267 CHECK(ISC_R_FAILURE);
268 }
269
270 CHECK(load_symbol(handle, filename, "dyndb_init",
271 (void **)®ister_func));
272 CHECK(load_symbol(handle, filename, "dyndb_destroy",
273 (void **)&destroy_func));
274
275 imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
276
277 imp->mctx = NULL;
278 isc_mem_attach(mctx, &imp->mctx);
279 imp->handle = handle;
280 imp->register_func = register_func;
281 imp->destroy_func = destroy_func;
282 imp->name = isc_mem_strdup(mctx, instname);
283
284 imp->inst = NULL;
285 INIT_LINK(imp, link);
286
287 *impp = imp;
288 imp = NULL;
289
290 cleanup:
291 if (result != ISC_R_SUCCESS) {
292 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
293 DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
294 "failed to dynamically load instance '%s' "
295 "driver '%s': %d (%s)",
296 instname, filename, GetLastError(),
297 isc_result_totext(result));
298 }
299 if (imp != NULL) {
300 isc_mem_putanddetach(&imp->mctx, imp,
301 sizeof(dyndb_implementation_t));
302 }
303 if (result != ISC_R_SUCCESS && handle != NULL) {
304 FreeLibrary(handle);
305 }
306
307 return (result);
308 }
309
310 static void
unload_library(dyndb_implementation_t ** impp)311 unload_library(dyndb_implementation_t **impp) {
312 dyndb_implementation_t *imp;
313
314 REQUIRE(impp != NULL && *impp != NULL);
315
316 imp = *impp;
317 *impp = NULL;
318
319 isc_mem_free(imp->mctx, imp->name);
320 isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
321 }
322 #else /* HAVE_DLFCN_H || _WIN32 */
323 static isc_result_t
load_library(isc_mem_t * mctx,const char * filename,const char * instname,dyndb_implementation_t ** impp)324 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
325 dyndb_implementation_t **impp) {
326 UNUSED(mctx);
327 UNUSED(filename);
328 UNUSED(instname);
329 UNUSED(impp);
330
331 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
332 ISC_LOG_ERROR,
333 "dynamic database support is not implemented");
334
335 return (ISC_R_NOTIMPLEMENTED);
336 }
337
338 static void
unload_library(dyndb_implementation_t ** impp)339 unload_library(dyndb_implementation_t **impp) {
340 UNUSED(impp);
341 }
342 #endif /* HAVE_DLFCN_H */
343
344 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)345 dns_dyndb_load(const char *libname, const char *name, const char *parameters,
346 const char *file, unsigned long line, isc_mem_t *mctx,
347 const dns_dyndbctx_t *dctx) {
348 isc_result_t result;
349 dyndb_implementation_t *implementation = NULL;
350
351 REQUIRE(DNS_DYNDBCTX_VALID(dctx));
352 REQUIRE(name != NULL);
353
354 RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
355
356 LOCK(&dyndb_lock);
357
358 /* duplicate instance names are not allowed */
359 if (impfind(name) != NULL) {
360 CHECK(ISC_R_EXISTS);
361 }
362
363 CHECK(load_library(mctx, libname, name, &implementation));
364 CHECK(implementation->register_func(mctx, name, parameters, file, line,
365 dctx, &implementation->inst));
366
367 APPEND(dyndb_implementations, implementation, link);
368 result = ISC_R_SUCCESS;
369
370 cleanup:
371 if (result != ISC_R_SUCCESS) {
372 if (implementation != NULL) {
373 unload_library(&implementation);
374 }
375 }
376
377 UNLOCK(&dyndb_lock);
378 return (result);
379 }
380
381 void
dns_dyndb_cleanup(bool exiting)382 dns_dyndb_cleanup(bool exiting) {
383 dyndb_implementation_t *elem;
384 dyndb_implementation_t *prev;
385
386 RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
387
388 LOCK(&dyndb_lock);
389 elem = TAIL(dyndb_implementations);
390 while (elem != NULL) {
391 prev = PREV(elem, link);
392 UNLINK(dyndb_implementations, elem, link);
393 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
394 DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
395 "unloading DynDB instance '%s'", elem->name);
396 elem->destroy_func(&elem->inst);
397 ENSURE(elem->inst == NULL);
398 unload_library(&elem);
399 elem = prev;
400 }
401 UNLOCK(&dyndb_lock);
402
403 if (exiting) {
404 isc_mutex_destroy(&dyndb_lock);
405 }
406 }
407
408 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)409 dns_dyndb_createctx(isc_mem_t *mctx, const void *hashinit, isc_log_t *lctx,
410 dns_view_t *view, dns_zonemgr_t *zmgr, isc_task_t *task,
411 isc_timermgr_t *tmgr, dns_dyndbctx_t **dctxp) {
412 dns_dyndbctx_t *dctx;
413
414 REQUIRE(dctxp != NULL && *dctxp == NULL);
415
416 dctx = isc_mem_get(mctx, sizeof(*dctx));
417
418 memset(dctx, 0, sizeof(*dctx));
419 if (view != NULL) {
420 dns_view_attach(view, &dctx->view);
421 }
422 if (zmgr != NULL) {
423 dns_zonemgr_attach(zmgr, &dctx->zmgr);
424 }
425 if (task != NULL) {
426 isc_task_attach(task, &dctx->task);
427 }
428 dctx->timermgr = tmgr;
429 dctx->hashinit = hashinit;
430 dctx->lctx = lctx;
431 dctx->memdebug = &isc_mem_debugging;
432
433 isc_mem_attach(mctx, &dctx->mctx);
434 dctx->magic = DNS_DYNDBCTX_MAGIC;
435
436 *dctxp = dctx;
437
438 return (ISC_R_SUCCESS);
439 }
440
441 void
dns_dyndb_destroyctx(dns_dyndbctx_t ** dctxp)442 dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) {
443 dns_dyndbctx_t *dctx;
444
445 REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp));
446
447 dctx = *dctxp;
448 *dctxp = NULL;
449
450 dctx->magic = 0;
451
452 if (dctx->view != NULL) {
453 dns_view_detach(&dctx->view);
454 }
455 if (dctx->zmgr != NULL) {
456 dns_zonemgr_detach(&dctx->zmgr);
457 }
458 if (dctx->task != NULL) {
459 isc_task_detach(&dctx->task);
460 }
461 dctx->timermgr = NULL;
462 dctx->lctx = NULL;
463
464 isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
465 }
466