dyndb.c revision 1.2
1/*	$NetBSD: dyndb.c,v 1.2 2018/08/12 13:02:35 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
47typedef struct dyndb_implementation dyndb_implementation_t;
48struct 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 */
64static LIST(dyndb_implementation_t) dyndb_implementations;
65
66/* Locks dyndb_implementations. */
67static isc_mutex_t dyndb_lock;
68static isc_once_t once = ISC_ONCE_INIT;
69
70static void
71dyndb_initialize(void) {
72	RUNTIME_CHECK(isc_mutex_init(&dyndb_lock) == ISC_R_SUCCESS);
73	INIT_LIST(dyndb_implementations);
74}
75
76static dyndb_implementation_t *
77impfind(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
89static isc_result_t
90load_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
118static isc_result_t
119load_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 **)&register_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
187cleanup:
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
202static void
203unload_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
216static isc_result_t
217load_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
241static isc_result_t
242load_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 **)&register_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
302cleanup:
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
317static void
318unload_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 */
331static isc_result_t
332load_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
347static void
348unload_library(dyndb_implementation_t **impp)
349{
350	UNUSED(impp);
351}
352#endif	/* HAVE_DLFCN_H */
353
354isc_result_t
355dns_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
380cleanup:
381	if (result != ISC_R_SUCCESS)
382		if (implementation != NULL)
383			unload_library(&implementation);
384
385	UNLOCK(&dyndb_lock);
386	return (result);
387}
388
389void
390dns_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
415isc_result_t
416dns_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
448void
449dns_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