1/* 2 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET 10 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 11 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 12 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 14 * 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 16 * USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was 19 * conceived and contributed by Rob Butler. 20 * 21 * Permission to use, copy, modify, and distribute this software for any 22 * purpose with or without fee is hereby granted, provided that the 23 * above copyright notice and this permission notice appear in all 24 * copies. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER 27 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 29 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 30 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 31 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 32 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 33 * USE OR PERFORMANCE OF THIS SOFTWARE. 34 */ 35 36/* 37 * Copyright (C) 1999-2001 Internet Software Consortium. 38 * 39 * Permission to use, copy, modify, and distribute this software for any 40 * purpose with or without fee is hereby granted, provided that the above 41 * copyright notice and this permission notice appear in all copies. 42 * 43 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM 44 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 46 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 47 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 48 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 49 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 50 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 51 */ 52 53#ifdef DLZ_FILESYSTEM 54 55#include <config.h> 56#include <stdio.h> 57#include <string.h> 58#include <stdlib.h> 59 60#include <sys/stat.h> 61 62#include <dns/log.h> 63#include <dns/sdlz.h> 64#include <dns/result.h> 65 66#include <isc/dir.h> 67#include <isc/mem.h> 68#include <isc/platform.h> 69#include <isc/print.h> 70#include <isc/result.h> 71#include <isc/util.h> 72 73#include <named/globals.h> 74 75#include <dlz/dlz_filesystem_driver.h> 76 77static dns_sdlzimplementation_t *dlz_fs = NULL; 78 79typedef struct config_data { 80 char *basedir; 81 int basedirsize; 82 char *datadir; 83 int datadirsize; 84 char *xfrdir; 85 int xfrdirsize; 86 int splitcnt; 87 char separator; 88 char pathsep; 89 isc_mem_t *mctx; 90} config_data_t; 91 92typedef struct dir_entry dir_entry_t; 93 94struct dir_entry { 95 char dirpath[ISC_DIR_PATHMAX]; 96 ISC_LINK(dir_entry_t) link; 97}; 98 99typedef ISC_LIST(dir_entry_t) dlist_t; 100 101/* forward reference */ 102 103static void 104fs_destroy(void *driverarg, void *dbdata); 105 106/* 107 * Private methods 108 */ 109 110static isc_boolean_t 111is_safe(const char *input) 112{ 113 unsigned int i; 114 unsigned int len = strlen(input); 115 116 /* check that only allowed characters are in the domain name */ 117 for (i=0; i < len; i++) { 118 /* '.' is allowed, but has special requirements */ 119 if (input[i] == '.') { 120 /* '.' is not allowed as first char */ 121 if (i == 0) 122 return ISC_FALSE; 123 /* '..', two dots together is not allowed. */ 124 else if (input[i-1] == '.') 125 return ISC_FALSE; 126 /* '.' is not allowed as last char */ 127 if (i == len) 128 return ISC_FALSE; 129 /* only 1 dot in ok location, continue at next char */ 130 continue; 131 } 132 /* '-' is allowed, continue at next char */ 133 if (input[i] == '-') 134 continue; 135 /* 0-9 is allowed, continue at next char */ 136 if (input[i] >= '0' && input[i] <= '9') 137 continue; 138 /* A-Z uppercase is allowed, continue at next char */ 139 if (input[i] >= 'A' && input[i] <= 'Z') 140 continue; 141 /* a-z lowercase is allowed, continue at next char */ 142 if (input[i] >= 'a' && input[i] <= 'z') 143 continue; 144 145 /* 146 * colon needs to be allowed for IPV6 client 147 * addresses. Not dangerous in domain names, as not a 148 * special char. 149 */ 150 if (input[i] == ':') 151 continue; 152 153 /* 154 * '@' needs to be allowed for in zone data. Not 155 * dangerous in domain names, as not a special char. 156 */ 157 if (input[i] == '@') 158 continue; 159 160 /* 161 * if we reach this point we have encountered a 162 * disallowed char! 163 */ 164 return ISC_FALSE; 165 } 166 /* everything ok. */ 167 return ISC_TRUE; 168} 169 170static isc_result_t 171create_path_helper(char *out, const char *in, config_data_t *cd) 172{ 173 174 char *tmpString; 175 char *tmpPtr; 176 int i; 177 178 tmpString = isc_mem_strdup(ns_g_mctx, in); 179 if (tmpString == NULL) 180 return (ISC_R_NOMEMORY); 181 182 /* 183 * don't forget is_safe guarantees '.' will NOT be the 184 * first/last char 185 */ 186 while ((tmpPtr = strrchr(tmpString, '.')) != NULL) { 187 i = 0; 188 while (tmpPtr[i+1] != '\0') { 189 if (cd->splitcnt < 1) 190 strcat(out, (char *) &tmpPtr[i+1]); 191 else 192 strncat(out, (char *) &tmpPtr[i+1], 193 cd->splitcnt); 194 strncat(out, (char *) &cd->pathsep, 1); 195 if (cd->splitcnt == 0) 196 break; 197 if (strlen((char *) &tmpPtr[i+1]) <= 198 (unsigned int) cd->splitcnt) 199 break; 200 i += cd->splitcnt; 201 } 202 tmpPtr[0] = '\0'; 203 } 204 205 /* handle the "first" label properly */ 206 i=0; 207 tmpPtr = tmpString; 208 while (tmpPtr[i] != '\0') { 209 if (cd->splitcnt < 1) 210 strcat(out, (char *) &tmpPtr[i]); 211 else 212 strncat(out, (char *) &tmpPtr[i], cd->splitcnt); 213 strncat(out, (char *) &cd->pathsep, 1); 214 if (cd->splitcnt == 0) 215 break; 216 if (strlen((char *) &tmpPtr[i]) <= 217 (unsigned int) cd->splitcnt) 218 break; 219 i += cd->splitcnt; 220 } 221 222 isc_mem_free(ns_g_mctx, tmpString); 223 return (ISC_R_SUCCESS); 224} 225 226/*% 227 * Checks to make sure zone and host are safe. If safe, then 228 * hashes zone and host strings to build a path. If zone / host 229 * are not safe an error is returned. 230 */ 231 232static isc_result_t 233create_path(const char *zone, const char *host, const char *client, 234 config_data_t *cd, char **path) 235{ 236 237 char *tmpPath; 238 int pathsize; 239 int len; 240 isc_result_t result; 241 242 /* we require a zone & cd parameter */ 243 REQUIRE(zone != NULL); 244 REQUIRE(cd != NULL); 245 /* require path to be a pointer to NULL */ 246 REQUIRE(path != NULL && *path == NULL); 247 /* 248 * client and host may both be NULL, but they can't both be 249 * NON-NULL 250 */ 251 REQUIRE( (host == NULL && client == NULL) || 252 (host != NULL && client == NULL) || 253 (host == NULL && client != NULL) ); 254 255 /* if the requested zone is "unsafe", return error */ 256 if (is_safe(zone) != ISC_TRUE) 257 return (ISC_R_FAILURE); 258 259 /* if host was passed, verify that it is safe */ 260 if ((host != NULL) && (is_safe(host) != ISC_TRUE) ) 261 return (ISC_R_FAILURE); 262 263 /* if client was passed, verify that it is safe */ 264 if ((client != NULL) && (is_safe(client) != ISC_TRUE) ) 265 return (ISC_R_FAILURE); 266 267 /* Determine how much memory the split up string will require */ 268 if (host != NULL) 269 len = strlen(zone) + strlen(host); 270 else if (client != NULL) 271 len = strlen(zone) + strlen(client); 272 else 273 len = strlen(zone); 274 275 /* 276 * even though datadir and xfrdir will never be in the same 277 * string we only waste a few bytes by allocating for both, 278 * and then we are safe from buffer overruns. 279 */ 280 pathsize = len + cd->basedirsize + 281 cd->datadirsize + cd->xfrdirsize + 4; 282 283 /* if we are splitting names, we will need extra space. */ 284 if (cd->splitcnt > 0) 285 pathsize += len/cd->splitcnt; 286 287 tmpPath = isc_mem_allocate(ns_g_mctx , pathsize * sizeof(char)); 288 if (tmpPath == NULL) { 289 /* write error message */ 290 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 291 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 292 "Filesystem driver unable to " 293 "allocate memory in create_path()."); 294 result = ISC_R_NOMEMORY; 295 goto cleanup_mem; 296 } 297 298 /* 299 * build path string. 300 * start out with base directory. 301 */ 302 strcpy(tmpPath, cd->basedir); 303 304 /* add zone name - parsed properly */ 305 if ((result = create_path_helper(tmpPath, zone, cd)) != ISC_R_SUCCESS) 306 goto cleanup_mem; 307 308 /* 309 * When neither client or host is passed we are building a 310 * path to see if a zone is supported. We require that a zone 311 * path have the "data dir" directory contained within it so 312 * that we know this zone is really supported. Otherwise, 313 * this zone may not really be supported because we are 314 * supporting a delagated sub zone. 315 * 316 * Example: 317 * 318 * We are supporting long.domain.com and using a splitcnt of 319 * 0. the base dir is "/base-dir/" and the data dir is 320 * "/.datadir" We want to see if we are authoritative for 321 * domain.com. Path /base-dir/com/domain/.datadir since 322 * /base-dir/com/domain/.datadir does not exist, we are not 323 * authoritative for the domain "domain.com". However we are 324 * authoritative for the domain "long.domain.com" because the 325 * path /base-dir/com/domain/long/.datadir does exist! 326 */ 327 328 /* if client is passed append xfr dir, otherwise append data dir */ 329 if (client != NULL) { 330 strcat(tmpPath, cd->xfrdir); 331 strncat(tmpPath, (char *) &cd->pathsep, 1); 332 strcat(tmpPath, client); 333 } else { 334 strcat(tmpPath, cd->datadir); 335 } 336 337 /* if host not null, add it. */ 338 if (host != NULL) { 339 strncat(tmpPath, (char *) &cd->pathsep, 1); 340 if ((result = create_path_helper(tmpPath, host, 341 cd)) != ISC_R_SUCCESS) 342 goto cleanup_mem; 343 } 344 345 /* return the path we built. */ 346 *path = tmpPath; 347 348 /* return success */ 349 result = ISC_R_SUCCESS; 350 351 cleanup_mem: 352 /* cleanup memory */ 353 354 /* free tmpPath memory */ 355 if (tmpPath != NULL && result != ISC_R_SUCCESS) 356 isc_mem_free(ns_g_mctx, tmpPath); 357 358 /* free tmpPath memory */ 359 return result; 360} 361 362static isc_result_t 363process_dir(isc_dir_t dir, void *passback, config_data_t *cd, 364 dlist_t *dir_list, unsigned int basedirlen) 365{ 366 367 char tmp[ISC_DIR_PATHMAX + ISC_DIR_NAMEMAX]; 368 int astPos; 369 struct stat sb; 370 isc_result_t result = ISC_R_FAILURE; 371 char *endp; 372 char *type; 373 char *ttlStr; 374 char *data; 375 char host[ISC_DIR_NAMEMAX]; 376 char *tmpString; 377 char *tmpPtr; 378 int ttl; 379 int i; 380 int len; 381 dir_entry_t *direntry; 382 isc_boolean_t foundHost; 383 384 tmp[0] = '\0'; /* set 1st byte to '\0' so strcpy works right. */ 385 host[0] = '\0'; 386 foundHost = ISC_FALSE; 387 388 /* copy base directory name to tmp. */ 389 strcpy(tmp, dir.dirname); 390 391 /* dir.dirname will always have '*' as the last char. */ 392 astPos = strlen(dir.dirname) - 1; 393 394 /* if dir_list != NULL, were are performing a zone xfr */ 395 if (dir_list != NULL) { 396 /* if splitcnt == 0, determine host from path. */ 397 if (cd->splitcnt == 0) { 398 if (strlen(tmp) - 3 > basedirlen) { 399 tmp[astPos-1] = '\0'; 400 tmpString = (char *) &tmp[basedirlen+1]; 401 /* handle filesystem's special wildcard "-" */ 402 if (strcmp(tmpString, "-") == 0) { 403 strcpy(host, "*"); 404 } else { 405 /* 406 * not special wildcard -- normal name 407 */ 408 while ((tmpPtr = strrchr(tmpString, 409 cd->pathsep)) 410 != NULL) { 411 strcat(host, tmpPtr + 1); 412 strcat(host, "."); 413 tmpPtr[0] = '\0'; 414 } 415 strcat(host, tmpString); 416 } 417 418 foundHost = ISC_TRUE; 419 /* set tmp again for use later */ 420 strcpy(tmp, dir.dirname); 421 } 422 } else { 423 /* 424 * if splitcnt != 0 determine host from 425 * ".host" directory entry 426 */ 427 while (isc_dir_read(&dir) == ISC_R_SUCCESS) { 428 if (strncasecmp(".host", 429 dir.entry.name, 5) == 0) { 430 /* 431 * handle filesystem's special 432 * wildcard "-" 433 */ 434 if (strcmp((char *) &dir.entry.name[6], 435 "-") == 0) 436 strcpy(host, "*"); 437 else 438 strcpy(host, 439 (char *) 440 &dir.entry.name[6]); 441 foundHost = ISC_TRUE; 442 break; 443 } 444 } 445 /* reset dir list for use later */ 446 isc_dir_reset(&dir); 447 } /* end of else */ 448 } 449 450 while (isc_dir_read(&dir) == ISC_R_SUCCESS) { 451 452 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 453 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 454 "Filesystem driver Dir name:" 455 " '%s' Dir entry: '%s'\n", 456 dir.dirname, dir.entry.name); 457 458 /* skip any entries starting with "." */ 459 if (dir.entry.name[0] == '.') 460 continue; 461 462 /* 463 * get rid of '*', set to NULL. Effectively trims 464 * string from previous loop to base directory only 465 * while still leaving memory for concat to be 466 * performed next. 467 */ 468 469 tmp[astPos] = '\0'; 470 471 /* add name to base directory name. */ 472 strcat(tmp, dir.entry.name); 473 474 /* make sure we can stat entry */ 475 if (stat(tmp, &sb) == 0 ) { 476 /* if entry is a directory */ 477 if ((sb.st_mode & S_IFDIR) != 0) { 478 /* 479 * if dir list is NOT NULL, add dir to 480 * dir list 481 */ 482 if (dir_list != NULL) { 483 direntry = 484 isc_mem_get(ns_g_mctx, 485 sizeof(dir_entry_t)); 486 if (direntry == NULL) 487 return (ISC_R_NOMEMORY); 488 strcpy(direntry->dirpath, tmp); 489 ISC_LINK_INIT(direntry, link); 490 ISC_LIST_APPEND(*dir_list, direntry, 491 link); 492 result = ISC_R_SUCCESS; 493 } 494 continue; 495 496 /* 497 * if entry is a file be sure we do 498 * not add entry to DNS results if we 499 * are performing a zone xfr and we 500 * could not find a host entry. 501 */ 502 503 } else if (dir_list != NULL && 504 foundHost == ISC_FALSE) { 505 continue; 506 } 507 } else /* if we cannot stat entry, skip it. */ 508 continue; 509 510 type = dir.entry.name; 511 ttlStr = strchr(type, cd->separator); 512 if (ttlStr == NULL) { 513 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 514 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 515 "Filesystem driver: " 516 "%s could not be parsed properly", 517 tmp); 518 return ISC_R_FAILURE; 519 } 520 521 /* replace separator char with NULL to split string */ 522 ttlStr[0] = '\0'; 523 /* start string after NULL of previous string */ 524 ttlStr = (char *) &ttlStr[1]; 525 526 data = strchr(ttlStr, cd->separator); 527 if (data == NULL) { 528 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 529 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 530 "Filesystem driver: " 531 "%s could not be parsed properly", 532 tmp); 533 return ISC_R_FAILURE; 534 } 535 536 /* replace separator char with NULL to split string */ 537 data[0] = '\0'; 538 539 /* start string after NULL of previous string */ 540 data = (char *) &data[1]; 541 542 /* replace all cd->separator chars with a space. */ 543 len = strlen(data); 544 545 for (i=0; i < len; i++) { 546 if (data[i] == cd->separator) 547 data[i] = ' '; 548 } 549 550 /* convert text to int, make sure it worked right */ 551 ttl = strtol(ttlStr, &endp, 10); 552 if (*endp != '\0' || ttl < 0) { 553 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 554 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 555 "Filesystem driver " 556 "ttl must be a postive number"); 557 } 558 559 /* pass data back to Bind */ 560 if (dir_list == NULL) 561 result = dns_sdlz_putrr((dns_sdlzlookup_t *) passback, 562 type, ttl, data); 563 else 564 result = dns_sdlz_putnamedrr((dns_sdlzallnodes_t *) 565 passback, 566 (char *) host, 567 type, ttl, data); 568 569 /* if error, return error right away */ 570 if (result != ISC_R_SUCCESS) 571 return result; 572 } /* end of while loop */ 573 574 return result; 575} 576 577/* 578 * SDLZ interface methods 579 */ 580 581static isc_result_t 582fs_allowzonexfr(void *driverarg, void *dbdata, const char *name, 583 const char *client) 584{ 585 586 isc_result_t result; 587 char *path; 588 struct stat sb; 589 config_data_t *cd; 590 path = NULL; 591 592 UNUSED(driverarg); 593 594 cd = (config_data_t *) dbdata; 595 596 if (create_path(name, NULL, client, cd, &path) != ISC_R_SUCCESS) { 597 return (ISC_R_NOTFOUND); 598 } 599 600 if (stat(path, &sb) != 0) { 601 result = ISC_R_NOTFOUND; 602 goto complete_AXFR; 603 } 604 605 if ((sb.st_mode & S_IFREG) != 0) { 606 result = ISC_R_SUCCESS; 607 goto complete_AXFR; 608 } 609 610 result = ISC_R_NOTFOUND; 611 612 complete_AXFR: 613 isc_mem_free(ns_g_mctx, path); 614 return result; 615} 616 617static isc_result_t 618fs_allnodes(const char *zone, void *driverarg, void *dbdata, 619 dns_sdlzallnodes_t *allnodes) 620{ 621 622 isc_result_t result; 623 dlist_t *dir_list; 624 config_data_t *cd; 625 char *basepath; 626 unsigned int basepathlen; 627 struct stat sb; 628 isc_dir_t dir; 629 dir_entry_t *dir_entry; 630 dir_entry_t *next_de; 631 632 basepath = NULL; 633 dir_list = NULL; 634 635 UNUSED(driverarg); 636 UNUSED(allnodes); 637 638 cd = (config_data_t *) dbdata; 639 640 /* allocate memory for list */ 641 dir_list = isc_mem_get(ns_g_mctx, sizeof(dlist_t)); 642 if (dir_list == NULL) { 643 result = ISC_R_NOTFOUND; 644 goto complete_allnds; 645 } 646 647 /* initialize list */ 648 ISC_LIST_INIT(*dir_list); 649 650 if (create_path(zone, NULL, NULL, cd, &basepath) != ISC_R_SUCCESS) { 651 return (ISC_R_NOTFOUND); 652 } 653 654 /* remove path separator at end of path so stat works properly */ 655 basepathlen = strlen(basepath); 656 657 if (stat(basepath, &sb) != 0) { 658 result = ISC_R_NOTFOUND; 659 goto complete_allnds; 660 } 661 662 if ((sb.st_mode & S_IFDIR) == 0) { 663 result = ISC_R_NOTFOUND; 664 goto complete_allnds; 665 } 666 667 /* initialize and open directory */ 668 isc_dir_init(&dir); 669 result = isc_dir_open(&dir, basepath); 670 671 /* if directory open failed, return error. */ 672 if (result != ISC_R_SUCCESS) { 673 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 674 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 675 "Unable to open %s directory to read entries.", 676 basepath); 677 result = ISC_R_FAILURE; 678 goto complete_allnds; 679 } 680 681 /* process the directory */ 682 result = process_dir(dir, allnodes, cd, dir_list, basepathlen); 683 684 /* close the directory */ 685 isc_dir_close(&dir); 686 687 if (result != ISC_R_SUCCESS) 688 goto complete_allnds; 689 690 /* get first dir entry from list. */ 691 dir_entry = ISC_LIST_HEAD(*dir_list); 692 while (dir_entry != NULL) { 693 694 result = isc_dir_open(&dir, dir_entry->dirpath); 695 /* if directory open failed, return error. */ 696 if (result != ISC_R_SUCCESS) { 697 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 698 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 699 "Unable to open %s " 700 "directory to read entries.", 701 basepath); 702 result = ISC_R_FAILURE; 703 goto complete_allnds; 704 } 705 706 /* process the directory */ 707 result = process_dir(dir, allnodes, cd, dir_list, basepathlen); 708 709 /* close the directory */ 710 isc_dir_close(&dir); 711 712 if (result != ISC_R_SUCCESS) 713 goto complete_allnds; 714 715 dir_entry = ISC_LIST_NEXT(dir_entry, link); 716 } /* end while */ 717 718 complete_allnds: 719 if (dir_list != NULL) { 720 /* clean up entries from list. */ 721 dir_entry = ISC_LIST_HEAD(*dir_list); 722 while (dir_entry != NULL) { 723 next_de = ISC_LIST_NEXT(dir_entry, link); 724 isc_mem_put(ns_g_mctx, dir_entry, sizeof(dir_entry_t)); 725 dir_entry = next_de; 726 } /* end while */ 727 isc_mem_put(ns_g_mctx, dir_list, sizeof(dlist_t)); 728 } 729 730 if (basepath != NULL) 731 isc_mem_free(ns_g_mctx, basepath); 732 733 return result; 734} 735 736static isc_result_t 737fs_findzone(void *driverarg, void *dbdata, const char *name) 738{ 739 740 isc_result_t result; 741 char *path; 742 struct stat sb; 743 path = NULL; 744 745 UNUSED(driverarg); 746 747 if (create_path(name, NULL, NULL, (config_data_t *) dbdata, 748 &path) != ISC_R_SUCCESS) { 749 return (ISC_R_NOTFOUND); 750 } 751 752 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 753 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 754 "Filesystem driver Findzone() Checking for path: '%s'\n", 755 path); 756 757 if (stat(path, &sb) != 0) { 758 result = ISC_R_NOTFOUND; 759 goto complete_FZ; 760 } 761 762 if ((sb.st_mode & S_IFDIR) != 0) { 763 result = ISC_R_SUCCESS; 764 goto complete_FZ; 765 } 766 767 result = ISC_R_NOTFOUND; 768 769 complete_FZ: 770 771 isc_mem_free(ns_g_mctx, path); 772 return result; 773} 774 775static isc_result_t 776fs_lookup(const char *zone, const char *name, void *driverarg, 777 void *dbdata, dns_sdlzlookup_t *lookup) 778{ 779 isc_result_t result; 780 char *path; 781 struct stat sb; 782 isc_dir_t dir; 783 path = NULL; 784 785 UNUSED(driverarg); 786 UNUSED(lookup); 787 788 if (strcmp(name, "*") == 0) 789 /* 790 * handle filesystem's special wildcard "-" 791 */ 792 result = create_path(zone, "-", NULL, 793 (config_data_t *) dbdata, &path); 794 else 795 result = create_path(zone, name, NULL, 796 (config_data_t *) dbdata, &path); 797 798 if ( result != ISC_R_SUCCESS) { 799 return (ISC_R_NOTFOUND); 800 } 801 802 /* remove path separator at end of path so stat works properly */ 803 path[strlen(path)-1] = '\0'; 804 805 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 806 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 807 "Filesystem driver lookup() Checking for path: '%s'\n", 808 path); 809 810 811 if (stat(path, &sb) != 0) { 812 result = ISC_R_NOTFOUND; 813 goto complete_lkup; 814 } 815 816 if ((sb.st_mode & S_IFDIR) == 0) { 817 result = ISC_R_NOTFOUND; 818 goto complete_lkup; 819 } 820 821 /* initialize and open directory */ 822 isc_dir_init(&dir); 823 result = isc_dir_open(&dir, path); 824 825 /* if directory open failed, return error. */ 826 if (result != ISC_R_SUCCESS) { 827 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 828 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 829 "Unable to open %s directory to read entries.", 830 path); 831 result = ISC_R_FAILURE; 832 goto complete_lkup; 833 } 834 835 /* process any records in the directory */ 836 result = process_dir(dir, lookup, (config_data_t *) dbdata, NULL, 0); 837 838 /* close the directory */ 839 isc_dir_close(&dir); 840 841 complete_lkup: 842 843 isc_mem_free(ns_g_mctx, path); 844 return result; 845} 846 847static isc_result_t 848fs_create(const char *dlzname, unsigned int argc, char *argv[], 849 void *driverarg, void **dbdata) 850{ 851 config_data_t *cd; 852 char *endp; 853 int len; 854 char pathsep; 855 856 UNUSED(driverarg); 857 UNUSED(dlzname); 858 859 /* we require 5 command line args. */ 860 if (argc != 6) { 861 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 862 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 863 "Filesystem driver requires " 864 "6 command line args."); 865 return (ISC_R_FAILURE); 866 } 867 868 if (strlen(argv[5]) > 1) { 869 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 870 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 871 "Filesystem driver can only " 872 "accept a single character for separator."); 873 return (ISC_R_FAILURE); 874 } 875 876 /* verify base dir ends with '/' or '\' */ 877 len = strlen(argv[1]); 878 if (argv[1][len-1] != '\\' && argv[1][len-1] != '/') { 879 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 880 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 881 "Base dir parameter for filesystem driver " 882 "should end with %s", 883 "either '/' or '\\' "); 884 return (ISC_R_FAILURE); 885 } 886 887 /* determine and save path separator for later */ 888 if (argv[1][len-1] == '\\') 889 pathsep = '\\'; 890 else 891 pathsep = '/'; 892 893 /* allocate memory for our config data */ 894 cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t)); 895 if (cd == NULL) 896 goto no_mem; 897 898 /* zero the memory */ 899 memset(cd, 0, sizeof(config_data_t)); 900 901 cd->pathsep = pathsep; 902 903 /* get and store our base directory */ 904 cd->basedir = isc_mem_strdup(ns_g_mctx, argv[1]); 905 if (cd->basedir == NULL) 906 goto no_mem; 907 cd->basedirsize = strlen(cd->basedir); 908 909 /* get and store our data sub-dir */ 910 cd->datadir = isc_mem_strdup(ns_g_mctx, argv[2]); 911 if (cd->datadir == NULL) 912 goto no_mem; 913 cd->datadirsize = strlen(cd->datadir); 914 915 /* get and store our zone xfr sub-dir */ 916 cd->xfrdir = isc_mem_strdup(ns_g_mctx, argv[3]); 917 if (cd->xfrdir == NULL) 918 goto no_mem; 919 cd->xfrdirsize = strlen(cd->xfrdir); 920 921 /* get and store our directory split count */ 922 cd->splitcnt = strtol(argv[4], &endp, 10); 923 if (*endp != '\0' || cd->splitcnt < 0) { 924 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 925 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 926 "Directory split count must be zero (0) " 927 "or a postive number"); 928 } 929 930 /* get and store our separator character */ 931 cd->separator = *argv[5]; 932 933 /* attach config data to memory context */ 934 isc_mem_attach(ns_g_mctx, &cd->mctx); 935 936 /* pass back config data */ 937 *dbdata = cd; 938 939 /* return success */ 940 return(ISC_R_SUCCESS); 941 942 /* handle no memory error */ 943 no_mem: 944 945 /* if we allocated a config data object clean it up */ 946 if (cd != NULL) 947 fs_destroy(NULL, cd); 948 949 /* write error message */ 950 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 951 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 952 "Filesystem driver unable to " 953 "allocate memory for config data."); 954 955 /* return error */ 956 return (ISC_R_NOMEMORY); 957} 958 959static void 960fs_destroy(void *driverarg, void *dbdata) 961{ 962 isc_mem_t *mctx; 963 config_data_t *cd; 964 965 UNUSED(driverarg); 966 967 cd = (config_data_t *) dbdata; 968 969 /* 970 * free memory for each section of config data that was 971 * allocated 972 */ 973 if (cd->basedir != NULL) 974 isc_mem_free(ns_g_mctx, cd->basedir); 975 976 if (cd->datadir != NULL) 977 isc_mem_free(ns_g_mctx, cd->datadir); 978 979 if (cd->xfrdir != NULL) 980 isc_mem_free(ns_g_mctx, cd->xfrdir); 981 982 /* hold memory context to use later */ 983 mctx = cd->mctx; 984 985 /* free config data memory */ 986 isc_mem_put(mctx, cd, sizeof(config_data_t)); 987 988 /* detach memory from context */ 989 isc_mem_detach(&mctx); 990} 991 992static dns_sdlzmethods_t dlz_fs_methods = { 993 fs_create, 994 fs_destroy, 995 fs_findzone, 996 fs_lookup, 997 NULL, 998 fs_allnodes, 999 fs_allowzonexfr, 1000 NULL, 1001 NULL, 1002 NULL, 1003 NULL, 1004 NULL, 1005 NULL, 1006 NULL, 1007}; 1008 1009/*% 1010 * Wrapper around dns_sdlzregister(). 1011 */ 1012isc_result_t 1013dlz_fs_init(void) 1014{ 1015 isc_result_t result; 1016 1017 /* 1018 * Write debugging message to log 1019 */ 1020 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1021 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 1022 "Registering DLZ filesystem driver."); 1023 1024 result = dns_sdlzregister("filesystem", &dlz_fs_methods, NULL, 1025 DNS_SDLZFLAG_RELATIVEOWNER | 1026 DNS_SDLZFLAG_RELATIVERDATA, 1027 ns_g_mctx, &dlz_fs); 1028 if (result != ISC_R_SUCCESS) { 1029 UNEXPECTED_ERROR(__FILE__, __LINE__, 1030 "dns_sdlzregister() failed: %s", 1031 isc_result_totext(result)); 1032 result = ISC_R_UNEXPECTED; 1033 } 1034 1035 return result; 1036} 1037 1038/*% 1039 * Wrapper around dns_sdlzunregister(). 1040 */ 1041void 1042dlz_fs_clear(void) { 1043 1044 /* 1045 * Write debugging message to log 1046 */ 1047 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1048 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 1049 "Unregistering DLZ filesystem driver."); 1050 1051 if (dlz_fs != NULL) 1052 dns_sdlzunregister(&dlz_fs); 1053} 1054 1055#endif 1056