1 /* $NetBSD: dlz.c,v 1.7 2014/12/10 04:37:58 christos Exp $ */ 2 3 /* 4 * Portions Copyright (C) 2005, 2007, 2009-2013 Internet Systems Consortium, Inc. ("ISC") 5 * Portions Copyright (C) 1999-2001 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. 22 * 23 * Permission to use, copy, modify, and distribute this software for any 24 * purpose with or without fee is hereby granted, provided that the 25 * above copyright notice and this permission notice appear in all 26 * copies. 27 * 28 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET 29 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 31 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 32 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 33 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 34 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 35 * USE OR PERFORMANCE OF THIS SOFTWARE. 36 * 37 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was 38 * conceived and contributed by Rob Butler. 39 * 40 * Permission to use, copy, modify, and distribute this software for any 41 * purpose with or without fee is hereby granted, provided that the 42 * above copyright notice and this permission notice appear in all 43 * copies. 44 * 45 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER 46 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 48 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 49 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 50 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 51 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 52 * USE OR PERFORMANCE OF THIS SOFTWARE. 53 */ 54 55 /* Id */ 56 57 /*! \file */ 58 59 /*** 60 *** Imports 61 ***/ 62 63 #include <config.h> 64 65 #include <dns/db.h> 66 #include <dns/dlz.h> 67 #include <dns/fixedname.h> 68 #include <dns/log.h> 69 #include <dns/master.h> 70 #include <dns/ssu.h> 71 #include <dns/zone.h> 72 73 74 #include <isc/buffer.h> 75 #include <isc/magic.h> 76 #include <isc/mem.h> 77 #include <isc/once.h> 78 #include <isc/rwlock.h> 79 #include <isc/string.h> 80 #include <isc/util.h> 81 82 /*** 83 *** Supported DLZ DB Implementations Registry 84 ***/ 85 86 static ISC_LIST(dns_dlzimplementation_t) dlz_implementations; 87 static isc_rwlock_t dlz_implock; 88 static isc_once_t once = ISC_ONCE_INIT; 89 90 static void 91 dlz_initialize(void) { 92 RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS); 93 ISC_LIST_INIT(dlz_implementations); 94 } 95 96 /*% 97 * Searches the dlz_implementations list for a driver matching name. 98 */ 99 static inline dns_dlzimplementation_t * 100 dlz_impfind(const char *name) { 101 dns_dlzimplementation_t *imp; 102 103 for (imp = ISC_LIST_HEAD(dlz_implementations); 104 imp != NULL; 105 imp = ISC_LIST_NEXT(imp, link)) 106 if (strcasecmp(name, imp->name) == 0) 107 return (imp); 108 return (NULL); 109 } 110 111 /*** 112 *** Basic DLZ Methods 113 ***/ 114 115 isc_result_t 116 dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name, 117 isc_sockaddr_t *clientaddr, dns_db_t **dbp) 118 { 119 isc_result_t result = ISC_R_NOTFOUND; 120 dns_dlzallowzonexfr_t allowzonexfr; 121 dns_dlzdb_t *dlzdb; 122 123 /* 124 * Performs checks to make sure data is as we expect it to be. 125 */ 126 REQUIRE(name != NULL); 127 REQUIRE(dbp != NULL && *dbp == NULL); 128 129 /* 130 * Find a driver in which the zone exists and transfer is supported 131 */ 132 for (dlzdb = ISC_LIST_HEAD(view->dlz_searched); 133 dlzdb != NULL; 134 dlzdb = ISC_LIST_NEXT(dlzdb, link)) 135 { 136 REQUIRE(DNS_DLZ_VALID(dlzdb)); 137 138 allowzonexfr = dlzdb->implementation->methods->allowzonexfr; 139 result = (*allowzonexfr)(dlzdb->implementation->driverarg, 140 dlzdb->dbdata, dlzdb->mctx, 141 view->rdclass, name, clientaddr, dbp); 142 143 /* 144 * if ISC_R_NOPERM, we found the right database but 145 * the zone may not transfer. 146 */ 147 if (result == ISC_R_SUCCESS || result == ISC_R_NOPERM) 148 return (result); 149 } 150 151 if (result == ISC_R_NOTIMPLEMENTED) 152 result = ISC_R_NOTFOUND; 153 154 return (result); 155 } 156 157 isc_result_t 158 dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername, 159 unsigned int argc, char *argv[], dns_dlzdb_t **dbp) 160 { 161 dns_dlzimplementation_t *impinfo; 162 isc_result_t result; 163 dns_dlzdb_t *db = NULL; 164 165 /* 166 * initialize the dlz_implementations list, this is guaranteed 167 * to only really happen once. 168 */ 169 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); 170 171 /* 172 * Performs checks to make sure data is as we expect it to be. 173 */ 174 REQUIRE(dbp != NULL && *dbp == NULL); 175 REQUIRE(dlzname != NULL); 176 REQUIRE(drivername != NULL); 177 REQUIRE(mctx != NULL); 178 179 /* write log message */ 180 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 181 DNS_LOGMODULE_DLZ, ISC_LOG_INFO, 182 "Loading '%s' using driver %s", dlzname, drivername); 183 184 /* lock the dlz_implementations list so we can search it. */ 185 RWLOCK(&dlz_implock, isc_rwlocktype_read); 186 187 /* search for the driver implementation */ 188 impinfo = dlz_impfind(drivername); 189 if (impinfo == NULL) { 190 RWUNLOCK(&dlz_implock, isc_rwlocktype_read); 191 192 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 193 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 194 "unsupported DLZ database driver '%s'." 195 " %s not loaded.", 196 drivername, dlzname); 197 198 return (ISC_R_NOTFOUND); 199 } 200 201 /* Allocate memory to hold the DLZ database driver */ 202 db = isc_mem_get(mctx, sizeof(dns_dlzdb_t)); 203 if (db == NULL) { 204 RWUNLOCK(&dlz_implock, isc_rwlocktype_read); 205 return (ISC_R_NOMEMORY); 206 } 207 208 /* Make sure memory region is set to all 0's */ 209 memset(db, 0, sizeof(dns_dlzdb_t)); 210 211 ISC_LINK_INIT(db, link); 212 db->implementation = impinfo; 213 if (dlzname != NULL) 214 db->dlzname = isc_mem_strdup(mctx, dlzname); 215 216 /* Create a new database using implementation 'drivername'. */ 217 result = ((impinfo->methods->create)(mctx, dlzname, argc, argv, 218 impinfo->driverarg, 219 &db->dbdata)); 220 221 /* mark the DLZ driver as valid */ 222 if (result == ISC_R_SUCCESS) { 223 RWUNLOCK(&dlz_implock, isc_rwlocktype_read); 224 db->magic = DNS_DLZ_MAGIC; 225 isc_mem_attach(mctx, &db->mctx); 226 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 227 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 228 "DLZ driver loaded successfully."); 229 *dbp = db; 230 return (ISC_R_SUCCESS); 231 } else { 232 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 233 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 234 "DLZ driver failed to load."); 235 } 236 237 /* impinfo->methods->create failed. */ 238 RWUNLOCK(&dlz_implock, isc_rwlocktype_read); 239 isc_mem_put(mctx, db, sizeof(dns_dlzdb_t)); 240 return (result); 241 } 242 243 void 244 dns_dlzdestroy(dns_dlzdb_t **dbp) { 245 isc_mem_t *mctx; 246 dns_dlzdestroy_t destroy; 247 248 /* Write debugging message to log */ 249 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 250 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 251 "Unloading DLZ driver."); 252 253 /* 254 * Perform checks to make sure data is as we expect it to be. 255 */ 256 REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp)); 257 258 if ((*dbp)->ssutable != NULL) { 259 dns_ssutable_detach(&(*dbp)->ssutable); 260 } 261 262 /* call the drivers destroy method */ 263 if ((*dbp) != NULL) { 264 mctx = (*dbp)->mctx; 265 if ((*dbp)->dlzname != NULL) 266 isc_mem_free(mctx, (*dbp)->dlzname); 267 destroy = (*dbp)->implementation->methods->destroy; 268 (*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata); 269 /* return memory */ 270 isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t)); 271 isc_mem_detach(&mctx); 272 } 273 274 *dbp = NULL; 275 } 276 277 /*% 278 * Registers a DLZ driver. This basically just adds the dlz 279 * driver to the list of available drivers in the dlz_implementations list. 280 */ 281 isc_result_t 282 dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods, 283 void *driverarg, isc_mem_t *mctx, 284 dns_dlzimplementation_t **dlzimp) 285 { 286 287 dns_dlzimplementation_t *dlz_imp; 288 289 /* Write debugging message to log */ 290 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 291 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 292 "Registering DLZ driver '%s'", drivername); 293 294 /* 295 * Performs checks to make sure data is as we expect it to be. 296 */ 297 REQUIRE(drivername != NULL); 298 REQUIRE(methods != NULL); 299 REQUIRE(methods->create != NULL); 300 REQUIRE(methods->destroy != NULL); 301 REQUIRE(methods->findzone != NULL); 302 REQUIRE(mctx != NULL); 303 REQUIRE(dlzimp != NULL && *dlzimp == NULL); 304 305 /* 306 * initialize the dlz_implementations list, this is guaranteed 307 * to only really happen once. 308 */ 309 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); 310 311 /* lock the dlz_implementations list so we can modify it. */ 312 RWLOCK(&dlz_implock, isc_rwlocktype_write); 313 314 /* 315 * check that another already registered driver isn't using 316 * the same name 317 */ 318 dlz_imp = dlz_impfind(drivername); 319 if (dlz_imp != NULL) { 320 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 321 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 322 "DLZ Driver '%s' already registered", 323 drivername); 324 RWUNLOCK(&dlz_implock, isc_rwlocktype_write); 325 return (ISC_R_EXISTS); 326 } 327 328 /* 329 * Allocate memory for a dlz_implementation object. Error if 330 * we cannot. 331 */ 332 dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t)); 333 if (dlz_imp == NULL) { 334 RWUNLOCK(&dlz_implock, isc_rwlocktype_write); 335 return (ISC_R_NOMEMORY); 336 } 337 338 /* Make sure memory region is set to all 0's */ 339 memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t)); 340 341 /* Store the data passed into this method */ 342 dlz_imp->name = drivername; 343 dlz_imp->methods = methods; 344 dlz_imp->mctx = NULL; 345 dlz_imp->driverarg = driverarg; 346 347 /* attach the new dlz_implementation object to a memory context */ 348 isc_mem_attach(mctx, &dlz_imp->mctx); 349 350 /* 351 * prepare the dlz_implementation object to be put in a list, 352 * and append it to the list 353 */ 354 ISC_LINK_INIT(dlz_imp, link); 355 ISC_LIST_APPEND(dlz_implementations, dlz_imp, link); 356 357 /* Unlock the dlz_implementations list. */ 358 RWUNLOCK(&dlz_implock, isc_rwlocktype_write); 359 360 /* Pass back the dlz_implementation that we created. */ 361 *dlzimp = dlz_imp; 362 363 return (ISC_R_SUCCESS); 364 } 365 366 /*% 367 * Helper function for dns_dlzstrtoargv(). 368 * Pardon the gratuitous recursion. 369 */ 370 static isc_result_t 371 dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp, 372 char ***argvp, unsigned int n) 373 { 374 isc_result_t result; 375 376 restart: 377 /* Discard leading whitespace. */ 378 while (*s == ' ' || *s == '\t') 379 s++; 380 381 if (*s == '\0') { 382 /* We have reached the end of the string. */ 383 *argcp = n; 384 *argvp = isc_mem_get(mctx, n * sizeof(char *)); 385 if (*argvp == NULL) 386 return (ISC_R_NOMEMORY); 387 } else { 388 char *p = s; 389 while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') { 390 if (*p == '\n') { 391 *p = ' '; 392 goto restart; 393 } 394 p++; 395 } 396 397 /* do "grouping", items between { and } are one arg */ 398 if (*p == '{') { 399 char *t = p; 400 /* 401 * shift all characters to left by 1 to get rid of '{' 402 */ 403 while (*t != '\0') { 404 t++; 405 *(t-1) = *t; 406 } 407 while (*p != '\0' && *p != '}') { 408 p++; 409 } 410 /* get rid of '}' character */ 411 if (*p == '}') { 412 *p = '\0'; 413 p++; 414 } 415 /* normal case, no "grouping" */ 416 } else if (*p != '\0') 417 *p++ = '\0'; 418 419 result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1); 420 if (result != ISC_R_SUCCESS) 421 return (result); 422 (*argvp)[n] = s; 423 } 424 return (ISC_R_SUCCESS); 425 } 426 427 /*% 428 * Tokenize the string "s" into whitespace-separated words, 429 * return the number of words in '*argcp' and an array 430 * of pointers to the words in '*argvp'. The caller 431 * must free the array using isc_mem_put(). The string 432 * is modified in-place. 433 */ 434 isc_result_t 435 dns_dlzstrtoargv(isc_mem_t *mctx, char *s, 436 unsigned int *argcp, char ***argvp) 437 { 438 return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0)); 439 } 440 441 /*% 442 * Unregisters a DLZ driver. This basically just removes the dlz 443 * driver from the list of available drivers in the dlz_implementations list. 444 */ 445 void 446 dns_dlzunregister(dns_dlzimplementation_t **dlzimp) { 447 dns_dlzimplementation_t *dlz_imp; 448 isc_mem_t *mctx; 449 450 /* Write debugging message to log */ 451 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 452 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 453 "Unregistering DLZ driver."); 454 455 /* 456 * Performs checks to make sure data is as we expect it to be. 457 */ 458 REQUIRE(dlzimp != NULL && *dlzimp != NULL); 459 460 /* 461 * initialize the dlz_implementations list, this is guaranteed 462 * to only really happen once. 463 */ 464 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); 465 466 dlz_imp = *dlzimp; 467 468 /* lock the dlz_implementations list so we can modify it. */ 469 RWLOCK(&dlz_implock, isc_rwlocktype_write); 470 471 /* remove the dlz_implementation object from the list */ 472 ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link); 473 mctx = dlz_imp->mctx; 474 475 /* 476 * Return the memory back to the available memory pool and 477 * remove it from the memory context. 478 */ 479 isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t)); 480 isc_mem_detach(&mctx); 481 482 /* Unlock the dlz_implementations list. */ 483 RWUNLOCK(&dlz_implock, isc_rwlocktype_write); 484 } 485 486 /* 487 * Create a writeable DLZ zone. This can be called by DLZ drivers 488 * during configure() to create a zone that can be updated. The zone 489 * type is set to dns_zone_dlz, which is equivalent to a master zone 490 * 491 * This function uses a callback setup in dns_dlzconfigure() to call 492 * into the server zone code to setup the remaining pieces of server 493 * specific functionality on the zone 494 */ 495 isc_result_t 496 dns_dlz_writeablezone(dns_view_t *view, dns_dlzdb_t *dlzdb, 497 const char *zone_name) 498 { 499 dns_zone_t *zone = NULL; 500 dns_zone_t *dupzone = NULL; 501 isc_result_t result; 502 isc_buffer_t buffer; 503 dns_fixedname_t fixorigin; 504 dns_name_t *origin; 505 506 REQUIRE(DNS_DLZ_VALID(dlzdb)); 507 508 REQUIRE(dlzdb->configure_callback != NULL); 509 510 isc_buffer_constinit(&buffer, zone_name, strlen(zone_name)); 511 isc_buffer_add(&buffer, strlen(zone_name)); 512 dns_fixedname_init(&fixorigin); 513 result = dns_name_fromtext(dns_fixedname_name(&fixorigin), 514 &buffer, dns_rootname, 0, NULL); 515 if (result != ISC_R_SUCCESS) 516 goto cleanup; 517 origin = dns_fixedname_name(&fixorigin); 518 519 if (!dlzdb->search) { 520 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 521 DNS_LOGMODULE_DLZ, ISC_LOG_WARNING, 522 "DLZ %s has 'search no;', but attempted to " 523 "register writeable zone %s.", 524 dlzdb->dlzname, zone_name); 525 result = ISC_R_SUCCESS; 526 goto cleanup; 527 } 528 529 /* See if the zone already exists */ 530 result = dns_view_findzone(view, origin, &dupzone); 531 if (result == ISC_R_SUCCESS) { 532 dns_zone_detach(&dupzone); 533 result = ISC_R_EXISTS; 534 goto cleanup; 535 } 536 INSIST(dupzone == NULL); 537 538 /* Create it */ 539 result = dns_zone_create(&zone, view->mctx); 540 if (result != ISC_R_SUCCESS) 541 goto cleanup; 542 result = dns_zone_setorigin(zone, origin); 543 if (result != ISC_R_SUCCESS) 544 goto cleanup; 545 dns_zone_setview(zone, view); 546 547 dns_zone_setadded(zone, ISC_TRUE); 548 549 if (dlzdb->ssutable == NULL) { 550 result = dns_ssutable_createdlz(dlzdb->mctx, 551 &dlzdb->ssutable, dlzdb); 552 if (result != ISC_R_SUCCESS) 553 goto cleanup; 554 } 555 dns_zone_setssutable(zone, dlzdb->ssutable); 556 557 result = dlzdb->configure_callback(view, dlzdb, zone); 558 if (result != ISC_R_SUCCESS) 559 goto cleanup; 560 561 result = dns_view_addzone(view, zone); 562 563 564 cleanup: 565 if (zone != NULL) 566 dns_zone_detach(&zone); 567 568 return (result); 569 } 570 571 /*% 572 * Configure a DLZ driver. This is optional, and if supplied gives 573 * the backend an opportunity to configure parameters related to DLZ. 574 */ 575 isc_result_t 576 dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb, 577 dlzconfigure_callback_t callback) 578 { 579 dns_dlzimplementation_t *impl; 580 isc_result_t result; 581 582 REQUIRE(DNS_DLZ_VALID(dlzdb)); 583 REQUIRE(dlzdb->implementation != NULL); 584 585 impl = dlzdb->implementation; 586 587 if (impl->methods->configure == NULL) 588 return (ISC_R_SUCCESS); 589 590 dlzdb->configure_callback = callback; 591 592 result = impl->methods->configure(impl->driverarg, dlzdb->dbdata, 593 view, dlzdb); 594 return (result); 595 } 596 597 isc_boolean_t 598 dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase, dns_name_t *signer, 599 dns_name_t *name, isc_netaddr_t *tcpaddr, 600 dns_rdatatype_t type, const dst_key_t *key) 601 { 602 dns_dlzimplementation_t *impl; 603 isc_boolean_t r; 604 605 REQUIRE(dlzdatabase != NULL); 606 REQUIRE(dlzdatabase->implementation != NULL); 607 REQUIRE(dlzdatabase->implementation->methods != NULL); 608 impl = dlzdatabase->implementation; 609 610 if (impl->methods->ssumatch == NULL) { 611 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 612 DNS_LOGMODULE_DLZ, ISC_LOG_INFO, 613 "No ssumatch method for DLZ database"); 614 return (ISC_FALSE); 615 } 616 617 r = impl->methods->ssumatch(signer, name, tcpaddr, type, key, 618 impl->driverarg, dlzdatabase->dbdata); 619 return (r); 620 } 621