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