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