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