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