dlz_filesystem_driver.c [plain text]
#ifdef DLZ_FILESYSTEM
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dns/log.h>
#include <dns/sdlz.h>
#include <dns/result.h>
#include <isc/dir.h>
#include <isc/mem.h>
#include <isc/platform.h>
#include <isc/print.h>
#include <isc/result.h>
#include <isc/util.h>
#include <named/globals.h>
#include <dlz/dlz_filesystem_driver.h>
static dns_sdlzimplementation_t *dlz_fs = NULL;
typedef struct config_data {
char *basedir;
int basedirsize;
char *datadir;
int datadirsize;
char *xfrdir;
int xfrdirsize;
int splitcnt;
char separator;
char pathsep;
isc_mem_t *mctx;
} config_data_t;
typedef struct dir_entry dir_entry_t;
struct dir_entry {
char dirpath[ISC_DIR_PATHMAX];
ISC_LINK(dir_entry_t) link;
};
typedef ISC_LIST(dir_entry_t) dlist_t;
static void
fs_destroy(void *driverarg, void *dbdata);
static isc_boolean_t
is_safe(const char *input) {
unsigned int i;
unsigned int len = strlen(input);
for (i=0; i < len; i++) {
if (input[i] == '.') {
if (i == 0)
return (ISC_FALSE);
else if (input[i-1] == '.')
return (ISC_FALSE);
if (i == len)
return (ISC_FALSE);
continue;
}
if (input[i] == '-')
continue;
if (input[i] >= '0' && input[i] <= '9')
continue;
if (input[i] >= 'A' && input[i] <= 'Z')
continue;
if (input[i] >= 'a' && input[i] <= 'z')
continue;
if (input[i] == ':')
continue;
if (input[i] == '@')
continue;
return (ISC_FALSE);
}
return (ISC_TRUE);
}
static isc_result_t
create_path_helper(char *out, const char *in, config_data_t *cd) {
char *tmpString;
char *tmpPtr;
int i;
tmpString = isc_mem_strdup(ns_g_mctx, in);
if (tmpString == NULL)
return (ISC_R_NOMEMORY);
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';
}
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;
}
isc_mem_free(ns_g_mctx, tmpString);
return (ISC_R_SUCCESS);
}
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;
isc_boolean_t isroot = ISC_FALSE;
REQUIRE(zone != NULL);
REQUIRE(cd != NULL);
REQUIRE(path != NULL && *path == NULL);
REQUIRE( (host == NULL && client == NULL) ||
(host != NULL && client == NULL) ||
(host == NULL && client != NULL) );
if (strcmp(zone, ".") == 0)
isroot = ISC_TRUE;
if (!isroot && !is_safe(zone))
return (ISC_R_FAILURE);
if (host != NULL && !is_safe(host))
return (ISC_R_FAILURE);
if (client != NULL && !is_safe(client))
return (ISC_R_FAILURE);
if (host != NULL)
len = strlen(zone) + strlen(host);
else if (client != NULL)
len = strlen(zone) + strlen(client);
else
len = strlen(zone);
pathsize = len + cd->basedirsize +
cd->datadirsize + cd->xfrdirsize + 4;
if (cd->splitcnt > 0)
pathsize += len/cd->splitcnt;
tmpPath = isc_mem_allocate(ns_g_mctx , pathsize * sizeof(char));
if (tmpPath == NULL) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Filesystem driver unable to "
"allocate memory in create_path().");
result = ISC_R_NOMEMORY;
goto cleanup_mem;
}
strcpy(tmpPath, cd->basedir);
if (!isroot) {
result = create_path_helper(tmpPath, zone, cd);
if (result != ISC_R_SUCCESS)
goto cleanup_mem;
}
if (client != NULL) {
strcat(tmpPath, cd->xfrdir);
strncat(tmpPath, (char *) &cd->pathsep, 1);
strcat(tmpPath, client);
} else {
strcat(tmpPath, cd->datadir);
}
if (host != NULL) {
strncat(tmpPath, (char *) &cd->pathsep, 1);
if ((result = create_path_helper(tmpPath, host,
cd)) != ISC_R_SUCCESS)
goto cleanup_mem;
}
*path = tmpPath;
result = ISC_R_SUCCESS;
cleanup_mem:
if (tmpPath != NULL && result != ISC_R_SUCCESS)
isc_mem_free(ns_g_mctx, tmpPath);
return (result);
}
static isc_result_t
process_dir(isc_dir_t *dir, void *passback, config_data_t *cd,
dlist_t *dir_list, unsigned int basedirlen)
{
char tmp[ISC_DIR_PATHMAX + ISC_DIR_NAMEMAX];
int astPos;
struct stat sb;
isc_result_t result = ISC_R_FAILURE;
char *endp;
char *type;
char *ttlStr;
char *data;
char host[ISC_DIR_NAMEMAX];
char *tmpString;
char *tmpPtr;
int ttl;
int i;
int len;
dir_entry_t *direntry;
isc_boolean_t foundHost;
tmp[0] = '\0';
host[0] = '\0';
foundHost = ISC_FALSE;
strcpy(tmp, dir->dirname);
astPos = strlen(dir->dirname) - 1;
if (dir_list != NULL) {
if (cd->splitcnt == 0) {
if (strlen(tmp) - 3 > basedirlen) {
tmp[astPos-1] = '\0';
tmpString = (char *) &tmp[basedirlen+1];
if (strcmp(tmpString, "-") == 0) {
strcpy(host, "*");
} else {
while ((tmpPtr = strrchr(tmpString,
cd->pathsep))
!= NULL)
{
if ((strlen(host) +
strlen(tmpPtr + 1) + 2)
> ISC_DIR_NAMEMAX)
continue;
strcat(host, tmpPtr + 1);
strcat(host, ".");
tmpPtr[0] = '\0';
}
if ((strlen(host) +
strlen(tmpString) + 1)
<= ISC_DIR_NAMEMAX)
strcat(host, tmpString);
}
foundHost = ISC_TRUE;
strcpy(tmp, dir->dirname);
}
} else {
while (isc_dir_read(dir) == ISC_R_SUCCESS) {
if (strncasecmp(".host",
dir->entry.name, 5) == 0) {
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 = ISC_TRUE;
break;
}
}
isc_dir_reset(dir);
}
}
while (isc_dir_read(dir) == ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
"Filesystem driver Dir name:"
" '%s' Dir entry: '%s'\n",
dir->dirname, dir->entry.name);
if (dir->entry.name[0] == '.')
continue;
tmp[astPos] = '\0';
strcat(tmp, dir->entry.name);
if (stat(tmp, &sb) == 0 ) {
if ((sb.st_mode & S_IFDIR) != 0) {
if (dir_list != NULL) {
direntry =
isc_mem_get(ns_g_mctx,
sizeof(dir_entry_t));
if (direntry == NULL)
return (ISC_R_NOMEMORY);
strcpy(direntry->dirpath, tmp);
ISC_LINK_INIT(direntry, link);
ISC_LIST_APPEND(*dir_list, direntry,
link);
result = ISC_R_SUCCESS;
}
continue;
} else if (dir_list != NULL &&
foundHost == ISC_FALSE) {
continue;
}
} else
continue;
type = dir->entry.name;
ttlStr = strchr(type, cd->separator);
if (ttlStr == NULL) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Filesystem driver: "
"%s could not be parsed properly",
tmp);
return (ISC_R_FAILURE);
}
ttlStr[0] = '\0';
ttlStr = (char *) &ttlStr[1];
data = strchr(ttlStr, cd->separator);
if (data == NULL) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Filesystem driver: "
"%s could not be parsed properly",
tmp);
return (ISC_R_FAILURE);
}
data[0] = '\0';
data = (char *) &data[1];
len = strlen(data);
for (i=0; i < len; i++) {
if (data[i] == cd->separator)
data[i] = ' ';
}
ttl = strtol(ttlStr, &endp, 10);
if (*endp != '\0' || ttl < 0) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Filesystem driver "
"ttl must be a postive number");
}
if (dir_list == NULL)
result = dns_sdlz_putrr((dns_sdlzlookup_t *) passback,
type, ttl, data);
else
result = dns_sdlz_putnamedrr((dns_sdlzallnodes_t *)
passback,
(char *) host,
type, ttl, data);
if (result != ISC_R_SUCCESS)
return (result);
}
return (result);
}
static isc_result_t
fs_allowzonexfr(void *driverarg, void *dbdata, const char *name,
const char *client)
{
isc_result_t result;
char *path;
struct stat sb;
config_data_t *cd;
path = NULL;
UNUSED(driverarg);
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:
isc_mem_free(ns_g_mctx, path);
return (result);
}
static isc_result_t
fs_allnodes(const char *zone, void *driverarg, void *dbdata,
dns_sdlzallnodes_t *allnodes)
{
isc_result_t result;
dlist_t *dir_list;
config_data_t *cd;
char *basepath;
unsigned int basepathlen;
struct stat sb;
isc_dir_t dir;
dir_entry_t *dir_entry;
dir_entry_t *next_de;
basepath = NULL;
dir_list = NULL;
UNUSED(driverarg);
UNUSED(allnodes);
cd = (config_data_t *) dbdata;
dir_list = isc_mem_get(ns_g_mctx, sizeof(dlist_t));
if (dir_list == NULL) {
result = ISC_R_NOTFOUND;
goto complete_allnds;
}
ISC_LIST_INIT(*dir_list);
if (create_path(zone, NULL, NULL, cd, &basepath) != ISC_R_SUCCESS) {
return (ISC_R_NOTFOUND);
}
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;
}
isc_dir_init(&dir);
result = isc_dir_open(&dir, basepath);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Unable to open %s directory to read entries.",
basepath);
result = ISC_R_FAILURE;
goto complete_allnds;
}
result = process_dir(&dir, allnodes, cd, dir_list, basepathlen);
isc_dir_close(&dir);
if (result != ISC_R_SUCCESS)
goto complete_allnds;
dir_entry = ISC_LIST_HEAD(*dir_list);
while (dir_entry != NULL) {
result = isc_dir_open(&dir, dir_entry->dirpath);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Unable to open %s "
"directory to read entries.",
basepath);
result = ISC_R_FAILURE;
goto complete_allnds;
}
result = process_dir(&dir, allnodes, cd, dir_list, basepathlen);
isc_dir_close(&dir);
if (result != ISC_R_SUCCESS)
goto complete_allnds;
dir_entry = ISC_LIST_NEXT(dir_entry, link);
}
complete_allnds:
if (dir_list != NULL) {
dir_entry = ISC_LIST_HEAD(*dir_list);
while (dir_entry != NULL) {
next_de = ISC_LIST_NEXT(dir_entry, link);
isc_mem_put(ns_g_mctx, dir_entry, sizeof(dir_entry_t));
dir_entry = next_de;
}
isc_mem_put(ns_g_mctx, dir_list, sizeof(dlist_t));
}
if (basepath != NULL)
isc_mem_free(ns_g_mctx, basepath);
return (result);
}
static isc_result_t
fs_findzone(void *driverarg, void *dbdata, const char *name,
dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
{
isc_result_t result;
char *path;
struct stat sb;
path = NULL;
UNUSED(driverarg);
UNUSED(methods);
UNUSED(clientinfo);
if (create_path(name, NULL, NULL, (config_data_t *) dbdata,
&path) != ISC_R_SUCCESS) {
return (ISC_R_NOTFOUND);
}
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, 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:
isc_mem_free(ns_g_mctx, path);
return (result);
}
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)
{
isc_result_t result;
char *path;
struct stat sb;
isc_dir_t dir;
path = NULL;
UNUSED(driverarg);
UNUSED(lookup);
UNUSED(methods);
UNUSED(clientinfo);
if (strcmp(name, "*") == 0)
result = create_path(zone, "-", NULL,
(config_data_t *) dbdata, &path);
else
result = create_path(zone, name, NULL,
(config_data_t *) dbdata, &path);
if ( result != ISC_R_SUCCESS) {
return (ISC_R_NOTFOUND);
}
path[strlen(path)-1] = '\0';
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, 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;
}
isc_dir_init(&dir);
result = isc_dir_open(&dir, path);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Unable to open %s directory to read entries.",
path);
result = ISC_R_FAILURE;
goto complete_lkup;
}
result = process_dir(&dir, lookup, (config_data_t *) dbdata, NULL, 0);
isc_dir_close(&dir);
complete_lkup:
isc_mem_free(ns_g_mctx, path);
return (result);
}
static isc_result_t
fs_create(const char *dlzname, unsigned int argc, char *argv[],
void *driverarg, void **dbdata)
{
config_data_t *cd;
char *endp;
int len;
char pathsep;
UNUSED(driverarg);
UNUSED(dlzname);
if (argc != 6) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Filesystem driver requires "
"6 command line args.");
return (ISC_R_FAILURE);
}
if (strlen(argv[5]) > 1) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Filesystem driver can only "
"accept a single character for separator.");
return (ISC_R_FAILURE);
}
len = strlen(argv[1]);
if (argv[1][len-1] != '\\' && argv[1][len-1] != '/') {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Base dir parameter for filesystem driver "
"should end with %s",
"either '/' or '\\' ");
return (ISC_R_FAILURE);
}
if (argv[1][len-1] == '\\')
pathsep = '\\';
else
pathsep = '/';
cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t));
if (cd == NULL)
goto no_mem;
memset(cd, 0, sizeof(config_data_t));
cd->pathsep = pathsep;
cd->basedir = isc_mem_strdup(ns_g_mctx, argv[1]);
if (cd->basedir == NULL)
goto no_mem;
cd->basedirsize = strlen(cd->basedir);
cd->datadir = isc_mem_strdup(ns_g_mctx, argv[2]);
if (cd->datadir == NULL)
goto no_mem;
cd->datadirsize = strlen(cd->datadir);
cd->xfrdir = isc_mem_strdup(ns_g_mctx, argv[3]);
if (cd->xfrdir == NULL)
goto no_mem;
cd->xfrdirsize = strlen(cd->xfrdir);
cd->splitcnt = strtol(argv[4], &endp, 10);
if (*endp != '\0' || cd->splitcnt < 0) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Directory split count must be zero (0) "
"or a postive number");
}
cd->separator = *argv[5];
isc_mem_attach(ns_g_mctx, &cd->mctx);
*dbdata = cd;
return (ISC_R_SUCCESS);
no_mem:
if (cd != NULL)
fs_destroy(NULL, cd);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Filesystem driver unable to "
"allocate memory for config data.");
return (ISC_R_NOMEMORY);
}
static void
fs_destroy(void *driverarg, void *dbdata)
{
isc_mem_t *mctx;
config_data_t *cd;
UNUSED(driverarg);
cd = (config_data_t *) dbdata;
if (cd->basedir != NULL)
isc_mem_free(ns_g_mctx, cd->basedir);
if (cd->datadir != NULL)
isc_mem_free(ns_g_mctx, cd->datadir);
if (cd->xfrdir != NULL)
isc_mem_free(ns_g_mctx, cd->xfrdir);
mctx = cd->mctx;
isc_mem_put(mctx, cd, sizeof(config_data_t));
isc_mem_detach(&mctx);
}
static dns_sdlzmethods_t dlz_fs_methods = {
fs_create,
fs_destroy,
fs_findzone,
fs_lookup,
NULL,
fs_allnodes,
fs_allowzonexfr,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
isc_result_t
dlz_fs_init(void)
{
isc_result_t result;
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
"Registering DLZ filesystem driver.");
result = dns_sdlzregister("filesystem", &dlz_fs_methods, NULL,
DNS_SDLZFLAG_RELATIVEOWNER |
DNS_SDLZFLAG_RELATIVERDATA,
ns_g_mctx, &dlz_fs);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"dns_sdlzregister() failed: %s",
isc_result_totext(result));
result = ISC_R_UNEXPECTED;
}
return (result);
}
void
dlz_fs_clear(void) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
"Unregistering DLZ filesystem driver.");
if (dlz_fs != NULL)
dns_sdlzunregister(&dlz_fs);
}
#endif