Home | History | Annotate | Line # | Download | only in dns
dyndb.c revision 1.1.1.8
      1 /*	$NetBSD: dyndb.c,v 1.1.1.8 2022/09/23 12:09:17 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
     71 dyndb_initialize(void) {
     72 	isc_mutex_init(&dyndb_lock);
     73 	INIT_LIST(dyndb_implementations);
     74 }
     75 
     76 static dyndb_implementation_t *
     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
     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
    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
    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
    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
    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
    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
    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
    341 unload_library(dyndb_implementation_t **impp) {
    342 	UNUSED(impp);
    343 }
    344 #endif /* HAVE_DLFCN_H */
    345 
    346 isc_result_t
    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
    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
    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
    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