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