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 **)&register_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 **)&register_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