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