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
is_safe(const char * input)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
create_path_helper(char * out,const char * in,config_data_t * cd)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
create_path(const char * zone,const char * host,const char * client,config_data_t * cd,char ** path)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
process_dir(dir_t * dir,void * passback,config_data_t * cd,dlist_t * dir_list,unsigned int basedirlen)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
dlz_allowzonexfr(void * dbdata,const char * name,const char * client)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
dlz_allnodes(const char * zone,void * dbdata,dns_sdlzallnodes_t * allnodes)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
dlz_findzonedb(void * dbdata,const char * name)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
dlz_lookup(const char * zone,const char * name,void * dbdata,dns_sdlzlookup_t * lookup)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
dlz_create(const char * dlzname,unsigned int argc,char * argv[],void ** dbdata,...)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
dlz_destroy(void * dbdata)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
dlz_version(unsigned int * flags)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
b9_add_helper(struct config_data * cd,const char * helper_name,void * ptr)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