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 https://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 
13 #include <config.h>
14 
15 #if HAVE_DLFCN_H
16 #include <dlfcn.h>
17 #elif _WIN32
18 #include <windows.h>
19 #endif
20 
21 #include <isc/buffer.h>
22 #include <isc/mem.h>
23 #include <isc/mutex.h>
24 #include <isc/once.h>
25 #include <isc/result.h>
26 #include <isc/region.h>
27 #include <isc/task.h>
28 #include <isc/types.h>
29 #include <isc/util.h>
30 
31 #include <dns/dyndb.h>
32 #include <dns/log.h>
33 #include <dns/types.h>
34 #include <dns/view.h>
35 #include <dns/zone.h>
36 
37 #include <string.h>
38 
39 #define CHECK(op)						\
40 	do { result = (op);					\
41 		if (result != ISC_R_SUCCESS) goto cleanup;	\
42 	} while (0)
43 
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 	RUNTIME_CHECK(isc_mutex_init(&dyndb_lock) == ISC_R_SUCCESS);
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);
79 	     imp != NULL;
80 	     imp = ISC_LIST_NEXT(imp, link))
81 		if (strcasecmp(name, imp->name) == 0)
82 			return (imp);
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,
89 	    const char *symbol_name, void **symbolp)
90 {
91 	const char *errmsg;
92 	void *symbol;
93 
94 	REQUIRE(handle != NULL);
95 	REQUIRE(symbolp != NULL && *symbolp == NULL);
96 
97 	symbol = dlsym(handle, symbol_name);
98 	if (symbol == NULL) {
99 		errmsg = dlerror();
100 		if (errmsg == NULL)
101 			errmsg = "returned function pointer is NULL";
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 {
120 	isc_result_t result;
121 	void *handle = NULL;
122 	dyndb_implementation_t *imp = NULL;
123 	dns_dyndb_register_t *register_func = NULL;
124 	dns_dyndb_destroy_t *destroy_func = NULL;
125 	dns_dyndb_version_t *version_func = NULL;
126 	int version, flags;
127 
128 	REQUIRE(impp != NULL && *impp == NULL);
129 
130 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
131 		      DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
132 		      "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
139 
140 	handle = dlopen(filename, flags);
141 	if (handle == NULL)
142 		CHECK(ISC_R_FAILURE);
143 
144 	/* Clear dlerror */
145 	dlerror();
146 
147 	CHECK(load_symbol(handle, filename, "dyndb_version",
148 			  (void **)&version_func));
149 
150 	version = version_func(NULL);
151 	if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
152 	    version > DNS_DYNDB_VERSION)
153 	{
154 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
155 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
156 			      "driver API version mismatch: %d/%d",
157 			      version, DNS_DYNDB_VERSION);
158 		CHECK(ISC_R_FAILURE);
159 	}
160 
161 	CHECK(load_symbol(handle, filename, "dyndb_init",
162 			  (void **)&register_func));
163 	CHECK(load_symbol(handle, filename, "dyndb_destroy",
164 			  (void **)&destroy_func));
165 
166 	imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
167 	if (imp == NULL)
168 		CHECK(ISC_R_NOMEMORY);
169 
170 	imp->mctx = NULL;
171 	isc_mem_attach(mctx, &imp->mctx);
172 	imp->handle = handle;
173 	imp->register_func = register_func;
174 	imp->destroy_func = destroy_func;
175 	imp->name = isc_mem_strdup(mctx, instname);
176 	if (imp->name == NULL)
177 		CHECK(ISC_R_NOMEMORY);
178 
179 	imp->inst = NULL;
180 	INIT_LINK(imp, link);
181 
182 	*impp = imp;
183 	imp = NULL;
184 
185 cleanup:
186 	if (result != ISC_R_SUCCESS)
187 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
188 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
189 			      "failed to dynamically load instance '%s' "
190 			      "driver '%s': %s (%s)", instname, filename,
191 			      dlerror(), isc_result_totext(result));
192 	if (imp != NULL)
193 		isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
194 	if (result != ISC_R_SUCCESS && handle != NULL)
195 		dlclose(handle);
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 
208 	isc_mem_free(imp->mctx, imp->name);
209 	isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
210 
211 	*impp = NULL;
212 }
213 #elif _WIN32
214 static isc_result_t
load_symbol(HMODULE handle,const char * filename,const char * symbol_name,void ** symbolp)215 load_symbol(HMODULE handle, const char *filename,
216 	    const char *symbol_name, void **symbolp)
217 {
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
load_library(isc_mem_t * mctx,const char * filename,const char * instname,dyndb_implementation_t ** impp)240 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
241 	     dyndb_implementation_t **impp)
242 {
243 	isc_result_t result;
244 	HMODULE handle;
245 	dyndb_implementation_t *imp = NULL;
246 	dns_dyndb_register_t *register_func = NULL;
247 	dns_dyndb_destroy_t *destroy_func = NULL;
248 	dns_dyndb_version_t *version_func = NULL;
249 	int version;
250 
251 	REQUIRE(impp != NULL && *impp == NULL);
252 
253 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
254 		      DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
255 		      "loading DynDB instance '%s' driver '%s'",
256 		      instname, filename);
257 
258 	handle = LoadLibraryA(filename);
259 	if (handle == NULL)
260 		CHECK(ISC_R_FAILURE);
261 
262 	CHECK(load_symbol(handle, filename, "dyndb_version",
263 			  (void **)&version_func));
264 
265 	version = version_func(NULL);
266 	if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
267 	    version > DNS_DYNDB_VERSION)
268 	{
269 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
270 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
271 			      "driver API version mismatch: %d/%d",
272 			      version, DNS_DYNDB_VERSION);
273 		CHECK(ISC_R_FAILURE);
274 	}
275 
276 	CHECK(load_symbol(handle, filename, "dyndb_init",
277 			  (void **)&register_func));
278 	CHECK(load_symbol(handle, filename, "dyndb_destroy",
279 			  (void **)&destroy_func));
280 
281 	imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
282 	if (imp == NULL)
283 		CHECK(ISC_R_NOMEMORY);
284 
285 	imp->mctx = NULL;
286 	isc_mem_attach(mctx, &imp->mctx);
287 	imp->handle = handle;
288 	imp->register_func = register_func;
289 	imp->destroy_func = destroy_func;
290 	imp->name = isc_mem_strdup(mctx, instname);
291 	if (imp->name == NULL)
292 		CHECK(ISC_R_NOMEMORY);
293 
294 	imp->inst = NULL;
295 	INIT_LINK(imp, link);
296 
297 	*impp = imp;
298 	imp = NULL;
299 
300 cleanup:
301 	if (result != ISC_R_SUCCESS)
302 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
303 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
304 			      "failed to dynamically load instance '%s' "
305 			      "driver '%s': %d (%s)", instname, filename,
306 			      GetLastError(), isc_result_totext(result));
307 	if (imp != NULL)
308 		isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
309 	if (result != ISC_R_SUCCESS && handle != NULL)
310 		FreeLibrary(handle);
311 
312 	return (result);
313 }
314 
315 static void
unload_library(dyndb_implementation_t ** impp)316 unload_library(dyndb_implementation_t **impp) {
317 	dyndb_implementation_t *imp;
318 
319 	REQUIRE(impp != NULL && *impp != NULL);
320 
321 	imp = *impp;
322 
323 	isc_mem_free(imp->mctx, imp->name);
324 	isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
325 
326 	*impp = NULL;
327 }
328 #else	/* HAVE_DLFCN_H || _WIN32 */
329 static isc_result_t
load_library(isc_mem_t * mctx,const char * filename,const char * instname,dyndb_implementation_t ** impp)330 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
331 	     dyndb_implementation_t **impp)
332 {
333 	UNUSED(mctx);
334 	UNUSED(filename);
335 	UNUSED(instname);
336 	UNUSED(impp);
337 
338 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
339 		      ISC_LOG_ERROR,
340 		      "dynamic database support is not implemented");
341 
342 	return (ISC_R_NOTIMPLEMENTED);
343 }
344 
345 static void
unload_library(dyndb_implementation_t ** impp)346 unload_library(dyndb_implementation_t **impp)
347 {
348 	UNUSED(impp);
349 }
350 #endif	/* HAVE_DLFCN_H */
351 
352 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)353 dns_dyndb_load(const char *libname, const char *name, const char *parameters,
354 	       const char *file, unsigned long line, isc_mem_t *mctx,
355 	       const dns_dyndbctx_t *dctx)
356 {
357 	isc_result_t result;
358 	dyndb_implementation_t *implementation = NULL;
359 
360 	REQUIRE(DNS_DYNDBCTX_VALID(dctx));
361 	REQUIRE(name != NULL);
362 
363 	RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
364 
365 	LOCK(&dyndb_lock);
366 
367 	/* duplicate instance names are not allowed */
368 	if (impfind(name) != NULL)
369 		CHECK(ISC_R_EXISTS);
370 
371 	CHECK(load_library(mctx, libname, name, &implementation));
372 	CHECK(implementation->register_func(mctx, name, parameters, file, line,
373 					    dctx, &implementation->inst));
374 
375 	APPEND(dyndb_implementations, implementation, link);
376 	result = ISC_R_SUCCESS;
377 
378 cleanup:
379 	if (result != ISC_R_SUCCESS)
380 		if (implementation != NULL)
381 			unload_library(&implementation);
382 
383 	UNLOCK(&dyndb_lock);
384 	return (result);
385 }
386 
387 void
dns_dyndb_cleanup(bool exiting)388 dns_dyndb_cleanup(bool exiting) {
389 	dyndb_implementation_t *elem;
390 	dyndb_implementation_t *prev;
391 
392 	RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
393 
394 	LOCK(&dyndb_lock);
395 	elem = TAIL(dyndb_implementations);
396 	while (elem != NULL) {
397 		prev = PREV(elem, link);
398 		UNLINK(dyndb_implementations, elem, link);
399 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
400 			      DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
401 			      "unloading DynDB instance '%s'", elem->name);
402 		elem->destroy_func(&elem->inst);
403 		ENSURE(elem->inst == NULL);
404 		unload_library(&elem);
405 		elem = prev;
406 	}
407 	UNLOCK(&dyndb_lock);
408 
409 	if (exiting == true)
410 		isc_mutex_destroy(&dyndb_lock);
411 }
412 
413 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)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 {
418 	dns_dyndbctx_t *dctx;
419 
420 	REQUIRE(dctxp != NULL && *dctxp == NULL);
421 
422 	dctx = isc_mem_get(mctx, sizeof(*dctx));
423 	if (dctx == NULL)
424 		return (ISC_R_NOMEMORY);
425 
426 	memset(dctx, 0, sizeof(*dctx));
427 	if (view != NULL)
428 		dns_view_attach(view, &dctx->view);
429 	if (zmgr != NULL)
430 		dns_zonemgr_attach(zmgr, &dctx->zmgr);
431 	if (task != NULL)
432 		isc_task_attach(task, &dctx->task);
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
dns_dyndb_destroyctx(dns_dyndbctx_t ** dctxp)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 	if (dctx->zmgr != NULL)
460 		dns_zonemgr_detach(&dctx->zmgr);
461 	if (dctx->task != NULL)
462 		isc_task_detach(&dctx->task);
463 	dctx->timermgr = NULL;
464 	dctx->lctx = NULL;
465 
466 	isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
467 }
468