xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/dns/dyndb.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
1 /*	$NetBSD: dyndb.c,v 1.1 2024/02/18 20:57:31 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #if HAVE_DLFCN_H
17 #include <dlfcn.h>
18 #elif _WIN32
19 #include <windows.h>
20 #endif /* if HAVE_DLFCN_H */
21 
22 #include <string.h>
23 
24 #include <isc/buffer.h>
25 #include <isc/mem.h>
26 #include <isc/mutex.h>
27 #include <isc/once.h>
28 #include <isc/region.h>
29 #include <isc/result.h>
30 #include <isc/task.h>
31 #include <isc/types.h>
32 #include <isc/util.h>
33 
34 #include <dns/dyndb.h>
35 #include <dns/log.h>
36 #include <dns/types.h>
37 #include <dns/view.h>
38 #include <dns/zone.h>
39 
40 #define CHECK(op)                            \
41 	do {                                 \
42 		result = (op);               \
43 		if (result != ISC_R_SUCCESS) \
44 			goto cleanup;        \
45 	} while (0)
46 
47 typedef struct dyndb_implementation dyndb_implementation_t;
48 struct 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  */
64 static LIST(dyndb_implementation_t) dyndb_implementations;
65 
66 /* Locks dyndb_implementations. */
67 static isc_mutex_t dyndb_lock;
68 static isc_once_t once = ISC_ONCE_INIT;
69 
70 static void
dyndb_initialize(void)71 dyndb_initialize(void) {
72 	isc_mutex_init(&dyndb_lock);
73 	INIT_LIST(dyndb_implementations);
74 }
75 
76 static dyndb_implementation_t *
impfind(const char * name)77 impfind(const char *name) {
78 	dyndb_implementation_t *imp;
79 
80 	for (imp = ISC_LIST_HEAD(dyndb_implementations); imp != NULL;
81 	     imp = ISC_LIST_NEXT(imp, link))
82 	{
83 		if (strcasecmp(name, imp->name) == 0) {
84 			return (imp);
85 		}
86 	}
87 	return (NULL);
88 }
89 
90 #if HAVE_DLFCN_H && HAVE_DLOPEN
91 static isc_result_t
load_symbol(void * handle,const char * filename,const char * symbol_name,void ** symbolp)92 load_symbol(void *handle, const char *filename, const char *symbol_name,
93 	    void **symbolp) {
94 	const char *errmsg;
95 	void *symbol;
96 
97 	REQUIRE(handle != NULL);
98 	REQUIRE(symbolp != NULL && *symbolp == NULL);
99 
100 	symbol = dlsym(handle, symbol_name);
101 	if (symbol == NULL) {
102 		errmsg = dlerror();
103 		if (errmsg == NULL) {
104 			errmsg = "returned function pointer is NULL";
105 		}
106 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
107 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
108 			      "failed to lookup symbol %s in "
109 			      "dyndb module '%s': %s",
110 			      symbol_name, filename, errmsg);
111 		return (ISC_R_FAILURE);
112 	}
113 	dlerror();
114 
115 	*symbolp = symbol;
116 
117 	return (ISC_R_SUCCESS);
118 }
119 
120 static isc_result_t
load_library(isc_mem_t * mctx,const char * filename,const char * instname,dyndb_implementation_t ** impp)121 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
122 	     dyndb_implementation_t **impp) {
123 	isc_result_t result;
124 	void *handle = NULL;
125 	dyndb_implementation_t *imp = NULL;
126 	dns_dyndb_register_t *register_func = NULL;
127 	dns_dyndb_destroy_t *destroy_func = NULL;
128 	dns_dyndb_version_t *version_func = NULL;
129 	int version;
130 
131 	REQUIRE(impp != NULL && *impp == NULL);
132 
133 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
134 		      ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
135 		      instname, filename);
136 
137 	handle = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
138 	if (handle == NULL) {
139 		CHECK(ISC_R_FAILURE);
140 	}
141 
142 	/* Clear dlerror */
143 	dlerror();
144 
145 	CHECK(load_symbol(handle, filename, "dyndb_version",
146 			  (void **)&version_func));
147 
148 	version = version_func(NULL);
149 	if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
150 	    version > DNS_DYNDB_VERSION)
151 	{
152 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
153 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
154 			      "driver API version mismatch: %d/%d", version,
155 			      DNS_DYNDB_VERSION);
156 		CHECK(ISC_R_FAILURE);
157 	}
158 
159 	CHECK(load_symbol(handle, filename, "dyndb_init",
160 			  (void **)&register_func));
161 	CHECK(load_symbol(handle, filename, "dyndb_destroy",
162 			  (void **)&destroy_func));
163 
164 	imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
165 
166 	imp->mctx = NULL;
167 	isc_mem_attach(mctx, &imp->mctx);
168 	imp->handle = handle;
169 	imp->register_func = register_func;
170 	imp->destroy_func = destroy_func;
171 	imp->name = isc_mem_strdup(mctx, instname);
172 
173 	imp->inst = NULL;
174 	INIT_LINK(imp, link);
175 
176 	*impp = imp;
177 	imp = NULL;
178 
179 cleanup:
180 	if (result != ISC_R_SUCCESS) {
181 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
182 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
183 			      "failed to dynamically load instance '%s' "
184 			      "driver '%s': %s (%s)",
185 			      instname, filename, dlerror(),
186 			      isc_result_totext(result));
187 	}
188 	if (imp != NULL) {
189 		isc_mem_putanddetach(&imp->mctx, imp,
190 				     sizeof(dyndb_implementation_t));
191 	}
192 	if (result != ISC_R_SUCCESS && handle != NULL) {
193 		dlclose(handle);
194 	}
195 
196 	return (result);
197 }
198 
199 static void
unload_library(dyndb_implementation_t ** impp)200 unload_library(dyndb_implementation_t **impp) {
201 	dyndb_implementation_t *imp;
202 
203 	REQUIRE(impp != NULL && *impp != NULL);
204 
205 	imp = *impp;
206 	*impp = NULL;
207 
208 	isc_mem_free(imp->mctx, imp->name);
209 	isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
210 }
211 #elif _WIN32
212 static isc_result_t
load_symbol(HMODULE handle,const char * filename,const char * symbol_name,void ** symbolp)213 load_symbol(HMODULE handle, const char *filename, const char *symbol_name,
214 	    void **symbolp) {
215 	void *symbol;
216 
217 	REQUIRE(handle != NULL);
218 	REQUIRE(symbolp != NULL && *symbolp == NULL);
219 
220 	symbol = GetProcAddress(handle, symbol_name);
221 	if (symbol == NULL) {
222 		int errstatus = GetLastError();
223 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
224 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
225 			      "failed to lookup symbol %s in "
226 			      "dyndb module '%s': %d",
227 			      symbol_name, filename, errstatus);
228 		return (ISC_R_FAILURE);
229 	}
230 
231 	*symbolp = symbol;
232 
233 	return (ISC_R_SUCCESS);
234 }
235 
236 static isc_result_t
load_library(isc_mem_t * mctx,const char * filename,const char * instname,dyndb_implementation_t ** impp)237 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
238 	     dyndb_implementation_t **impp) {
239 	isc_result_t result;
240 	HMODULE handle;
241 	dyndb_implementation_t *imp = NULL;
242 	dns_dyndb_register_t *register_func = NULL;
243 	dns_dyndb_destroy_t *destroy_func = NULL;
244 	dns_dyndb_version_t *version_func = NULL;
245 	int version;
246 
247 	REQUIRE(impp != NULL && *impp == NULL);
248 
249 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
250 		      ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
251 		      instname, filename);
252 
253 	handle = LoadLibraryA(filename);
254 	if (handle == NULL) {
255 		CHECK(ISC_R_FAILURE);
256 	}
257 
258 	CHECK(load_symbol(handle, filename, "dyndb_version",
259 			  (void **)&version_func));
260 
261 	version = version_func(NULL);
262 	if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
263 	    version > DNS_DYNDB_VERSION)
264 	{
265 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
266 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
267 			      "driver API version mismatch: %d/%d", version,
268 			      DNS_DYNDB_VERSION);
269 		CHECK(ISC_R_FAILURE);
270 	}
271 
272 	CHECK(load_symbol(handle, filename, "dyndb_init",
273 			  (void **)&register_func));
274 	CHECK(load_symbol(handle, filename, "dyndb_destroy",
275 			  (void **)&destroy_func));
276 
277 	imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
278 
279 	imp->mctx = NULL;
280 	isc_mem_attach(mctx, &imp->mctx);
281 	imp->handle = handle;
282 	imp->register_func = register_func;
283 	imp->destroy_func = destroy_func;
284 	imp->name = isc_mem_strdup(mctx, instname);
285 
286 	imp->inst = NULL;
287 	INIT_LINK(imp, link);
288 
289 	*impp = imp;
290 	imp = NULL;
291 
292 cleanup:
293 	if (result != ISC_R_SUCCESS) {
294 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
295 			      DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
296 			      "failed to dynamically load instance '%s' "
297 			      "driver '%s': %d (%s)",
298 			      instname, filename, GetLastError(),
299 			      isc_result_totext(result));
300 	}
301 	if (imp != NULL) {
302 		isc_mem_putanddetach(&imp->mctx, imp,
303 				     sizeof(dyndb_implementation_t));
304 	}
305 	if (result != ISC_R_SUCCESS && handle != NULL) {
306 		FreeLibrary(handle);
307 	}
308 
309 	return (result);
310 }
311 
312 static void
unload_library(dyndb_implementation_t ** impp)313 unload_library(dyndb_implementation_t **impp) {
314 	dyndb_implementation_t *imp;
315 
316 	REQUIRE(impp != NULL && *impp != NULL);
317 
318 	imp = *impp;
319 	*impp = NULL;
320 
321 	isc_mem_free(imp->mctx, imp->name);
322 	isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
323 }
324 #else  /* HAVE_DLFCN_H || _WIN32 */
325 static isc_result_t
load_library(isc_mem_t * mctx,const char * filename,const char * instname,dyndb_implementation_t ** impp)326 load_library(isc_mem_t *mctx, const char *filename, const char *instname,
327 	     dyndb_implementation_t **impp) {
328 	UNUSED(mctx);
329 	UNUSED(filename);
330 	UNUSED(instname);
331 	UNUSED(impp);
332 
333 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
334 		      ISC_LOG_ERROR,
335 		      "dynamic database support is not implemented");
336 
337 	return (ISC_R_NOTIMPLEMENTED);
338 }
339 
340 static void
unload_library(dyndb_implementation_t ** impp)341 unload_library(dyndb_implementation_t **impp) {
342 	UNUSED(impp);
343 }
344 #endif /* HAVE_DLFCN_H */
345 
346 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)347 dns_dyndb_load(const char *libname, const char *name, const char *parameters,
348 	       const char *file, unsigned long line, isc_mem_t *mctx,
349 	       const dns_dyndbctx_t *dctx) {
350 	isc_result_t result;
351 	dyndb_implementation_t *implementation = NULL;
352 
353 	REQUIRE(DNS_DYNDBCTX_VALID(dctx));
354 	REQUIRE(name != NULL);
355 
356 	RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
357 
358 	LOCK(&dyndb_lock);
359 
360 	/* duplicate instance names are not allowed */
361 	if (impfind(name) != NULL) {
362 		CHECK(ISC_R_EXISTS);
363 	}
364 
365 	CHECK(load_library(mctx, libname, name, &implementation));
366 	CHECK(implementation->register_func(mctx, name, parameters, file, line,
367 					    dctx, &implementation->inst));
368 
369 	APPEND(dyndb_implementations, implementation, link);
370 	result = ISC_R_SUCCESS;
371 
372 cleanup:
373 	if (result != ISC_R_SUCCESS) {
374 		if (implementation != NULL) {
375 			unload_library(&implementation);
376 		}
377 	}
378 
379 	UNLOCK(&dyndb_lock);
380 	return (result);
381 }
382 
383 void
dns_dyndb_cleanup(bool exiting)384 dns_dyndb_cleanup(bool exiting) {
385 	dyndb_implementation_t *elem;
386 	dyndb_implementation_t *prev;
387 
388 	RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
389 
390 	LOCK(&dyndb_lock);
391 	elem = TAIL(dyndb_implementations);
392 	while (elem != NULL) {
393 		prev = PREV(elem, link);
394 		UNLINK(dyndb_implementations, elem, link);
395 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
396 			      DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
397 			      "unloading DynDB instance '%s'", elem->name);
398 		elem->destroy_func(&elem->inst);
399 		ENSURE(elem->inst == NULL);
400 		unload_library(&elem);
401 		elem = prev;
402 	}
403 	UNLOCK(&dyndb_lock);
404 
405 	if (exiting) {
406 		isc_mutex_destroy(&dyndb_lock);
407 	}
408 }
409 
410 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)411 dns_dyndb_createctx(isc_mem_t *mctx, const void *hashinit, isc_log_t *lctx,
412 		    dns_view_t *view, dns_zonemgr_t *zmgr, isc_task_t *task,
413 		    isc_timermgr_t *tmgr, dns_dyndbctx_t **dctxp) {
414 	dns_dyndbctx_t *dctx;
415 
416 	REQUIRE(dctxp != NULL && *dctxp == NULL);
417 
418 	dctx = isc_mem_get(mctx, sizeof(*dctx));
419 
420 	memset(dctx, 0, sizeof(*dctx));
421 	if (view != NULL) {
422 		dns_view_attach(view, &dctx->view);
423 	}
424 	if (zmgr != NULL) {
425 		dns_zonemgr_attach(zmgr, &dctx->zmgr);
426 	}
427 	if (task != NULL) {
428 		isc_task_attach(task, &dctx->task);
429 	}
430 	dctx->timermgr = tmgr;
431 	dctx->hashinit = hashinit;
432 	dctx->lctx = lctx;
433 	dctx->memdebug = &isc_mem_debugging;
434 
435 	isc_mem_attach(mctx, &dctx->mctx);
436 	dctx->magic = DNS_DYNDBCTX_MAGIC;
437 
438 	*dctxp = dctx;
439 
440 	return (ISC_R_SUCCESS);
441 }
442 
443 void
dns_dyndb_destroyctx(dns_dyndbctx_t ** dctxp)444 dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) {
445 	dns_dyndbctx_t *dctx;
446 
447 	REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp));
448 
449 	dctx = *dctxp;
450 	*dctxp = NULL;
451 
452 	dctx->magic = 0;
453 
454 	if (dctx->view != NULL) {
455 		dns_view_detach(&dctx->view);
456 	}
457 	if (dctx->zmgr != NULL) {
458 		dns_zonemgr_detach(&dctx->zmgr);
459 	}
460 	if (dctx->task != NULL) {
461 		isc_task_detach(&dctx->task);
462 	}
463 	dctx->timermgr = NULL;
464 	dctx->lctx = NULL;
465 
466 	isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
467 }
468