1/* $NetBSD: dlz_dlopen_driver.c,v 1.1.1.1.4.1 2012/06/06 18:17:11 bouyer Exp $ */ 2 3/* 4 * Copyright (C) 2011 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 43static dns_sdlzimplementation_t *dlz_dlopen = NULL; 44 45 46typedef 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 (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 (0) 88 89/* 90 * Log a message at the given level. 91 */ 92static 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 106static isc_result_t 107dlopen_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 127static isc_result_t 128dlopen_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 147static isc_result_t 148dlopen_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 166static isc_result_t 167dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name) 168{ 169 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 170 isc_result_t result; 171 172 UNUSED(driverarg); 173 174 MAYBE_LOCK(cd); 175 result = cd->dlz_findzonedb(cd->dbdata, name); 176 MAYBE_UNLOCK(cd); 177 return (result); 178} 179 180 181static isc_result_t 182dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg, 183 void *dbdata, dns_sdlzlookup_t *lookup, 184 dns_clientinfomethods_t *methods, 185 dns_clientinfo_t *clientinfo) 186{ 187 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 188 isc_result_t result; 189 190 UNUSED(driverarg); 191 192 MAYBE_LOCK(cd); 193 result = cd->dlz_lookup(zone, name, cd->dbdata, lookup, 194 methods, clientinfo); 195 MAYBE_UNLOCK(cd); 196 return (result); 197} 198 199/* 200 * Load a symbol from the library 201 */ 202static void * 203dl_load_symbol(dlopen_data_t *cd, const char *symbol, isc_boolean_t mandatory) { 204 void *ptr = GetProcAddress(cd->dl_handle, symbol); 205 if (ptr == NULL && mandatory) { 206 dlopen_log(ISC_LOG_ERROR, 207 "dlz_dlopen: library '%s' is missing " 208 "required symbol '%s'", cd->dl_path, symbol); 209 } 210 return (ptr); 211} 212 213/* 214 * Called at startup for each dlopen zone in named.conf 215 */ 216static isc_result_t 217dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[], 218 void *driverarg, void **dbdata) 219{ 220 dlopen_data_t *cd; 221 isc_mem_t *mctx = NULL; 222 isc_result_t result = ISC_R_FAILURE; 223 isc_boolean_t triedload = ISC_FALSE; 224 225 UNUSED(driverarg); 226 227 if (argc < 2) { 228 dlopen_log(ISC_LOG_ERROR, 229 "dlz_dlopen driver for '%s' needs a path to " 230 "the shared library", dlzname); 231 return (ISC_R_FAILURE); 232 } 233 234 isc_mem_create(0, 0, &mctx); 235 236 cd = isc_mem_get(mctx, sizeof(*cd)); 237 if (cd == NULL) { 238 isc_mem_destroy(&mctx); 239 return (ISC_R_NOMEMORY); 240 } 241 memset(cd, 0, sizeof(*cd)); 242 243 cd->mctx = mctx; 244 245 cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]); 246 if (cd->dl_path == NULL) { 247 goto failed; 248 } 249 250 cd->dlzname = isc_mem_strdup(cd->mctx, dlzname); 251 if (cd->dlzname == NULL) { 252 goto failed; 253 } 254 255 triedload = ISC_TRUE; 256 257 /* Initialize the lock */ 258 isc_mutex_init(&cd->lock); 259 260 /* Open the library */ 261 cd->dl_handle = LoadLibraryA(cd->dl_path); 262 if (cd->dl_handle == NULL) { 263 unsigned int error = GetLastError(); 264 265 dlopen_log(ISC_LOG_ERROR, 266 "dlz_dlopen failed to open library '%s' - %u", 267 cd->dl_path, error); 268 goto failed; 269 } 270 271 /* Find the symbols */ 272 cd->dlz_version = (dlz_dlopen_version_t *) 273 dl_load_symbol(cd, "dlz_version", ISC_TRUE); 274 cd->dlz_create = (dlz_dlopen_create_t *) 275 dl_load_symbol(cd, "dlz_create", ISC_TRUE); 276 cd->dlz_lookup = (dlz_dlopen_lookup_t *) 277 dl_load_symbol(cd, "dlz_lookup", ISC_TRUE); 278 cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *) 279 dl_load_symbol(cd, "dlz_findzonedb", ISC_TRUE); 280 281 if (cd->dlz_create == NULL || 282 cd->dlz_lookup == NULL || 283 cd->dlz_findzonedb == NULL) 284 { 285 /* We're missing a required symbol */ 286 goto failed; 287 } 288 289 cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *) 290 dl_load_symbol(cd, "dlz_allowzonexfr", ISC_FALSE); 291 cd->dlz_allnodes = (dlz_dlopen_allnodes_t *) 292 dl_load_symbol(cd, "dlz_allnodes", 293 ISC_TF(cd->dlz_allowzonexfr != NULL)); 294 cd->dlz_authority = (dlz_dlopen_authority_t *) 295 dl_load_symbol(cd, "dlz_authority", ISC_FALSE); 296 cd->dlz_newversion = (dlz_dlopen_newversion_t *) 297 dl_load_symbol(cd, "dlz_newversion", ISC_FALSE); 298 cd->dlz_closeversion = (dlz_dlopen_closeversion_t *) 299 dl_load_symbol(cd, "dlz_closeversion", 300 ISC_TF(cd->dlz_newversion != NULL)); 301 cd->dlz_configure = (dlz_dlopen_configure_t *) 302 dl_load_symbol(cd, "dlz_configure", ISC_FALSE); 303 cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *) 304 dl_load_symbol(cd, "dlz_ssumatch", ISC_FALSE); 305 cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *) 306 dl_load_symbol(cd, "dlz_addrdataset", ISC_FALSE); 307 cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *) 308 dl_load_symbol(cd, "dlz_subrdataset", ISC_FALSE); 309 cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *) 310 dl_load_symbol(cd, "dlz_delrdataset", ISC_FALSE); 311 312 /* Check the version of the API is the same */ 313 cd->version = cd->dlz_version(&cd->flags); 314 if (cd->version != DLZ_DLOPEN_VERSION) { 315 dlopen_log(ISC_LOG_ERROR, 316 "dlz_dlopen: incorrect version %d " 317 "should be %d in '%s'", 318 cd->version, DLZ_DLOPEN_VERSION, cd->dl_path); 319 goto failed; 320 } 321 322 /* 323 * Call the library's create function. Note that this is an 324 * extended version of dlz create, with the addition of 325 * named function pointers for helper functions that the 326 * driver will need. This avoids the need for the backend to 327 * link the BIND9 libraries 328 */ 329 MAYBE_LOCK(cd); 330 result = cd->dlz_create(dlzname, argc-1, argv+1, 331 &cd->dbdata, 332 "log", dlopen_log, 333 "putrr", dns_sdlz_putrr, 334 "putnamedrr", dns_sdlz_putnamedrr, 335 "writeable_zone", dns_dlz_writeablezone, 336 NULL); 337 MAYBE_UNLOCK(cd); 338 if (result != ISC_R_SUCCESS) 339 goto failed; 340 341 *dbdata = cd; 342 343 return (ISC_R_SUCCESS); 344 345failed: 346 dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname); 347 if (cd->dl_path) 348 isc_mem_free(mctx, cd->dl_path); 349 if (cd->dlzname) 350 isc_mem_free(mctx, cd->dlzname); 351 if (triedload) 352 (void) isc_mutex_destroy(&cd->lock); 353 if (cd->dl_handle) 354 FreeLibrary(cd->dl_handle); 355 isc_mem_put(mctx, cd, sizeof(*cd)); 356 isc_mem_destroy(&mctx); 357 return (result); 358} 359 360 361/* 362 * Called when bind is shutting down 363 */ 364static void 365dlopen_dlz_destroy(void *driverarg, void *dbdata) { 366 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 367 isc_mem_t *mctx; 368 369 UNUSED(driverarg); 370 371 if (cd->dlz_destroy) { 372 MAYBE_LOCK(cd); 373 cd->dlz_destroy(cd->dbdata); 374 MAYBE_UNLOCK(cd); 375 } 376 377 if (cd->dl_path) 378 isc_mem_free(cd->mctx, cd->dl_path); 379 if (cd->dlzname) 380 isc_mem_free(cd->mctx, cd->dlzname); 381 382 if (cd->dl_handle) 383 FreeLibrary(cd->dl_handle); 384 385 (void) isc_mutex_destroy(&cd->lock); 386 387 mctx = cd->mctx; 388 isc_mem_put(mctx, cd, sizeof(*cd)); 389 isc_mem_destroy(&mctx); 390} 391 392/* 393 * Called to start a transaction 394 */ 395static isc_result_t 396dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata, 397 void **versionp) 398{ 399 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 400 isc_result_t result; 401 402 UNUSED(driverarg); 403 404 if (cd->dlz_newversion == NULL) 405 return (ISC_R_NOTIMPLEMENTED); 406 407 MAYBE_LOCK(cd); 408 result = cd->dlz_newversion(zone, cd->dbdata, versionp); 409 MAYBE_UNLOCK(cd); 410 return (result); 411} 412 413/* 414 * Called to end a transaction 415 */ 416static void 417dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit, 418 void *driverarg, void *dbdata, void **versionp) 419{ 420 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 421 422 UNUSED(driverarg); 423 424 if (cd->dlz_newversion == NULL) { 425 *versionp = NULL; 426 return; 427 } 428 429 MAYBE_LOCK(cd); 430 cd->dlz_closeversion(zone, commit, cd->dbdata, versionp); 431 MAYBE_UNLOCK(cd); 432} 433 434/* 435 * Called on startup to configure any writeable zones 436 */ 437static isc_result_t 438dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) { 439 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 440 isc_result_t result; 441 442 UNUSED(driverarg); 443 444 if (cd->dlz_configure == NULL) 445 return (ISC_R_SUCCESS); 446 447 MAYBE_LOCK(cd); 448 cd->in_configure = ISC_TRUE; 449 result = cd->dlz_configure(view, cd->dbdata); 450 cd->in_configure = ISC_FALSE; 451 MAYBE_UNLOCK(cd); 452 453 return (result); 454} 455 456 457/* 458 * Check for authority to change a name 459 */ 460static isc_boolean_t 461dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, 462 const char *type, const char *key, isc_uint32_t keydatalen, 463 unsigned char *keydata, void *driverarg, void *dbdata) 464{ 465 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 466 isc_boolean_t ret; 467 468 UNUSED(driverarg); 469 470 if (cd->dlz_ssumatch == NULL) 471 return (ISC_FALSE); 472 473 MAYBE_LOCK(cd); 474 ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen, 475 keydata, cd->dbdata); 476 MAYBE_UNLOCK(cd); 477 478 return (ret); 479} 480 481 482/* 483 * Add an rdataset 484 */ 485static isc_result_t 486dlopen_dlz_addrdataset(const char *name, const char *rdatastr, 487 void *driverarg, void *dbdata, void *version) 488{ 489 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 490 isc_result_t result; 491 492 UNUSED(driverarg); 493 494 if (cd->dlz_addrdataset == NULL) 495 return (ISC_R_NOTIMPLEMENTED); 496 497 MAYBE_LOCK(cd); 498 result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version); 499 MAYBE_UNLOCK(cd); 500 501 return (result); 502} 503 504/* 505 * Subtract an rdataset 506 */ 507static isc_result_t 508dlopen_dlz_subrdataset(const char *name, const char *rdatastr, 509 void *driverarg, void *dbdata, void *version) 510{ 511 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 512 isc_result_t result; 513 514 UNUSED(driverarg); 515 516 if (cd->dlz_subrdataset == NULL) 517 return (ISC_R_NOTIMPLEMENTED); 518 519 MAYBE_LOCK(cd); 520 result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version); 521 MAYBE_UNLOCK(cd); 522 523 return (result); 524} 525 526/* 527 delete a rdataset 528 */ 529static isc_result_t 530dlopen_dlz_delrdataset(const char *name, const char *type, 531 void *driverarg, void *dbdata, void *version) 532{ 533 dlopen_data_t *cd = (dlopen_data_t *) dbdata; 534 isc_result_t result; 535 536 UNUSED(driverarg); 537 538 if (cd->dlz_delrdataset == NULL) 539 return (ISC_R_NOTIMPLEMENTED); 540 541 MAYBE_LOCK(cd); 542 result = cd->dlz_delrdataset(name, type, cd->dbdata, version); 543 MAYBE_UNLOCK(cd); 544 545 return (result); 546} 547 548 549static dns_sdlzmethods_t dlz_dlopen_methods = { 550 dlopen_dlz_create, 551 dlopen_dlz_destroy, 552 dlopen_dlz_findzonedb, 553 dlopen_dlz_lookup, 554 dlopen_dlz_authority, 555 dlopen_dlz_allnodes, 556 dlopen_dlz_allowzonexfr, 557 dlopen_dlz_newversion, 558 dlopen_dlz_closeversion, 559 dlopen_dlz_configure, 560 dlopen_dlz_ssumatch, 561 dlopen_dlz_addrdataset, 562 dlopen_dlz_subrdataset, 563 dlopen_dlz_delrdataset 564}; 565#endif 566 567/* 568 * Register driver with BIND 569 */ 570isc_result_t 571dlz_dlopen_init(isc_mem_t *mctx) { 572#ifndef ISC_DLZ_DLOPEN 573 UNUSED(mctx); 574 return (ISC_R_NOTIMPLEMENTED); 575#else 576 isc_result_t result; 577 578 dlopen_log(2, "Registering DLZ_dlopen driver"); 579 580 result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL, 581 DNS_SDLZFLAG_RELATIVEOWNER | 582 DNS_SDLZFLAG_THREADSAFE, 583 mctx, &dlz_dlopen); 584 585 if (result != ISC_R_SUCCESS) { 586 UNEXPECTED_ERROR(__FILE__, __LINE__, 587 "dns_sdlzregister() failed: %s", 588 isc_result_totext(result)); 589 result = ISC_R_UNEXPECTED; 590 } 591 592 return (result); 593#endif 594} 595 596 597/* 598 * Unregister the driver 599 */ 600void 601dlz_dlopen_clear(void) { 602#ifdef ISC_DLZ_DLOPEN 603 dlopen_log(2, "Unregistering DLZ_dlopen driver"); 604 if (dlz_dlopen != NULL) 605 dns_sdlzunregister(&dlz_dlopen); 606#endif 607} 608