1 /* $NetBSD: dyndb.c,v 1.9 2021/08/19 11:50:17 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 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 69 dyndb_initialize(void) { 70 isc_mutex_init(&dyndb_lock); 71 INIT_LIST(dyndb_implementations); 72 } 73 74 static dyndb_implementation_t * 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 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 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 **)®ister_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 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 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 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 **)®ister_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 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 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 339 unload_library(dyndb_implementation_t **impp) { 340 UNUSED(impp); 341 } 342 #endif /* HAVE_DLFCN_H */ 343 344 isc_result_t 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 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 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 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