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