diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:59:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:59:48 +0000 |
commit | 3b9b6d0b8e7f798023c9d109c490449d528fde80 (patch) | |
tree | 2e1c188dd7b8d7475cd163de9ae02c428343669b /contrib/dlz/modules/filesystem | |
parent | Initial commit. (diff) | |
download | bind9-3b9b6d0b8e7f798023c9d109c490449d528fde80.tar.xz bind9-3b9b6d0b8e7f798023c9d109c490449d528fde80.zip |
Adding upstream version 1:9.18.19.upstream/1%9.18.19upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | contrib/dlz/modules/filesystem/Makefile | 45 | ||||
-rw-r--r-- | contrib/dlz/modules/filesystem/dir.c | 118 | ||||
-rw-r--r-- | contrib/dlz/modules/filesystem/dir.h | 45 | ||||
-rw-r--r-- | contrib/dlz/modules/filesystem/dlz_filesystem_dynamic.c | 1008 |
4 files changed, 1216 insertions, 0 deletions
diff --git a/contrib/dlz/modules/filesystem/Makefile b/contrib/dlz/modules/filesystem/Makefile new file mode 100644 index 0000000..6c5470f --- /dev/null +++ b/contrib/dlz/modules/filesystem/Makefile @@ -0,0 +1,45 @@ +# Copyright Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. + +# Copyright (C) Stichting NLnet, Netherlands, stichting@nlnet.nl. +# +# The development of Dynamically Loadable Zones (DLZ) for Bind 9 was +# conceived and contributed by Rob Butler. +# +# SPDX-License-Identifier: ISC and MPL-2.0 +# +# Permission to use, copy, modify, and distribute this software for any purpose +# with or without fee is hereby granted, provided that the above copyright +# notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL STICHTING NLNET BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +prefix = /usr +libdir = $(prefix)/lib/bind9 + +CFLAGS += -fPIC -g -I../include + +all: dlz_filesystem_dynamic.so + +dir.o: dir.c + $(CC) $(CFLAGS) -c dir.c + +dlz_filesystem_dynamic.so: dlz_filesystem_dynamic.c dir.o + $(CC) $(CFLAGS) -shared -o dlz_filesystem_dynamic.so \ + dlz_filesystem_dynamic.c dir.o + +clean: + rm -f dlz_filesystem_dynamic.so *.o + +install: dlz_filesystem_dynamic.so + mkdir -p $(DESTDIR)$(libdir) + install dlz_filesystem_dynamic.so $(DESTDIR)$(libdir) diff --git a/contrib/dlz/modules/filesystem/dir.c b/contrib/dlz/modules/filesystem/dir.c new file mode 100644 index 0000000..f2b3a4e --- /dev/null +++ b/contrib/dlz/modules/filesystem/dir.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "dir.h" +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "dlz_minimal.h" + +void +dir_init(dir_t *dir) { + dir->entry.name[0] = '\0'; + dir->entry.length = 0; + + dir->handle = NULL; +} + +isc_result_t +dir_open(dir_t *dir, const char *dirname) { + char *p; + isc_result_t result = ISC_R_SUCCESS; + + if (strlen(dirname) + 3 > sizeof(dir->dirname)) { + return (ISC_R_NOSPACE); + } + strcpy(dir->dirname, dirname); + + p = dir->dirname + strlen(dir->dirname); + if (dir->dirname < p && *(p - 1) != '/') { + *p++ = '/'; + } + *p++ = '*'; + *p = '\0'; + + dir->handle = opendir(dirname); + if (dir->handle == NULL) { + switch (errno) { + case ENOTDIR: + case ELOOP: + case EINVAL: + case ENAMETOOLONG: + case EBADF: + result = ISC_R_INVALIDFILE; + break; + case ENOENT: + result = ISC_R_FILENOTFOUND; + break; + case EACCES: + case EPERM: + result = ISC_R_NOPERM; + break; + case ENOMEM: + result = ISC_R_NOMEMORY; + break; + default: + result = ISC_R_UNEXPECTED; + break; + } + } + + return (result); +} + +/*! + * \brief Return previously retrieved file or get next one. + * + * Unix's dirent has + * separate open and read functions, but the Win32 and DOS interfaces open + * the dir stream and reads the first file in one operation. + */ +isc_result_t +dir_read(dir_t *dir) { + struct dirent *entry; + + entry = readdir(dir->handle); + if (entry == NULL) { + return (ISC_R_NOMORE); + } + + if (sizeof(dir->entry.name) <= strlen(entry->d_name)) { + return (ISC_R_UNEXPECTED); + } + + strcpy(dir->entry.name, entry->d_name); + + dir->entry.length = strlen(entry->d_name); + return (ISC_R_SUCCESS); +} + +/*! + * \brief Close directory stream. + */ +void +dir_close(dir_t *dir) { + (void)closedir(dir->handle); + dir->handle = NULL; +} + +/*! + * \brief Reposition directory stream at start. + */ +isc_result_t +dir_reset(dir_t *dir) { + rewinddir(dir->handle); + + return (ISC_R_SUCCESS); +} diff --git a/contrib/dlz/modules/filesystem/dir.h b/contrib/dlz/modules/filesystem/dir.h new file mode 100644 index 0000000..b9a3749 --- /dev/null +++ b/contrib/dlz/modules/filesystem/dir.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <dirent.h> +#include <sys/types.h> + +#include <dlz_minimal.h> + +#define DIR_NAMEMAX 256 +#define DIR_PATHMAX 1024 + +typedef struct direntry { + char name[DIR_NAMEMAX]; + unsigned int length; +} direntry_t; + +typedef struct dir { + char dirname[DIR_PATHMAX]; + direntry_t entry; + DIR *handle; +} dir_t; + +void +dir_init(dir_t *dir); + +isc_result_t +dir_open(dir_t *dir, const char *dirname); + +isc_result_t +dir_read(dir_t *dir); + +isc_result_t +dir_reset(dir_t *dir); + +void +dir_close(dir_t *dir); diff --git a/contrib/dlz/modules/filesystem/dlz_filesystem_dynamic.c b/contrib/dlz/modules/filesystem/dlz_filesystem_dynamic.c new file mode 100644 index 0000000..3b8a8bb --- /dev/null +++ b/contrib/dlz/modules/filesystem/dlz_filesystem_dynamic.c @@ -0,0 +1,1008 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 and ISC + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + */ + +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * Permission to use, copy, modify, and distribute this software for any purpose + * with or without fee is hereby granted, provided that the above copyright + * notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This provides the externally loadable filesystem DLZ module, without + * update support + */ + +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include "dir.h" +#include "dlz_list.h" +#include "dlz_minimal.h" + +typedef struct config_data { + char *basedir; + int basedirsize; + char *datadir; + int datadirsize; + char *xfrdir; + int xfrdirsize; + int splitcnt; + char separator; + char pathsep; + + /* Helper functions from the dlz_dlopen driver */ + log_t *log; + dns_sdlz_putrr_t *putrr; + dns_sdlz_putnamedrr_t *putnamedrr; + dns_dlz_writeablezone_t *writeable_zone; +} config_data_t; + +typedef struct dir_entry dir_entry_t; + +struct dir_entry { + char dirpath[DIR_PATHMAX]; + DLZ_LINK(dir_entry_t) link; +}; + +typedef DLZ_LIST(dir_entry_t) dlist_t; + +/* forward reference */ + +static void +b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr); + +/* + * Private methods + */ +static bool +is_safe(const char *input) { + unsigned int i; + unsigned int len = strlen(input); + + /* check that only allowed characters are in the domain name */ + for (i = 0; i < len; i++) { + /* '.' is allowed, but has special requirements */ + if (input[i] == '.') { + /* '.' is not allowed as first char */ + if (i == 0) { + return (false); + } + /* '..', two dots together is not allowed. */ + if (input[i - 1] == '.') { + return (false); + } + /* '.' is not allowed as last char */ + if (i == len - 1) { + return (false); + } + /* only 1 dot in ok location, continue at next char */ + continue; + } + /* '-' is allowed, continue at next char */ + if (input[i] == '-') { + continue; + } + /* 0-9 is allowed, continue at next char */ + if (input[i] >= '0' && input[i] <= '9') { + continue; + } + /* A-Z uppercase is allowed, continue at next char */ + if (input[i] >= 'A' && input[i] <= 'Z') { + continue; + } + /* a-z lowercase is allowed, continue at next char */ + if (input[i] >= 'a' && input[i] <= 'z') { + continue; + } + + /* + * colon needs to be allowed for IPV6 client + * addresses. Not dangerous in domain names, as not a + * special char. + */ + if (input[i] == ':') { + continue; + } + + /* + * '@' needs to be allowed for in zone data. Not + * dangerous in domain names, as not a special char. + */ + if (input[i] == '@') { + continue; + } + + /* + * if we reach this point we have encountered a + * disallowed char! + */ + return (false); + } + /* everything ok. */ + return (true); +} + +static isc_result_t +create_path_helper(char *out, const char *in, config_data_t *cd) { + char *tmpString; + char *tmpPtr; + int i; + + tmpString = strdup(in); + if (tmpString == NULL) { + return (ISC_R_NOMEMORY); + } + + /* + * don't forget is_safe guarantees '.' will NOT be the + * first/last char + */ + while ((tmpPtr = strrchr(tmpString, '.')) != NULL) { + i = 0; + while (tmpPtr[i + 1] != '\0') { + if (cd->splitcnt < 1) { + strcat(out, (char *)&tmpPtr[i + 1]); + } else { + strncat(out, (char *)&tmpPtr[i + 1], + cd->splitcnt); + } + strncat(out, (char *)&cd->pathsep, 1); + if (cd->splitcnt == 0) { + break; + } + if (strlen((char *)&tmpPtr[i + 1]) <= + (unsigned int)cd->splitcnt) + { + break; + } + i += cd->splitcnt; + } + tmpPtr[0] = '\0'; + } + + /* handle the "first" label properly */ + i = 0; + tmpPtr = tmpString; + while (tmpPtr[i] != '\0') { + if (cd->splitcnt < 1) { + strcat(out, (char *)&tmpPtr[i]); + } else { + strncat(out, (char *)&tmpPtr[i], cd->splitcnt); + } + strncat(out, (char *)&cd->pathsep, 1); + if (cd->splitcnt == 0) { + break; + } + if (strlen((char *)&tmpPtr[i]) <= (unsigned int)cd->splitcnt) { + break; + } + i += cd->splitcnt; + } + + free(tmpString); + return (ISC_R_SUCCESS); +} + +/*% + * Checks to make sure zone and host are safe. If safe, then + * hashes zone and host strings to build a path. If zone / host + * are not safe an error is returned. + */ + +static isc_result_t +create_path(const char *zone, const char *host, const char *client, + config_data_t *cd, char **path) { + char *tmpPath; + int pathsize; + int len; + isc_result_t result; + bool isroot = false; + + /* special case for root zone */ + if (strcmp(zone, ".") == 0) { + isroot = true; + } + + /* if the requested zone is "unsafe", return error */ + if (!isroot && !is_safe(zone)) { + return (ISC_R_FAILURE); + } + + /* if host was passed, verify that it is safe */ + if (host != NULL && !is_safe(host)) { + return (ISC_R_FAILURE); + } + + /* if client was passed, verify that it is safe */ + if (client != NULL && !is_safe(client)) { + return (ISC_R_FAILURE); + } + + /* Determine how much memory the split up string will require */ + if (host != NULL) { + len = strlen(zone) + strlen(host); + } else if (client != NULL) { + len = strlen(zone) + strlen(client); + } else { + len = strlen(zone); + } + + /* + * even though datadir and xfrdir will never be in the same + * string we only waste a few bytes by allocating for both, + * and then we are safe from buffer overruns. + */ + pathsize = len + cd->basedirsize + cd->datadirsize + cd->xfrdirsize + 4; + + /* if we are splitting names, we will need extra space. */ + if (cd->splitcnt > 0) { + pathsize += len / cd->splitcnt; + } + + tmpPath = malloc(pathsize * sizeof(char)); + if (tmpPath == NULL) { + /* write error message */ + cd->log(ISC_LOG_ERROR, "Filesystem driver unable to " + "allocate memory in create_path()."); + result = ISC_R_NOMEMORY; + goto cleanup_mem; + } + + /* + * build path string. + * start out with base directory. + */ + strcpy(tmpPath, cd->basedir); + + /* add zone name - parsed properly */ + if (!isroot) { + result = create_path_helper(tmpPath, zone, cd); + if (result != ISC_R_SUCCESS) { + goto cleanup_mem; + } + } + + /* + * When neither client or host is passed we are building a + * path to see if a zone is supported. We require that a zone + * path have the "data dir" directory contained within it so + * that we know this zone is really supported. Otherwise, + * this zone may not really be supported because we are + * supporting a delagated sub zone. + * + * Example: + * + * We are supporting long.domain.com and using a splitcnt of + * 0. the base dir is "/base-dir/" and the data dir is + * "/.datadir" We want to see if we are authoritative for + * domain.com. Path /base-dir/com/domain/.datadir since + * /base-dir/com/domain/.datadir does not exist, we are not + * authoritative for the domain "domain.com". However we are + * authoritative for the domain "long.domain.com" because the + * path /base-dir/com/domain/long/.datadir does exist! + */ + + /* if client is passed append xfr dir, otherwise append data dir */ + if (client != NULL) { + strcat(tmpPath, cd->xfrdir); + strncat(tmpPath, (char *)&cd->pathsep, 1); + strcat(tmpPath, client); + } else { + strcat(tmpPath, cd->datadir); + } + + /* if host not null, add it. */ + if (host != NULL) { + strncat(tmpPath, (char *)&cd->pathsep, 1); + result = create_path_helper(tmpPath, host, cd); + if (result != ISC_R_SUCCESS) { + goto cleanup_mem; + } + } + + /* return the path we built. */ + *path = tmpPath; + + /* return success */ + result = ISC_R_SUCCESS; + +cleanup_mem: + /* cleanup memory */ + + /* free tmpPath memory */ + if (tmpPath != NULL && result != ISC_R_SUCCESS) { + free(tmpPath); + } + + return (result); +} + +static isc_result_t +process_dir(dir_t *dir, void *passback, config_data_t *cd, dlist_t *dir_list, + unsigned int basedirlen) { + char tmp[DIR_PATHMAX + DIR_NAMEMAX]; + int astPos; + struct stat sb; + isc_result_t result = ISC_R_FAILURE; + char *endp; + char *type; + char *ttlStr; + char *data; + char host[DIR_NAMEMAX]; + char *tmpString; + char *tmpPtr; + int ttl; + int i; + int len; + dir_entry_t *direntry; + bool foundHost; + + tmp[0] = '\0'; /* set 1st byte to '\0' so strcpy works right. */ + host[0] = '\0'; + foundHost = false; + + /* copy base directory name to tmp. */ + strcpy(tmp, dir->dirname); + + /* dir->dirname will always have '*' as the last char. */ + astPos = strlen(dir->dirname) - 1; + + /* if dir_list != NULL, were are performing a zone xfr */ + if (dir_list != NULL) { + /* if splitcnt == 0, determine host from path. */ + if (cd->splitcnt == 0) { + if (strlen(tmp) - 3 > basedirlen) { + tmp[astPos - 1] = '\0'; + tmpString = (char *)&tmp[basedirlen + 1]; + /* handle filesystem's special wildcard "-" */ + if (strcmp(tmpString, "-") == 0) { + strcpy(host, "*"); + } else { + /* + * not special wildcard -- normal name + */ + while ((tmpPtr = strrchr( + tmpString, + cd->pathsep)) != NULL) + { + if ((strlen(host) + + strlen(tmpPtr + 1) + 2) > + DIR_NAMEMAX) + { + continue; + } + strcat(host, tmpPtr + 1); + strcat(host, "."); + tmpPtr[0] = '\0'; + } + if ((strlen(host) + strlen(tmpString) + + 1) <= DIR_NAMEMAX) + { + strcat(host, tmpString); + } + } + + foundHost = true; + /* set tmp again for use later */ + strcpy(tmp, dir->dirname); + } + } else { + /* + * if splitcnt != 0 determine host from + * ".host" directory entry + */ + while (dir_read(dir) == ISC_R_SUCCESS) { + if (strncasecmp(".host", dir->entry.name, 5) == + 0) + { + /* + * handle filesystem's special + * wildcard "-" + */ + if (strcmp((char *)&dir->entry.name[6], + "-") == 0) + { + strcpy(host, "*"); + } else { + strncpy(host, + (char *)&dir->entry + .name[6], + sizeof(host) - 1); + host[255] = '\0'; + } + foundHost = true; + break; + } + } + /* reset dir list for use later */ + dir_reset(dir); + } /* end of else */ + } + + while (dir_read(dir) == ISC_R_SUCCESS) { + cd->log(ISC_LOG_DEBUG(1), + "Filesystem driver Dir name:" + " '%s' Dir entry: '%s'\n", + dir->dirname, dir->entry.name); + + /* skip any entries starting with "." */ + if (dir->entry.name[0] == '.') { + continue; + } + + /* + * get rid of '*', set to NULL. Effectively trims + * string from previous loop to base directory only + * while still leaving memory for concat to be + * performed next. + */ + + tmp[astPos] = '\0'; + + /* add name to base directory name. */ + strcat(tmp, dir->entry.name); + + /* make sure we can stat entry */ + if (stat(tmp, &sb) == 0) { + /* if entry is a directory */ + if ((sb.st_mode & S_IFDIR) != 0) { + /* + * if dir list is NOT NULL, add dir to + * dir list + */ + if (dir_list != NULL) { + direntry = malloc(sizeof(dir_entry_t)); + if (direntry == NULL) { + return (ISC_R_NOMEMORY); + } + strcpy(direntry->dirpath, tmp); + DLZ_LINK_INIT(direntry, link); + DLZ_LIST_APPEND(*dir_list, direntry, + link); + result = ISC_R_SUCCESS; + } + continue; + + /* + * if entry is a file be sure we do + * not add entry to DNS results if we + * are performing a zone xfr and we + * could not find a host entry. + */ + } else if (dir_list != NULL && !foundHost) { + continue; + } + } else { /* if we cannot stat entry, skip it. */ + continue; + } + + type = dir->entry.name; + ttlStr = strchr(type, cd->separator); + if (ttlStr == NULL) { + cd->log(ISC_LOG_ERROR, + "Filesystem driver: " + "%s could not be parsed properly", + tmp); + return (ISC_R_FAILURE); + } + + /* replace separator char with NULL to split string */ + ttlStr[0] = '\0'; + /* start string after NULL of previous string */ + ttlStr = (char *)&ttlStr[1]; + + data = strchr(ttlStr, cd->separator); + if (data == NULL) { + cd->log(ISC_LOG_ERROR, + "Filesystem driver: " + "%s could not be parsed properly", + tmp); + return (ISC_R_FAILURE); + } + + /* replace separator char with NULL to split string */ + data[0] = '\0'; + + /* start string after NULL of previous string */ + data = (char *)&data[1]; + + /* replace all cd->separator chars with a space. */ + len = strlen(data); + + for (i = 0; i < len; i++) { + if (data[i] == cd->separator) { + data[i] = ' '; + } + } + + /* convert text to int, make sure it worked right */ + ttl = strtol(ttlStr, &endp, 10); + if (*endp != '\0' || ttl < 0) { + cd->log(ISC_LOG_ERROR, "Filesystem driver " + "ttl must be a positive number"); + } + + /* pass data back to Bind */ + if (dir_list == NULL) { + result = cd->putrr((dns_sdlzlookup_t *)passback, type, + ttl, data); + } else { + result = cd->putnamedrr((dns_sdlzallnodes_t *)passback, + (char *)host, type, ttl, data); + } + + /* if error, return error right away */ + if (result != ISC_R_SUCCESS) { + return (result); + } + } /* end of while loop */ + + return (result); +} + +/* + * DLZ methods + */ +isc_result_t +dlz_allowzonexfr(void *dbdata, const char *name, const char *client) { + isc_result_t result; + char *path; + struct stat sb; + config_data_t *cd; + path = NULL; + + cd = (config_data_t *)dbdata; + + if (create_path(name, NULL, client, cd, &path) != ISC_R_SUCCESS) { + return (ISC_R_NOTFOUND); + } + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_AXFR; + } + + if ((sb.st_mode & S_IFREG) != 0) { + result = ISC_R_SUCCESS; + goto complete_AXFR; + } + + result = ISC_R_NOTFOUND; + +complete_AXFR: + free(path); + return (result); +} + +isc_result_t +dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) { + isc_result_t result; + dlist_t *dir_list; + config_data_t *cd = (config_data_t *)dbdata; + char *basepath; + unsigned int basepathlen; + struct stat sb; + dir_t dir; + dir_entry_t *dir_entry; + dir_entry_t *next_de; + + basepath = NULL; + + /* allocate memory for list */ + dir_list = malloc(sizeof(dlist_t)); + if (dir_list == NULL) { + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + /* initialize list */ + DLZ_LIST_INIT(*dir_list); + + if (create_path(zone, NULL, NULL, cd, &basepath) != ISC_R_SUCCESS) { + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + /* remove path separator at end of path so stat works properly */ + basepathlen = strlen(basepath); + + if (stat(basepath, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + if ((sb.st_mode & S_IFDIR) == 0) { + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + /* initialize and open directory */ + dir_init(&dir); + result = dir_open(&dir, basepath); + + /* if directory open failed, return error. */ + if (result != ISC_R_SUCCESS) { + cd->log(ISC_LOG_ERROR, + "Unable to open %s directory to read entries.", + basepath); + result = ISC_R_FAILURE; + goto complete_allnds; + } + + /* process the directory */ + result = process_dir(&dir, allnodes, cd, dir_list, basepathlen); + + /* close the directory */ + dir_close(&dir); + + if (result != ISC_R_SUCCESS) { + goto complete_allnds; + } + + /* get first dir entry from list. */ + dir_entry = DLZ_LIST_HEAD(*dir_list); + while (dir_entry != NULL) { + result = dir_open(&dir, dir_entry->dirpath); + /* if directory open failed, return error. */ + if (result != ISC_R_SUCCESS) { + cd->log(ISC_LOG_ERROR, + "Unable to open %s " + "directory to read entries.", + basepath); + result = ISC_R_FAILURE; + goto complete_allnds; + } + + /* process the directory */ + result = process_dir(&dir, allnodes, cd, dir_list, basepathlen); + + /* close the directory */ + dir_close(&dir); + + if (result != ISC_R_SUCCESS) { + goto complete_allnds; + } + + dir_entry = DLZ_LIST_NEXT(dir_entry, link); + } /* end while */ + +complete_allnds: + if (dir_list != NULL) { + /* clean up entries from list. */ + dir_entry = DLZ_LIST_HEAD(*dir_list); + while (dir_entry != NULL) { + next_de = DLZ_LIST_NEXT(dir_entry, link); + free(dir_entry); + dir_entry = next_de; + } /* end while */ + free(dir_list); + } + + if (basepath != NULL) { + free(basepath); + } + + return (result); +} + +#if DLZ_DLOPEN_VERSION < 3 +isc_result_t +dlz_findzonedb(void *dbdata, const char *name) +#else /* if DLZ_DLOPEN_VERSION < 3 */ +isc_result_t +dlz_findzonedb(void *dbdata, const char *name, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) +#endif /* if DLZ_DLOPEN_VERSION < 3 */ +{ + isc_result_t result; + config_data_t *cd = (config_data_t *)dbdata; + char *path; + struct stat sb; + path = NULL; + +#if DLZ_DLOPEN_VERSION >= 3 + UNUSED(methods); + UNUSED(clientinfo); +#endif /* if DLZ_DLOPEN_VERSION >= 3 */ + + if (create_path(name, NULL, NULL, cd, &path) != ISC_R_SUCCESS) { + return (ISC_R_NOTFOUND); + } + + cd->log(ISC_LOG_DEBUG(1), + "Filesystem driver Findzone() Checking for path: '%s'\n", path); + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_FZ; + } + + if ((sb.st_mode & S_IFDIR) != 0) { + result = ISC_R_SUCCESS; + goto complete_FZ; + } + + result = ISC_R_NOTFOUND; + +complete_FZ: + + free(path); + return (result); +} + +#if DLZ_DLOPEN_VERSION == 1 +isc_result_t +dlz_lookup(const char *zone, const char *name, void *dbdata, + dns_sdlzlookup_t *lookup) +#else /* if DLZ_DLOPEN_VERSION == 1 */ +isc_result_t +dlz_lookup(const char *zone, const char *name, void *dbdata, + dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) +#endif /* if DLZ_DLOPEN_VERSION == 1 */ +{ + isc_result_t result = ISC_R_NOTFOUND; + config_data_t *cd = (config_data_t *)dbdata; + char *path; + struct stat sb; + dir_t dir; + path = NULL; + + UNUSED(lookup); +#if DLZ_DLOPEN_VERSION >= 2 + UNUSED(methods); + UNUSED(clientinfo); +#endif /* if DLZ_DLOPEN_VERSION >= 2 */ + + if (strcmp(name, "*") == 0) { + /* + * handle filesystem's special wildcard "-" + */ + result = create_path(zone, "-", NULL, cd, &path); + } else { + result = create_path(zone, name, NULL, cd, &path); + } + + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOTFOUND); + } + + /* remove path separator at end of path so stat works properly */ + path[strlen(path) - 1] = '\0'; + + cd->log(ISC_LOG_DEBUG(1), + "Filesystem driver lookup() Checking for path: '%s'\n", path); + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_lkup; + } + + if ((sb.st_mode & S_IFDIR) == 0) { + result = ISC_R_NOTFOUND; + goto complete_lkup; + } + + /* initialize and open directory */ + dir_init(&dir); + result = dir_open(&dir, path); + + /* if directory open failed, return error. */ + if (result != ISC_R_SUCCESS) { + cd->log(ISC_LOG_ERROR, + "Unable to open %s directory to read entries.", path); + result = ISC_R_FAILURE; + goto complete_lkup; + } + + /* process any records in the directory */ + result = process_dir(&dir, lookup, cd, NULL, 0); + + /* close the directory */ + dir_close(&dir); + +complete_lkup: + + free(path); + return (result); +} + +isc_result_t +dlz_create(const char *dlzname, unsigned int argc, char *argv[], void **dbdata, + ...) { + isc_result_t result = ISC_R_NOMEMORY; + config_data_t *cd; + char *endp; + int len; + char pathsep; + const char *helper_name; + va_list ap; + + UNUSED(dlzname); + + /* allocate memory for our config data and helper functions */ + cd = calloc(1, sizeof(config_data_t)); + if (cd == NULL) { + goto no_mem; + } + + /* zero the memory */ + memset(cd, 0, sizeof(config_data_t)); + + /* Fill in the helper functions */ + va_start(ap, dbdata); + while ((helper_name = va_arg(ap, const char *)) != NULL) { + b9_add_helper(cd, helper_name, va_arg(ap, void *)); + } + va_end(ap); + + /* we require 5 command line args. */ + if (argc != 6) { + cd->log(ISC_LOG_ERROR, "Filesystem driver requires " + "6 command line args."); + result = ISC_R_FAILURE; + goto free_cd; + } + + if (strlen(argv[5]) > 1) { + cd->log(ISC_LOG_ERROR, "Filesystem driver can only " + "accept a single character for " + "separator."); + result = ISC_R_FAILURE; + goto free_cd; + } + + /* verify base dir ends with '/' or '\' */ + len = strlen(argv[1]); + if (argv[1][len - 1] != '\\' && argv[1][len - 1] != '/') { + cd->log(ISC_LOG_ERROR, + "Base dir parameter for filesystem driver " + "should end with %s", + "either '/' or '\\' "); + result = ISC_R_FAILURE; + goto free_cd; + } + + /* determine and save path separator for later */ + if (argv[1][len - 1] == '\\') { + pathsep = '\\'; + } else { + pathsep = '/'; + } + + cd->pathsep = pathsep; + + /* get and store our base directory */ + cd->basedir = strdup(argv[1]); + if (cd->basedir == NULL) { + goto no_mem; + } + cd->basedirsize = strlen(cd->basedir); + + /* get and store our data sub-dir */ + cd->datadir = strdup(argv[2]); + if (cd->datadir == NULL) { + goto no_mem; + } + cd->datadirsize = strlen(cd->datadir); + + /* get and store our zone xfr sub-dir */ + cd->xfrdir = strdup(argv[3]); + if (cd->xfrdir == NULL) { + goto no_mem; + } + cd->xfrdirsize = strlen(cd->xfrdir); + + /* get and store our directory split count */ + cd->splitcnt = strtol(argv[4], &endp, 10); + if (*endp != '\0' || cd->splitcnt < 0) { + cd->log(ISC_LOG_ERROR, "Directory split count must be zero (0) " + "or a positive number"); + } + + /* get and store our separator character */ + cd->separator = *argv[5]; + + /* pass back config data */ + *dbdata = cd; + + /* return success */ + return (ISC_R_SUCCESS); + + /* handle no memory error */ +no_mem: + + /* write error message */ + if (cd != NULL && cd->log != NULL) { + cd->log(ISC_LOG_ERROR, "filesystem_dynamic: Filesystem driver " + "unable to " + "allocate memory for config data."); + } + +free_cd: + /* if we allocated a config data object clean it up */ + if (cd != NULL) { + dlz_destroy(cd); + } + + /* return error */ + return (result); +} + +void +dlz_destroy(void *dbdata) { + config_data_t *cd; + + cd = (config_data_t *)dbdata; + + /* + * free memory for each section of config data that was + * allocated + */ + if (cd->basedir != NULL) { + free(cd->basedir); + } + + if (cd->datadir != NULL) { + free(cd->datadir); + } + + if (cd->xfrdir != NULL) { + free(cd->xfrdir); + } + + /* free config data memory */ + free(cd); +} + +/* + * Return the version of the API + */ +int +dlz_version(unsigned int *flags) { + UNUSED(flags); + return (DLZ_DLOPEN_VERSION); +} + +/* + * Register a helper function from the bind9 dlz_dlopen driver + */ +static void +b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr) { + if (strcmp(helper_name, "log") == 0) { + cd->log = (log_t *)ptr; + } + if (strcmp(helper_name, "putrr") == 0) { + cd->putrr = (dns_sdlz_putrr_t *)ptr; + } + if (strcmp(helper_name, "putnamedrr") == 0) { + cd->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr; + } + if (strcmp(helper_name, "writeable_zone") == 0) { + cd->writeable_zone = (dns_dlz_writeablezone_t *)ptr; + } +} |