1 /* $NetBSD: dlz_dlopen_driver.c,v 1.4 2015/07/08 17:28:55 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2011-2014 Internet Systems Consortium, Inc. ("ISC") 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* Id: dlz_dlopen_driver.c,v 1.5 2011/10/14 00:52:32 marka Exp */ 20 21 #include <config.h> 22 23 #include <windows.h> 24 25 #include <stdio.h> 26 #include <string.h> 27 #include <stdlib.h> 28 29 #include <dns/log.h> 30 #include <dns/result.h> 31 #include <dns/dlz_dlopen.h> 32 33 #include <isc/mem.h> 34 #include <isc/print.h> 35 #include <isc/result.h> 36 #include <isc/util.h> 37 38 #include <named/globals.h> 39 40 #include <dlz/dlz_dlopen_driver.h> 41 42 #ifdef ISC_DLZ_DLOPEN 43 static dns_sdlzimplementation_t *dlz_dlopen = NULL; 44 45 46 typedef struct dlopen_data { 47 isc_mem_t *mctx; 48 char *dl_path; 49 char *dlzname; 50 HMODULE dl_handle; 51 void *dbdata; 52 unsigned int flags; 53 isc_mutex_t lock; 54 int version; 55 isc_boolean_t in_configure; 56 57 dlz_dlopen_version_t *dlz_version; 58 dlz_dlopen_create_t *dlz_create; 59 dlz_dlopen_findzonedb_t *dlz_findzonedb; 60 dlz_dlopen_lookup_t *dlz_lookup; 61 dlz_dlopen_authority_t *dlz_authority; 62 dlz_dlopen_allnodes_t *dlz_allnodes; 63 dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr; 64 dlz_dlopen_newversion_t *dlz_newversion; 65 dlz_dlopen_closeversion_t *dlz_closeversion; 66 dlz_dlopen_configure_t *dlz_configure; 67 dlz_dlopen_ssumatch_t *dlz_ssumatch; 68 dlz_dlopen_addrdataset_t *dlz_addrdataset; 69 dlz_dlopen_subrdataset_t *dlz_subrdataset; 70 dlz_dlopen_delrdataset_t *dlz_delrdataset; 71 dlz_dlopen_destroy_t *dlz_destroy; 72 } dlopen_data_t; 73 74 /* Modules can choose whether they are lock-safe or not. */ 75 #define MAYBE_LOCK(cd) \ 76 do { \ 77 if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \ 78 cd->in_configure == ISC_FALSE) \ 79 LOCK(&cd->lock); \ 80 } while (/*CONSTCOND*/0) 81 82 #define MAYBE_UNLOCK(cd) \ 83 do { \ 84 if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \ 85 cd->in_configure == ISC_FALSE) \ 86 UNLOCK(&cd->lock); \ 87 } while (/*CONSTCOND*/0) 88 89 /* 90 * Log a message at the given level. 91 */ 92 static void dlopen_log(int level, const char *fmt, ...) 93 { 94 va_list ap; 95 va_start(ap, fmt); 96 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, 97 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(level), 98 fmt, ap); 99 va_end(ap); 100 } 101 102 /* 103 * SDLZ methods 104 */ 105 106 static isc_result_t 107 dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata, 108 dns_sdlzallnodes_t *allnodes) 109 { 110 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 111 isc_result_t result; 112 113 114 UNUSED(driverarg); 115 116 if (cd->dlz_allnodes == NULL) { 117 return (ISC_R_NOPERM); 118 } 119 120 MAYBE_LOCK(cd); 121 result = cd->dlz_allnodes(zone, cd->dbdata, allnodes); 122 MAYBE_UNLOCK(cd); 123 return (result); 124 } 125 126 127 static isc_result_t 128 dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name, 129 const char *client) 130 { 131 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 132 isc_result_t result; 133 134 UNUSED(driverarg); 135 136 137 if (cd->dlz_allowzonexfr == NULL) { 138 return (ISC_R_NOPERM); 139 } 140 141 MAYBE_LOCK(cd); 142 result = cd->dlz_allowzonexfr(cd->dbdata, name, client); 143 MAYBE_UNLOCK(cd); 144 return (result); 145 } 146 147 static isc_result_t 148 dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata, 149 dns_sdlzlookup_t *lookup) 150 { 151 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 152 isc_result_t result; 153 154 UNUSED(driverarg); 155 156 if (cd->dlz_authority == NULL) { 157 return (ISC_R_NOTIMPLEMENTED); 158 } 159 160 MAYBE_LOCK(cd); 161 result = cd->dlz_authority(zone, cd->dbdata, lookup); 162 MAYBE_UNLOCK(cd); 163 return (result); 164 } 165 166 static isc_result_t 167 dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name, 168 dns_clientinfomethods_t *methods, 169 dns_clientinfo_t *clientinfo) 170 { 171 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 172 isc_result_t result; 173 174 UNUSED(driverarg); 175 176 MAYBE_LOCK(cd); 177 result = cd->dlz_findzonedb(cd->dbdata, name, methods, clientinfo); 178 MAYBE_UNLOCK(cd); 179 return (result); 180 } 181 182 183 static isc_result_t 184 dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg, 185 void *dbdata, dns_sdlzlookup_t *lookup, 186 dns_clientinfomethods_t *methods, 187 dns_clientinfo_t *clientinfo) 188 { 189 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 190 isc_result_t result; 191 192 UNUSED(driverarg); 193 194 MAYBE_LOCK(cd); 195 result = cd->dlz_lookup(zone, name, cd->dbdata, lookup, 196 methods, clientinfo); 197 MAYBE_UNLOCK(cd); 198 return (result); 199 } 200 201 /* 202 * Load a symbol from the library 203 */ 204 static void * 205 dl_load_symbol(dlopen_data_t *cd, const char *symbol, isc_boolean_t mandatory) { 206 void *ptr = GetProcAddress(cd->dl_handle, symbol); 207 if (ptr == NULL && mandatory) { 208 dlopen_log(ISC_LOG_ERROR, 209 "dlz_dlopen: library '%s' is missing " 210 "required symbol '%s'", cd->dl_path, symbol); 211 } 212 return (ptr); 213 } 214 215 /* 216 * Called at startup for each dlopen zone in named.conf 217 */ 218 static isc_result_t 219 dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[], 220 void *driverarg, void **dbdata) 221 { 222 dlopen_data_t *cd; 223 isc_mem_t *mctx = NULL; 224 isc_result_t result = ISC_R_FAILURE; 225 isc_boolean_t triedload = ISC_FALSE; 226 227 UNUSED(driverarg); 228 229 if (argc < 2) { 230 dlopen_log(ISC_LOG_ERROR, 231 "dlz_dlopen driver for '%s' needs a path to " 232 "the shared library", dlzname); 233 return (ISC_R_FAILURE); 234 } 235 236 isc_mem_create(0, 0, &mctx); 237 238 cd = isc_mem_get(mctx, sizeof(*cd)); 239 if (cd == NULL) { 240 isc_mem_destroy(&mctx); 241 return (ISC_R_NOMEMORY); 242 } 243 memset(cd, 0, sizeof(*cd)); 244 245 cd->mctx = mctx; 246 247 cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]); 248 if (cd->dl_path == NULL) { 249 result = ISC_R_NOMEMORY; 250 goto failed; 251 } 252 253 cd->dlzname = isc_mem_strdup(cd->mctx, dlzname); 254 if (cd->dlzname == NULL) { 255 result = ISC_R_NOMEMORY; 256 goto failed; 257 } 258 259 triedload = ISC_TRUE; 260 261 /* Initialize the lock */ 262 result = isc_mutex_init(&cd->lock); 263 if (result != ISC_R_SUCCESS) 264 goto failed; 265 266 /* Open the library */ 267 cd->dl_handle = LoadLibraryA(cd->dl_path); 268 if (cd->dl_handle == NULL) { 269 unsigned int error = GetLastError(); 270 271 dlopen_log(ISC_LOG_ERROR, 272 "dlz_dlopen failed to open library '%s' - %u", 273 cd->dl_path, error); 274 result = ISC_R_FAILURE; 275 goto cleanup_lock; 276 } 277 278 /* Find the symbols */ 279 cd->dlz_version = (dlz_dlopen_version_t *) 280 dl_load_symbol(cd, "dlz_version", ISC_TRUE); 281 cd->dlz_create = (dlz_dlopen_create_t *) 282 dl_load_symbol(cd, "dlz_create", ISC_TRUE); 283 cd->dlz_lookup = (dlz_dlopen_lookup_t *) 284 dl_load_symbol(cd, "dlz_lookup", ISC_TRUE); 285 cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *) 286 dl_load_symbol(cd, "dlz_findzonedb", ISC_TRUE); 287 288 if (cd->dlz_create == NULL || 289 cd->dlz_version == NULL || 290 cd->dlz_lookup == NULL || 291 cd->dlz_findzonedb == NULL) 292 { 293 /* We're missing a required symbol */ 294 result = ISC_R_FAILURE; 295 goto cleanup_lock; 296 } 297 298 cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *) 299 dl_load_symbol(cd, "dlz_allowzonexfr", ISC_FALSE); 300 cd->dlz_allnodes = (dlz_dlopen_allnodes_t *) 301 dl_load_symbol(cd, "dlz_allnodes", 302 ISC_TF(cd->dlz_allowzonexfr != NULL)); 303 cd->dlz_authority = (dlz_dlopen_authority_t *) 304 dl_load_symbol(cd, "dlz_authority", ISC_FALSE); 305 cd->dlz_newversion = (dlz_dlopen_newversion_t *) 306 dl_load_symbol(cd, "dlz_newversion", ISC_FALSE); 307 cd->dlz_closeversion = (dlz_dlopen_closeversion_t *) 308 dl_load_symbol(cd, "dlz_closeversion", 309 ISC_TF(cd->dlz_newversion != NULL)); 310 cd->dlz_configure = (dlz_dlopen_configure_t *) 311 dl_load_symbol(cd, "dlz_configure", ISC_FALSE); 312 cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *) 313 dl_load_symbol(cd, "dlz_ssumatch", ISC_FALSE); 314 cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *) 315 dl_load_symbol(cd, "dlz_addrdataset", ISC_FALSE); 316 cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *) 317 dl_load_symbol(cd, "dlz_subrdataset", ISC_FALSE); 318 cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *) 319 dl_load_symbol(cd, "dlz_delrdataset", ISC_FALSE); 320 321 /* Check the version of the API is the same */ 322 cd->version = cd->dlz_version(&cd->flags); 323 if (cd->version < (DLZ_DLOPEN_VERSION - DLZ_DLOPEN_AGE) || 324 cd->version > DLZ_DLOPEN_VERSION) 325 { 326 dlopen_log(ISC_LOG_ERROR, 327 "dlz_dlopen: %s: incorrect driver API version %d, " 328 "requires %d", 329 cd->dl_path, cd->version, DLZ_DLOPEN_VERSION); 330 result = ISC_R_FAILURE; 331 goto cleanup_lock; 332 } 333 334 /* 335 * Call the library's create function. Note that this is an 336 * extended version of dlz create, with the addition of 337 * named function pointers for helper functions that the 338 * driver will need. This avoids the need for the backend to 339 * link the BIND9 libraries 340 */ 341 MAYBE_LOCK(cd); 342 result = cd->dlz_create(dlzname, argc-1, argv+1, 343 &cd->dbdata, 344 "log", dlopen_log, 345 "putrr", dns_sdlz_putrr, 346 "putnamedrr", dns_sdlz_putnamedrr, 347 "writeable_zone", dns_dlz_writeablezone, 348 NULL); 349 MAYBE_UNLOCK(cd); 350 if (result != ISC_R_SUCCESS) 351 goto cleanup_lock; 352 353 *dbdata = cd; 354 355 return (ISC_R_SUCCESS); 356 357 cleanup_lock: 358 DESTROYLOCK(&cd->lock); 359 failed: 360 dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname); 361 if (cd->dl_path) 362 isc_mem_free(mctx, cd->dl_path); 363 if (cd->dlzname) 364 isc_mem_free(mctx, cd->dlzname); 365 if (triedload) 366 (void) isc_mutex_destroy(&cd->lock); 367 if (cd->dl_handle) 368 FreeLibrary(cd->dl_handle); 369 isc_mem_put(mctx, cd, sizeof(*cd)); 370 isc_mem_destroy(&mctx); 371 return (result); 372 } 373 374 375 /* 376 * Called when bind is shutting down 377 */ 378 static void 379 dlopen_dlz_destroy(void *driverarg, void *dbdata) { 380 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 381 isc_mem_t *mctx; 382 383 UNUSED(driverarg); 384 385 if (cd->dlz_destroy) { 386 MAYBE_LOCK(cd); 387 cd->dlz_destroy(cd->dbdata); 388 MAYBE_UNLOCK(cd); 389 } 390 391 if (cd->dl_path) 392 isc_mem_free(cd->mctx, cd->dl_path); 393 if (cd->dlzname) 394 isc_mem_free(cd->mctx, cd->dlzname); 395 396 if (cd->dl_handle) 397 FreeLibrary(cd->dl_handle); 398 399 DESTROYLOCK(&cd->lock); 400 401 mctx = cd->mctx; 402 isc_mem_put(mctx, cd, sizeof(*cd)); 403 isc_mem_destroy(&mctx); 404 } 405 406 /* 407 * Called to start a transaction 408 */ 409 static isc_result_t 410 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata, 411 void **versionp) 412 { 413 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 414 isc_result_t result; 415 416 UNUSED(driverarg); 417 418 if (cd->dlz_newversion == NULL) 419 return (ISC_R_NOTIMPLEMENTED); 420 421 MAYBE_LOCK(cd); 422 result = cd->dlz_newversion(zone, cd->dbdata, versionp); 423 MAYBE_UNLOCK(cd); 424 return (result); 425 } 426 427 /* 428 * Called to end a transaction 429 */ 430 static void 431 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit, 432 void *driverarg, void *dbdata, void **versionp) 433 { 434 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 435 436 UNUSED(driverarg); 437 438 if (cd->dlz_newversion == NULL) { 439 *versionp = NULL; 440 return; 441 } 442 443 MAYBE_LOCK(cd); 444 cd->dlz_closeversion(zone, commit, cd->dbdata, versionp); 445 MAYBE_UNLOCK(cd); 446 } 447 448 /* 449 * Called on startup to configure any writeable zones 450 */ 451 static isc_result_t 452 dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb, 453 void *driverarg, void *dbdata) 454 { 455 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 456 isc_result_t result; 457 458 UNUSED(driverarg); 459 460 if (cd->dlz_configure == NULL) 461 return (ISC_R_SUCCESS); 462 463 MAYBE_LOCK(cd); 464 cd->in_configure = ISC_TRUE; 465 result = cd->dlz_configure(view, dlzdb, cd->dbdata); 466 cd->in_configure = ISC_FALSE; 467 MAYBE_UNLOCK(cd); 468 469 return (result); 470 } 471 472 473 /* 474 * Check for authority to change a name 475 */ 476 static isc_boolean_t 477 dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, 478 const char *type, const char *key, isc_uint32_t keydatalen, 479 unsigned char *keydata, void *driverarg, void *dbdata) 480 { 481 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 482 isc_boolean_t ret; 483 484 UNUSED(driverarg); 485 486 if (cd->dlz_ssumatch == NULL) 487 return (ISC_FALSE); 488 489 MAYBE_LOCK(cd); 490 ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen, 491 keydata, cd->dbdata); 492 MAYBE_UNLOCK(cd); 493 494 return (ret); 495 } 496 497 498 /* 499 * Add an rdataset 500 */ 501 static isc_result_t 502 dlopen_dlz_addrdataset(const char *name, const char *rdatastr, 503 void *driverarg, void *dbdata, void *version) 504 { 505 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 506 isc_result_t result; 507 508 UNUSED(driverarg); 509 510 if (cd->dlz_addrdataset == NULL) 511 return (ISC_R_NOTIMPLEMENTED); 512 513 MAYBE_LOCK(cd); 514 result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version); 515 MAYBE_UNLOCK(cd); 516 517 return (result); 518 } 519 520 /* 521 * Subtract an rdataset 522 */ 523 static isc_result_t 524 dlopen_dlz_subrdataset(const char *name, const char *rdatastr, 525 void *driverarg, void *dbdata, void *version) 526 { 527 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 528 isc_result_t result; 529 530 UNUSED(driverarg); 531 532 if (cd->dlz_subrdataset == NULL) 533 return (ISC_R_NOTIMPLEMENTED); 534 535 MAYBE_LOCK(cd); 536 result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version); 537 MAYBE_UNLOCK(cd); 538 539 return (result); 540 } 541 542 /* 543 delete a rdataset 544 */ 545 static isc_result_t 546 dlopen_dlz_delrdataset(const char *name, const char *type, 547 void *driverarg, void *dbdata, void *version) 548 { 549 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 550 isc_result_t result; 551 552 UNUSED(driverarg); 553 554 if (cd->dlz_delrdataset == NULL) 555 return (ISC_R_NOTIMPLEMENTED); 556 557 MAYBE_LOCK(cd); 558 result = cd->dlz_delrdataset(name, type, cd->dbdata, version); 559 MAYBE_UNLOCK(cd); 560 561 return (result); 562 } 563 564 565 static dns_sdlzmethods_t dlz_dlopen_methods = { 566 dlopen_dlz_create, 567 dlopen_dlz_destroy, 568 dlopen_dlz_findzonedb, 569 dlopen_dlz_lookup, 570 dlopen_dlz_authority, 571 dlopen_dlz_allnodes, 572 dlopen_dlz_allowzonexfr, 573 dlopen_dlz_newversion, 574 dlopen_dlz_closeversion, 575 dlopen_dlz_configure, 576 dlopen_dlz_ssumatch, 577 dlopen_dlz_addrdataset, 578 dlopen_dlz_subrdataset, 579 dlopen_dlz_delrdataset 580 }; 581 #endif 582 583 /* 584 * Register driver with BIND 585 */ 586 isc_result_t 587 dlz_dlopen_init(isc_mem_t *mctx) { 588 #ifndef ISC_DLZ_DLOPEN 589 UNUSED(mctx); 590 return (ISC_R_NOTIMPLEMENTED); 591 #else 592 isc_result_t result; 593 594 dlopen_log(2, "Registering DLZ_dlopen driver"); 595 596 result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL, 597 DNS_SDLZFLAG_RELATIVEOWNER | 598 DNS_SDLZFLAG_RELATIVERDATA | 599 DNS_SDLZFLAG_THREADSAFE, 600 mctx, &dlz_dlopen); 601 602 if (result != ISC_R_SUCCESS) { 603 UNEXPECTED_ERROR(__FILE__, __LINE__, 604 "dns_sdlzregister() failed: %s", 605 isc_result_totext(result)); 606 result = ISC_R_UNEXPECTED; 607 } 608 609 return (result); 610 #endif 611 } 612 613 614 /* 615 * Unregister the driver 616 */ 617 void 618 dlz_dlopen_clear(void) { 619 #ifdef ISC_DLZ_DLOPEN 620 dlopen_log(2, "Unregistering DLZ_dlopen driver"); 621 if (dlz_dlopen != NULL) 622 dns_sdlzunregister(&dlz_dlopen); 623 #endif 624 } 625