[master] DLZ modules: filesystem, ldap, wildcard

3523.	[contrib]	Ported filesystem and ldap DLZ drivers to
			dynamically-loadable modules, and added the
			"wildcard" module based on a contribution from
			Vadim Goncharov <vgoncharov@nic.ru>. [RT #23569]
This commit is contained in:
Evan Hunt 2013-03-11 16:49:52 -07:00
parent 21a7fde6ba
commit 72c86c105a
26 changed files with 5026 additions and 653 deletions

View file

@ -1,3 +1,8 @@
3523. [contrib] Ported filesystem and ldap DLZ drivers to
dynamically-loadable modules, and added the
"wildcard" module based on a contribution from
Vadim Goncharov <vgoncharov@nic.ru>. [RT #23569]
3522. [bug] DLZ lookups could fail to return SERVFAIL when
they ought to. [RT #32685]

View file

@ -602,6 +602,7 @@ dlz_dlopen_init(isc_mem_t *mctx) {
result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
DNS_SDLZFLAG_RELATIVEOWNER |
DNS_SDLZFLAG_RELATIVERDATA |
DNS_SDLZFLAG_THREADSAFE,
mctx, &dlz_dlopen);

View file

@ -580,6 +580,7 @@ dlz_dlopen_init(isc_mem_t *mctx) {
result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
DNS_SDLZFLAG_RELATIVEOWNER |
DNS_SDLZFLAG_RELATIVERDATA |
DNS_SDLZFLAG_THREADSAFE,
mctx, &dlz_dlopen);

View file

@ -1,12 +1,12 @@
prefix = /usr
libdir = $(prefix)/lib/bind9
CFLAGS=-fPIC -g
CFLAGS=-fPIC -g -I../include
BDB_LIBS=-ldb
all: dlz_bdbhpt_dynamic.so
dlz_bdbhpt_dynamic.so:
dlz_bdbhpt_dynamic.so: dlz_bdbhpt_dynamic.c
$(CC) $(CFLAGS) -shared -o dlz_bdbhpt_dynamic.so \
dlz_bdbhpt_dynamic.c $(BDB_LIBS)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,461 @@
/*
* Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
*
* 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.
*
* 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 ROB BUTLER
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* ROB BUTLER 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.
*/
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2001 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/errno.h>
#include <dlz_minimal.h>
#include <dlz_list.h>
#include <dlz_dbi.h>
#include <dlz_pthread.h>
/*%
* properly destroys a querylist by de-allocating the
* memory for each query segment, and then the list itself
*/
void
destroy_querylist(query_list_t **querylist) {
query_segment_t *tseg = NULL;
query_segment_t *nseg = NULL;
/* if query list is null, nothing to do */
if (*querylist == NULL)
return;
/* start at the top of the list */
nseg = DLZ_LIST_HEAD(**querylist);
while (nseg != NULL) { /* loop, until end of list */
tseg = nseg;
/*
* free the query segment's text string but only if it
* was really a query segment, and not a pointer to
* %zone%, or %record%, or %client%
*/
if (tseg->cmd != NULL && tseg->direct == ISC_TRUE)
free(tseg->cmd);
/* get the next query segment, before we destroy this one. */
nseg = DLZ_LIST_NEXT(nseg, link);
/* deallocate this query segment. */
free(tseg);
}
/* deallocate the query segment list */
free(*querylist);
}
/*% constructs a query list by parsing a string into query segments */
isc_result_t
build_querylist(const char *query_str, char **zone, char **record,
char **client, query_list_t **querylist, unsigned int flags,
log_t log)
{
isc_result_t result;
isc_boolean_t foundzone = ISC_FALSE;
isc_boolean_t foundrecord = ISC_FALSE;
isc_boolean_t foundclient = ISC_FALSE;
char *temp_str = NULL;
char *right_str = NULL;
query_list_t *tql;
query_segment_t *tseg = NULL;
/* if query string is null, or zero length */
if (query_str == NULL || strlen(query_str) < 1) {
if ((flags & REQUIRE_QUERY) == 0)
/* we don't need it were ok. */
return (ISC_R_SUCCESS);
else
/* we did need it, PROBLEM!!! */
return (ISC_R_FAILURE);
}
/* allocate memory for query list */
tql = calloc(1, sizeof(query_list_t));
/* couldn't allocate memory. Problem!! */
if (tql == NULL)
return (ISC_R_NOMEMORY);
/* initialize the query segment list */
DLZ_LIST_INIT(*tql);
/* make a copy of query_str so we can chop it up */
temp_str = right_str = strdup(query_str);
/* couldn't make a copy, problem!! */
if (right_str == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
/* loop through the string and chop it up */
while (right_str != NULL) {
/* allocate memory for tseg */
tseg = calloc(1, sizeof(query_segment_t));
if (tseg == NULL) { /* no memory, clean everything up. */
result = ISC_R_NOMEMORY;
goto cleanup;
}
tseg->cmd = NULL;
tseg->direct = ISC_FALSE;
/* initialize the query segment link */
DLZ_LINK_INIT(tseg, link);
/* append the query segment to the list */
DLZ_LIST_APPEND(*tql, tseg, link);
/*
* split string at the first "$". set query segment to
* left portion
*/
tseg->cmd = strdup(strsep(&right_str, "$"));
if (tseg->cmd == NULL) {
/* no memory, clean everything up. */
result = ISC_R_NOMEMORY;
goto cleanup;
}
/* tseg->cmd points directly to a string. */
tseg->direct = ISC_TRUE;
tseg->strlen = strlen(tseg->cmd);
/* check if we encountered "$zone$" token */
if (strcasecmp(tseg->cmd, "zone") == 0) {
/*
* we don't really need, or want the "zone"
* text, so get rid of it.
*/
free(tseg->cmd);
/* set tseg->cmd to in-direct zone string */
tseg->cmd = (char**) zone;
tseg->strlen = 0;
/* tseg->cmd points in-directly to a string */
tseg->direct = ISC_FALSE;
foundzone = ISC_TRUE;
/* check if we encountered "$record$" token */
} else if (strcasecmp(tseg->cmd, "record") == 0) {
/*
* we don't really need, or want the "record"
* text, so get rid of it.
*/
free(tseg->cmd);
/* set tseg->cmd to in-direct record string */
tseg->cmd = (char**) record;
tseg->strlen = 0;
/* tseg->cmd points in-directly poinsts to a string */
tseg->direct = ISC_FALSE;
foundrecord = ISC_TRUE;
/* check if we encountered "$client$" token */
} else if (strcasecmp(tseg->cmd, "client") == 0) {
/*
* we don't really need, or want the "client"
* text, so get rid of it.
*/
free(tseg->cmd);
/* set tseg->cmd to in-direct record string */
tseg->cmd = (char**) client;
tseg->strlen = 0;
/* tseg->cmd points in-directly poinsts to a string */
tseg->direct = ISC_FALSE;
foundclient = ISC_TRUE;
}
}
/* we don't need temp_str any more */
free(temp_str);
/*
* add checks later to verify zone and record are found if
* necessary.
*/
/* if this query requires %client%, make sure we found it */
if (((flags & REQUIRE_CLIENT) != 0) && (!foundclient) ) {
/* Write error message to log */
if (log != NULL)
log(ISC_LOG_ERROR,
"Required token $client$ not found.");
result = ISC_R_FAILURE;
goto flag_fail;
}
/* if this query requires %record%, make sure we found it */
if (((flags & REQUIRE_RECORD) != 0) && (!foundrecord) ) {
/* Write error message to log */
if (log != NULL)
log(ISC_LOG_ERROR,
"Required token $record$ not found.");
result = ISC_R_FAILURE;
goto flag_fail;
}
/* if this query requires %zone%, make sure we found it */
if (((flags & REQUIRE_ZONE) != 0) && (!foundzone) ) {
/* Write error message to log */
if (log != NULL)
log(ISC_LOG_ERROR, "Required token $zone$ not found.");
result = ISC_R_FAILURE;
goto flag_fail;
}
/* pass back the query list */
*querylist = (query_list_t *) tql;
/* return success */
return (ISC_R_SUCCESS);
cleanup:
/* get rid of temp_str */
if (temp_str != NULL)
free(temp_str);
flag_fail:
/* get rid of what was build of the query list */
if (tql != NULL)
destroy_querylist(&tql);
return (result);
}
/*%
* build a query string from query segments, and dynamic segments
* dynamic segments replace where the tokens %zone%, %record%, %client%
* used to be in our queries from named.conf
*/
char *
build_querystring(query_list_t *querylist) {
query_segment_t *tseg = NULL;
unsigned int length = 0;
char *qs = NULL;
/* start at the top of the list */
tseg = DLZ_LIST_HEAD(*querylist);
while (tseg != NULL) {
/*
* if this is a query segment, use the
* precalculated string length
*/
if (tseg->direct == ISC_TRUE)
length += tseg->strlen;
else /* calculate string length for dynamic segments. */
length += strlen(* (char**) tseg->cmd);
/* get the next segment */
tseg = DLZ_LIST_NEXT(tseg, link);
}
/* allocate memory for the string */
qs = malloc(length + 1);
/* couldn't allocate memory, We need more ram! */
if (qs == NULL)
return (NULL);
*qs = 0;
/* start at the top of the list again */
tseg = DLZ_LIST_HEAD(*querylist);
while (tseg != NULL) {
if (tseg->direct == ISC_TRUE)
/* query segments */
strcat(qs, tseg->cmd);
else
/* dynamic segments */
strcat(qs, * (char**) tseg->cmd);
/* get the next segment */
tseg = DLZ_LIST_NEXT(tseg, link);
}
return (qs);
}
/*% constructs a dbinstance (DBI) */
isc_result_t
build_dbinstance(const char *allnodes_str, const char *allowxfr_str,
const char *authority_str, const char *findzone_str,
const char *lookup_str, const char *countzone_str,
dbinstance_t **dbi, log_t log)
{
isc_result_t result;
dbinstance_t *db = NULL;
int err;
/* allocate and zero memory for driver structure */
db = calloc(1, sizeof(dbinstance_t));
if (db == NULL) {
if (log != NULL)
log(ISC_LOG_ERROR,
"Could not allocate memory for "
"database instance object.");
return (ISC_R_NOMEMORY);
}
memset(db, 0, sizeof(dbinstance_t));
db->dbconn = NULL;
db->client = NULL;
db->record = NULL;
db->zone = NULL;
db->query_buf = NULL;
db->allnodes_q = NULL;
db->allowxfr_q = NULL;
db->authority_q = NULL;
db->findzone_q = NULL;
db->countzone_q = NULL;
db->lookup_q = NULL;
/* initialize the reference count mutex */
err = dlz_mutex_init(&db->lock, NULL);
if (err == ENOMEM) {
result = ISC_R_NOMEMORY;
goto cleanup;
} else if (err != 0) {
result = ISC_R_UNEXPECTED;
goto cleanup;
}
/* build the all nodes query list */
result = build_querylist(allnodes_str, &db->zone, &db->record,
&db->client, &db->allnodes_q,
REQUIRE_ZONE, log);
/* if unsuccessful, log err msg and cleanup */
if (result != ISC_R_SUCCESS) {
if (log != NULL)
log(ISC_LOG_ERROR,
"Could not build all nodes query list");
goto cleanup;
}
/* build the allow zone transfer query list */
result = build_querylist(allowxfr_str, &db->zone, &db->record,
&db->client, &db->allowxfr_q,
REQUIRE_ZONE | REQUIRE_CLIENT,
log);
/* if unsuccessful, log err msg and cleanup */
if (result != ISC_R_SUCCESS) {
if (log != NULL)
log(ISC_LOG_ERROR,
"Could not build allow xfr query list");
goto cleanup;
}
/* build the authority query, query list */
result = build_querylist(authority_str, &db->zone, &db->record,
&db->client, &db->authority_q,
REQUIRE_ZONE, log);
/* if unsuccessful, log err msg and cleanup */
if (result != ISC_R_SUCCESS) {
if (log != NULL)
log(ISC_LOG_ERROR,
"Could not build authority query list");
goto cleanup;
}
/* build findzone query, query list */
result = build_querylist(findzone_str, &db->zone, &db->record,
&db->client, &db->findzone_q,
REQUIRE_ZONE, log);
/* if unsuccessful, log err msg and cleanup */
if (result != ISC_R_SUCCESS) {
if (log != NULL)
log(ISC_LOG_ERROR,
"Could not build find zone query list");
goto cleanup;
}
/* build countzone query, query list */
result = build_querylist(countzone_str, &db->zone, &db->record,
&db->client, &db->countzone_q,
REQUIRE_ZONE, log);
/* if unsuccessful, log err msg and cleanup */
if (result != ISC_R_SUCCESS) {
if (log != NULL)
log(ISC_LOG_ERROR,
"Could not build count zone query list");
goto cleanup;
}
/* build lookup query, query list */
result = build_querylist(lookup_str, &db->zone, &db->record,
&db->client, &db->lookup_q,
REQUIRE_RECORD, log);
/* if unsuccessful, log err msg and cleanup */
if (result != ISC_R_SUCCESS) {
if (log != NULL)
log(ISC_LOG_ERROR,
"Could not build lookup query list");
goto cleanup;
}
/* pass back the db instance */
*dbi = (dbinstance_t *) db;
/* return success */
return (ISC_R_SUCCESS);
cleanup:
/* destroy whatever was build of the db instance */
destroy_dbinstance(db);
/* return failure */
return (ISC_R_FAILURE);
}
void
destroy_dbinstance(dbinstance_t *dbi) {
/* destroy any query lists we created */
destroy_querylist(&dbi->allnodes_q);
destroy_querylist(&dbi->allowxfr_q);
destroy_querylist(&dbi->authority_q);
destroy_querylist(&dbi->findzone_q);
destroy_querylist(&dbi->countzone_q);
destroy_querylist(&dbi->lookup_q);
/* get rid of the mutex */
(void) dlz_mutex_destroy(&dbi->lock);
/* return, and detach the memory */
free(dbi);
}

View file

@ -0,0 +1,20 @@
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)

View file

@ -0,0 +1,116 @@
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include "dlz_minimal.h"
#include "dir.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;
case ENOENT:
result = ISC_R_FILENOTFOUND;
case EACCES:
case EPERM:
result = ISC_R_NOPERM;
case ENOMEM:
result = ISC_R_NOMEMORY;
default:
result = ISC_R_UNEXPECTED;
}
}
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);
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
#include <sys/types.h>
#include <dirent.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);

View file

@ -0,0 +1,979 @@
/*
* Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
*
* 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.
*
* 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 ROB BUTLER
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* ROB BUTLER 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.
*/
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2001 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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 <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "dlz_minimal.h"
#include "dlz_list.h"
#include "dir.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 isc_boolean_t
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 (ISC_FALSE);
/* '..', two dots together is not allowed. */
else if (input[i-1] == '.')
return (ISC_FALSE);
/* '.' is not allowed as last char */
if (i == len)
return (ISC_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 (ISC_FALSE);
}
/* everything ok. */
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 = 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;
isc_boolean_t isroot = ISC_FALSE;
/* special case for root zone */
if (strcmp(zone, ".") == 0)
isroot = ISC_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;
isc_boolean_t foundHost;
tmp[0] = '\0'; /* set 1st byte to '\0' so strcpy works right. */
host[0] = '\0';
foundHost = ISC_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 = ISC_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 = ISC_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 == ISC_FALSE) {
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 postive 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;
dir_list = 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) {
return (ISC_R_NOTFOUND);
}
/* 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
isc_result_t
dlz_findzonedb(void *dbdata, const char *name,
dns_clientinfomethods_t *methods,
dns_clientinfo_t *clientinfo)
#endif
{
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 (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
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
{
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 (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, ...)
{
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.");
return (ISC_R_FAILURE);
}
if (strlen(argv[5]) > 1) {
cd->log(ISC_LOG_ERROR,
"Filesystem driver can only "
"accept a single character for separator.");
return (ISC_R_FAILURE);
}
/* 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 '\\' ");
return (ISC_R_FAILURE);
}
/* 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 postive 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.");
/* if we allocated a config data object clean it up */
if (cd != NULL)
dlz_destroy(cd);
/* return error */
return (ISC_R_NOMEMORY);
}
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;
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
*
* 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.
*
* 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 ROB BUTLER
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* ROB BUTLER 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.
*/
#include <dlz_minimal.h>
#include <dlz_list.h>
#include <dlz_pthread.h>
#ifndef DLZ_DBI_H
#define DLZ_DBI_H 1
/*
* Types
*/
#define REQUIRE_CLIENT 0x01
#define REQUIRE_QUERY 0x02
#define REQUIRE_RECORD 0x04
#define REQUIRE_ZONE 0x08
typedef struct query_segment query_segment_t;
typedef DLZ_LIST(query_segment_t) query_list_t;
typedef struct dbinstance dbinstance_t;
typedef DLZ_LIST(dbinstance_t) db_list_t;
typedef struct driverinstance driverinstance_t;
/*%
* a query segment is all the text between our special tokens
* special tokens are %zone%, %record%, %client%
*/
struct query_segment {
void *cmd;
unsigned int strlen;
isc_boolean_t direct;
DLZ_LINK(query_segment_t) link;
};
/*%
* a database instance contains everything we need for running
* a query against the database. Using it each separate thread
* can dynamically construct a query and execute it against the
* database. The "instance_lock" and locking code in the driver's
* make sure no two threads try to use the same DBI at a time.
*/
struct dbinstance {
void *dbconn;
query_list_t *allnodes_q;
query_list_t *allowxfr_q;
query_list_t *authority_q;
query_list_t *findzone_q;
query_list_t *lookup_q;
query_list_t *countzone_q;
char *query_buf;
char *zone;
char *record;
char *client;
dlz_mutex_t lock;
DLZ_LINK(dbinstance_t) link;
};
/*
* Method declarations
*/
void
destroy_querylist(query_list_t **querylist);
isc_result_t
build_querylist(const char *query_str, char **zone, char **record,
char **client, query_list_t **querylist, unsigned int flags,
log_t log);
char *
build_querystring(query_list_t *querylist);
isc_result_t
build_dbinstance(const char *allnodes_str, const char *allowxfr_str,
const char *authority_str, const char *findzone_str,
const char *lookup_str, const char *countzone_str,
dbinstance_t **dbi, log_t log);
void
destroy_dbinstance(dbinstance_t *dbi);
char *
get_parameter_value(const char *input, const char* key);
#endif /* DLZ_DBI_H */

View file

@ -0,0 +1,49 @@
/*
* Copyright (C) 2004, 2006, 2007, 2011-2013 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1997-2002 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
#ifndef DLZ_LIST_H
#define DLZ_LIST_H 1
#define DLZ_LIST(type) struct { type *head, *tail; }
#define DLZ_LIST_INIT(list) \
do { (list).head = NULL; (list).tail = NULL; } while (0)
#define DLZ_LINK(type) struct { type *prev, *next; }
#define DLZ_LINK_INIT(elt, link) \
do { \
(elt)->link.prev = (void *)(-1); \
(elt)->link.next = (void *)(-1); \
} while (0)
#define DLZ_LIST_HEAD(list) ((list).head)
#define DLZ_LIST_TAIL(list) ((list).tail)
#define DLZ_LIST_APPEND(list, elt, link) \
do { \
if ((list).tail != NULL) \
(list).tail->link.next = (elt); \
else \
(list).head = (elt); \
(elt)->link.prev = (list).tail; \
(elt)->link.next = NULL; \
(list).tail = (elt); \
} while (0)
#define DLZ_LIST_PREV(elt, link) ((elt)->link.prev)
#define DLZ_LIST_NEXT(elt, link) ((elt)->link.next)
#endif /* DLZ_LIST_H */

View file

@ -23,6 +23,9 @@
* tree.
*/
#ifndef DLZ_MINIMAL_H
#define DLZ_MINIMAL_H 1
#include <sys/types.h>
#include <sys/socket.h>
#ifdef ISC_PLATFORM_HAVESYSUNH
@ -45,8 +48,10 @@ typedef uint32_t dns_ttl_t;
#define DLZ_DLOPEN_AGE 0
#endif
/* return this in flags to dlz_version() if thread safe */
/* return these in flags from dlz_version() */
#define DNS_SDLZFLAG_THREADSAFE 0x00000001U
#define DNS_SDLZFLAG_RELATIVEOWNER 0x00000002U
#define DNS_SDLZFLAG_RELATIVERDATA 0x00000004U
/* result codes */
#define ISC_R_SUCCESS 0
@ -57,6 +62,9 @@ typedef uint32_t dns_ttl_t;
#define ISC_R_FAILURE 25
#define ISC_R_NOTIMPLEMENTED 27
#define ISC_R_NOMORE 29
#define ISC_R_INVALIDFILE 30
#define ISC_R_UNEXPECTED 34
#define ISC_R_FILENOTFOUND 38
/* boolean values */
#define ISC_TRUE 1
@ -68,6 +76,7 @@ typedef uint32_t dns_ttl_t;
#define ISC_LOG_WARNING (-3)
#define ISC_LOG_ERROR (-4)
#define ISC_LOG_CRITICAL (-5)
#define ISC_LOG_DEBUG(level) (level)
/* other useful definitions */
#define UNUSED(x) (void)(x)
@ -196,6 +205,13 @@ dlz_lookup(const char *zone, const char *name, void *dbdata,
dns_clientinfo_t *clientinfo);
#endif /* DLZ_DLOPEN_VERSION */
/*
* dlz_authority() is optional if dlz_lookup() supplies
* authority information (i.e., SOA, NS) for the dns record
*/
isc_result_t
dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup);
/*
* dlz_allowzonexfr() is optional, and should be supplied if you want to
* support zone transfers
@ -269,3 +285,5 @@ dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata,
isc_result_t
dlz_delrdataset(const char *name, const char *type, void *dbdata,
void *version);
#endif /* DLZ_MINIMAL_H */

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
#ifndef DLZ_PTHREAD_H
#define DLZ_PTHREAD_H 1
#ifndef PTHREADS
#define PTHREADS 1
#endif
#ifdef PTHREADS
#define dlz_mutex_t pthread_mutex_t
#define dlz_mutex_init pthread_mutex_init
#define dlz_mutex_destroy pthread_mutex_destroy
#define dlz_mutex_trylock pthread_mutex_trylock
#define dlz_mutex_unlock pthread_mutex_unlock
#else /* !PTHREADS */
#define dlz_mutex_t void
#define dlz_mutex_init(a, b) (0)
#define dlz_mutex_destroy(a) (0)
#define dlz_mutex_trylock(a) (0)
#define dlz_mutex_unlock(a) (0)
#endif
#endif /* DLZ_PTHREAD_H */

View file

@ -0,0 +1,21 @@
prefix = /usr
libdir = $(prefix)/lib/bind9
CFLAGS=-fPIC -g -I../include
LDAP_LIBS=-lldap
all: dlz_ldap_dynamic.so
dlz_dbi.o: ../common/dlz_dbi.c
$(CC) $(CFLAGS) -c ../common/dlz_dbi.c
dlz_ldap_dynamic.so: dlz_ldap_dynamic.c dlz_dbi.o
$(CC) $(CFLAGS) -shared -o dlz_ldap_dynamic.so \
dlz_ldap_dynamic.c dlz_dbi.o $(LDAP_LIBS)
clean:
rm -f dlz_ldap_dynamic.so *.o
install: dlz_ldap_dynamic.so
mkdir -p $(DESTDIR)$(libdir)
install dlz_ldap_dynamic.so $(DESTDIR)$(libdir)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
These files were used for testing on Ubuntu Linux using OpenLDAP.
- Move aside /etc/ldap/slapd.d
- Move slapd.conf to /etc/ldap
- Move dlz.schema to /etc/ldap/schema/dlz.schema
- Run "/etc/init.d/slapd restart"
- Run "ldapadd -x -f example.ldif -D 'cn=Manager,o=bind-dlz' -w secret"
LDAP server is now loaded with example.com data from the file example.ldif

View file

@ -0,0 +1,187 @@
#
#
# 1.3.6.1.4.1.18420.1.1.X is reserved for attribute types declared by the DLZ project.
# 1.3.6.1.4.1.18420.1.2.X is reserved for object classes declared by the DLZ project.
# 1.3.6.1.4.1.18420.1.3.X is reserved for PRIVATE extensions to the DLZ attribute
# types and object classes that may be needed by end users
# to add security, etc. Attributes and object classes using
# this OID MUST NOT be published outside of an organization
# except to offer them for consideration to become part of the
# standard attributes and object classes published by the DLZ project.
attributetype ( 1.3.6.1.4.1.18420.1.1.10
NAME 'dlzZoneName'
DESC 'DNS zone name - domain name not including host name'
SUP name
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.20
NAME 'dlzHostName'
DESC 'Host portion of a domain name'
SUP name
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.30
NAME 'dlzData'
DESC 'Data for the resource record'
SUP name
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.40
NAME 'dlzType'
DESC 'DNS record type - A, SOA, NS, MX, etc...'
SUP name
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.50
NAME 'dlzSerial'
DESC 'SOA record serial number'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.60
NAME 'dlzRefresh'
DESC 'SOA record refresh time in seconds'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.70
NAME 'dlzRetry'
DESC 'SOA retry time in seconds'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.80
NAME 'dlzExpire'
DESC 'SOA expire time in seconds'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.90
NAME 'dlzMinimum'
DESC 'SOA minimum time in seconds'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.100
NAME 'dlzAdminEmail'
DESC 'E-mail address of person responsible for this zone - @ should be replaced with . (period)'
SUP name
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.110
NAME 'dlzPrimaryNS'
DESC 'Primary name server for this zone - should be host name not IP address'
SUP name
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.120
NAME 'dlzIPAddr'
DESC 'IP address - IPV4 should be in dot notation xxx.xxx.xxx.xxx IPV6 should be in colon notation xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{40}
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.130
NAME 'dlzCName'
DESC 'DNS cname'
SUP name
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.140
NAME 'dlzPreference'
DESC 'DNS MX record preference. Lower numbers have higher preference'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.150
NAME 'dlzTTL'
DESC 'DNS time to live - how long this record can be cached by caching DNS servers'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.18420.1.1.160
NAME 'dlzRecordID'
DESC 'Unique ID for each DLZ resource record'
SUP name
SINGLE-VALUE )
#------------------------------------------------------------------------------
# Object class definitions
#------------------------------------------------------------------------------
objectclass ( 1.3.6.1.4.1.18420.1.2.10
NAME 'dlzZone'
DESC 'Zone name portion of a domain name'
SUP top STRUCTURAL
MUST ( objectclass $ dlzZoneName ) )
objectclass ( 1.3.6.1.4.1.18420.1.2.20
NAME 'dlzHost'
DESC 'Host name portion of a domain name'
SUP top STRUCTURAL
MUST ( objectclass $ dlzHostName ) )
objectclass ( 1.3.6.1.4.1.18420.1.2.30
NAME 'dlzAbstractRecord'
DESC 'Data common to all DNS record types'
SUP top ABSTRACT
MUST ( objectclass $ dlzRecordID $ dlzHostName $ dlzType $ dlzTTL ) )
objectclass ( 1.3.6.1.4.1.18420.1.2.40
NAME 'dlzGenericRecord'
DESC 'Generic DNS record - useful when a specific object class has not been defined for a DNS record'
SUP dlzAbstractRecord STRUCTURAL
MUST ( dlzData ) )
objectclass ( 1.3.6.1.4.1.18420.1.2.50
NAME 'dlzARecord'
DESC 'DNS A record'
SUP dlzAbstractrecord STRUCTURAL
MUST ( dlzIPAddr ) )
objectclass ( 1.3.6.1.4.1.18420.1.2.60
NAME 'dlzNSRecord'
DESC 'DNS NS record'
SUP dlzGenericRecord STRUCTURAL )
objectclass ( 1.3.6.1.4.1.18420.1.2.70
NAME 'dlzMXRecord'
DESC 'DNS MX record'
SUP dlzGenericRecord STRUCTURAL
MUST ( dlzPreference ) )
objectclass ( 1.3.6.1.4.1.18420.1.2.80
NAME 'dlzSOARecord'
DESC 'DNS SOA record'
SUP dlzAbstractRecord STRUCTURAL
MUST ( dlzSerial $ dlzRefresh $ dlzRetry
$ dlzExpire $ dlzMinimum $ dlzAdminEmail $ dlzPrimaryNS ) )
objectclass ( 1.3.6.1.4.1.18420.1.2.90
NAME 'dlzTextRecord'
DESC 'Text data with spaces should be wrapped in double quotes'
SUP dlzGenericRecord STRUCTURAL )
objectclass ( 1.3.6.1.4.1.18420.1.2.100
NAME 'dlzPTRRecord'
DESC 'DNS PTR record'
SUP dlzGenericRecord STRUCTURAL )
objectclass ( 1.3.6.1.4.1.18420.1.2.110
NAME 'dlzCNameRecord'
DESC 'DNS CName record'
SUP dlzGenericRecord STRUCTURAL )
objectclass ( 1.3.6.1.4.1.18420.1.2.120
NAME 'dlzXFR'
DESC 'Host allowed to perform zone transfer'
SUP top STRUCTURAL
MUST ( objectclass $ dlzRecordID $ dlzIPAddr ) )

View file

@ -0,0 +1,168 @@
# server suffix - o=bind-dlz
dn: o=bind-dlz
objectclass: organization
o: bind-dlz
dn: ou=dns,o=bind-dlz
objectclass: organizationalUnit
ou: dns
dn: dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzZone
dlzZoneName: example.com
dn: dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzHost
dlzHostName: @
dn: dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzHost
dlzHostName: www
dn: dlzHostName=mail,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzHost
dlzHostName: mail
dn: dlzHostName=backup,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzHost
dlzHostName: backup
dn: dlzHostName=ns1,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzHost
dlzHostName: ns1
dn: dlzHostName=ns2,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzHost
dlzHostName: ns2
dn: dlzHostName=~,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzHost
dlzHostName: ~
dn: dlzRecordID=1,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzGenericRecord
dlzRecordID: 1
dlzHostName: @
dlzType: txt
dlzData: "this is a text record"
dlzTTL: 10
dn: dlzRecordID=2,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzARecord
dlzRecordID: 2
dlzHostName: www
dlzType: a
dlzIPAddr: 192.168.0.1
dlzTTL: 10
dn: dlzRecordID=3,dlzHostName=mail,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzARecord
dlzRecordID: 3
dlzHostName: mail
dlzType: a
dlzIPAddr: 192.168.0.2
dlzTTL: 10
dn: dlzRecordID=4,dlzHostName=backup,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzARecord
dlzRecordID: 4
dlzHostName: backup
dlzType: a
dlzIPAddr: 192.168.0.3
dlzTTL: 10
dn: dlzRecordID=5,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzMXRecord
dlzRecordID: 5
dlzHostName: @
dlzType: mx
dlzData: mail
dlzPreference: 20
dlzTTL: 10
dn: dlzRecordID=6,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzMXRecord
dlzRecordID: 6
dlzHostName: @
dlzType: mx
dlzData: backup
dlzPreference: 40
dlzTTL: 10
dn: dlzRecordID=7,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzMXRecord
dlzRecordID: 7
dlzHostName: www
dlzType: mx
dlzData: backup
dlzPreference: 40
dlzTTL: 10
dn: dlzRecordID=8,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzMXRecord
dlzRecordID: 8
dlzHostName: www
dlzType: mx
dlzData: mail
dlzPreference: 20
dlzTTL: 10
dn: dlzRecordID=9,dlzHostName=ns1,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzARecord
dlzRecordID: 9
dlzHostName: ns1
dlzType: a
dlzIPAddr: 192.168.0.4
dlzTTL: 10
dn: dlzRecordID=10,dlzHostName=ns2,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzARecord
dlzRecordID: 10
dlzHostName: ns2
dlzType: a
dlzIPAddr: 192.168.0.5
dlzTTL: 10
dn: dlzRecordID=11,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzSOARecord
dlzRecordID: 11
dlzHostName: @
dlzType: soa
dlzSerial: 2
dlzRefresh: 2800
dlzRetry: 7200
dlzExpire: 604800
dlzMinimum: 86400
dlzAdminEmail: root.example.com.
dlzPrimaryns: ns1.example.com.
dlzTTL: 10
dn: dlzRecordID=12,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzNSRecord
dlzRecordID: 12
dlzHostName: @
dlzType: ns
dlzData: ns1.example.com.
dlzTTL: 10
dn: dlzRecordID=13,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzNSRecord
dlzRecordID: 13
dlzHostName: @
dlzType: ns
dlzData: ns2
dlzTTL: 10
dn: dlzRecordID=14,dlzHostName=~,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzARecord
dlzRecordID: 14
dlzHostName: ~
dlzType: a
dlzIPAddr: 192.168.0.250
dlzTTL: 10
dn: dlzRecordID=15,dlzZoneName=example.com,ou=dns,o=bind-dlz
objectclass: dlzXFR
dlzRecordID: 15
dlzIPAddr: 127.0.0.1

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
controls { };
options {
directory ".";
port 5300;
pid-file "named.pid";
session-keyfile "session.key";
listen-on { any; };
listen-on-v6 { none; };
recursion no;
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-md5;
};
controls {
inet 127.0.0.1 port 9953 allow { any; } keys { rndc_key; };
};
dlz "test" {
database "dlopen ../dlz_ldap_dynamic.so 2
v3 simple {cn=Manager,o=bind-dlz} {secret} {127.0.0.1}
ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz???objectclass=dlzZone
ldap:///dlzHostName=$record$,dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzPreference,dlzData,dlzIPAddr?sub?(&(objectclass=dlzAbstractRecord)(!(dlzType=soa)))
ldap:///dlzHostName=@,dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzData,dlzPrimaryNS,dlzAdminEmail,dlzSerial,dlzRefresh,dlzRetry,dlzExpire,dlzMinimum?sub?(&(objectclass=dlzAbstractRecord)(dlzType=soa))
ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzHostName,dlzPreference,dlzData,dlzIPAddr,dlzPrimaryNS,dlzAdminEmail,dlzSerial,dlzRefresh,dlzRetry,dlzExpire,dlzMinimum?sub?(&(objectclass=dlzAbstractRecord)(!(dlzType=soa)))
ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz??sub?(&(objectclass=dlzXFR)(dlzIPAddr=$client$))";
};

View file

@ -0,0 +1,44 @@
# this is the full path to the core.schema
include /etc/ldap/schema/core.schema
# this is the full path to the dlz.schema
include /etc/ldap/schema/dlz.schema
# these files hold the slapd process ID and program args when
# slapd is started.
pidfile /var/run/slapd/slapd.pid
argsfile /var/run/slapd/slapd.args
modulepath /usr/lib/ldap
moduleload back_hdb
# this allows ldap version 2 connections. You should comment
# it out if you don't need ldap version 2.
allow bind_v2
# this sets up the Berkeley DB database backend for LDAP to use.
database hdb
# This is the root of the LDAP server. You still need to add
# an entry to this location via a LDIF file, or you won't be
# able to add anything else into the LDAP server.
suffix "o=bind-dlz"
# this is the "username" you have to use when connecting to the
# ldap server to make updates. Type the whole thing exactly
# as you see it as a parameter to ldapadd.
rootdn "cn=Manager,o=bind-dlz"
# this is the "password" you have to use when connecting to the
# ldap server to make updates.
rootpw secret
# this is the directory that the LDAP server will create the
# Berkeley DB backend in.
directory /var/lib/ldap
# this just adds some indexing to the LDAP server.
# probably should have more to better optimize DLZ LDAP searches.
index cn,sn,uid pres,eq
index objectClass eq

View file

@ -0,0 +1,20 @@
prefix = /usr
libdir = $(prefix)/lib/bind9
CFLAGS=-fPIC -g -I../include
all: dlz_wildcard_dynamic.so
dlz_dbi.o: ../common/dlz_dbi.c
$(CC) $(CFLAGS) -c ../common/dlz_dbi.c
dlz_wildcard_dynamic.so: dlz_wildcard_dynamic.c dlz_dbi.o
$(CC) $(CFLAGS) -shared -o dlz_wildcard_dynamic.so \
dlz_wildcard_dynamic.c dlz_dbi.o
clean:
rm -f dlz_wildcard_dynamic.so *.o
install: dlz_wildcard_dynamic.so
mkdir -p $(DESTDIR)$(libdir)
install dlz_wildcard_dynamic.so $(DESTDIR)$(libdir)

View file

@ -0,0 +1,31 @@
The "wildcard" DLZ module provides a "template" zone for domains matching
a wildcard name. For example, the following DLZ configuration would match
any zone name containing the string "example" and ending with .com, such
as "thisexample.com", "exampleofthat.com", or "anexampleoftheotherthing.com".
dlz "test" {
database "dlopen ../dlz_wildcard_dynamic.so
*example*.com 10.53.* 1800
@ 3600 SOA {ns3.example.nil. support.example.nil. 42 14400 7200 2592000 600}
@ 3600 NS ns3.example.nil.
@ 3600 NS ns4.example.nil.
@ 3600 NS ns8.example.nil.
@ 3600 MX {5 mail.example.nil.}
ftp 86400 A 192.0.0.1
sql 86400 A 192.0.0.2
tmp {} A 192.0.0.3
www 86400 A 192.0.0.3
www 86400 AAAA ::1
txt 300 TXT {\"you requested $record$ in $zone$\"}
* 86400 A 192.0.0.100";
};
For any zone name matchin the wildcard, it would return the data from
the template. "$zone$" is replaced with zone name: i.e., the shortest
possible string of labels in the query name that matches the wildcard.
"$record$" is replaced with the remainder of the query name. In the
example above, a query for "txt.thisexample.com/TXT" would return the
string "you requested txt in thisexample.com".
Any client whose source address matches the second wildcard ("10.53.*")
is allowed to request a zone transfer.

View file

@ -0,0 +1,738 @@
/*
* Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
* Copyright (C) 2012 Vadim Goncharov, Russia, vadim_nuclight@mail.ru.
*
* 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.
*
* 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 ROB BUTLER
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* ROB BUTLER 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.
*/
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2001 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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 wildcard DLZ module.
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <dlz_minimal.h>
#include <dlz_list.h>
#include <dlz_dbi.h>
#include <ctype.h>
#define DE_CONST(konst, var) \
do { \
union { const void *k; void *v; } _u; \
_u.k = konst; \
var = _u.v; \
} while (0)
/* fnmatch() return values. */
#define FNM_NOMATCH 1 /* Match failed. */
/* fnmatch() flags. */
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
#define FNM_IGNORECASE FNM_CASEFOLD
#define FNM_FILE_NAME FNM_PATHNAME
/*
* Our data structures.
*/
typedef struct named_rr nrr_t;
typedef DLZ_LIST(nrr_t) rr_list_t;
typedef struct config_data {
char *zone_pattern;
char *axfr_pattern;
rr_list_t rrs_list;
char *zone;
char *record;
char *client;
/* 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;
struct named_rr {
char *name;
char *type;
int ttl;
query_list_t *data;
DLZ_LINK(nrr_t) link;
};
/*
* Forward references
*/
static int
rangematch(const char *, char, int, char **);
static int
fnmatch(const char *pattern, const char *string, int flags);
static void
b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr);
static const char *
shortest_match(const char *pattern, const char *string);
isc_result_t
dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
config_data_t *cd = (config_data_t *) dbdata;
isc_result_t result;
char *querystring = NULL;
nrr_t *nrec;
int i = 0;
DE_CONST(zone, cd->zone);
/* Write info message to log */
cd->log(ISC_LOG_DEBUG(1),
"dlz_wildcard allnodes called for zone '%s'", zone);
result = ISC_R_FAILURE;
nrec = DLZ_LIST_HEAD(cd->rrs_list);
while (nrec != NULL) {
cd->record = nrec->name;
querystring = build_querystring(nrec->data);
if (querystring == NULL) {
result = ISC_R_NOMEMORY;
goto done;
}
cd->log(ISC_LOG_DEBUG(2),
"dlz_wildcard allnodes entry num %d: calling "
"putnamedrr(name=%s type=%s ttl=%d qs=%s)",
i++, nrec->name, nrec->type, nrec->ttl, querystring);
result = cd->putnamedrr(allnodes, nrec->name, nrec->type,
nrec->ttl, querystring);
if (result != ISC_R_SUCCESS)
goto done;
nrec = DLZ_LIST_NEXT(nrec, link);
}
done:
cd->zone = NULL;
if (querystring != NULL)
free(querystring);
return (result);
}
isc_result_t
dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
config_data_t *cd = (config_data_t *) dbdata;
UNUSED(name);
/* Write info message to log */
cd->log(ISC_LOG_DEBUG(1),
"dlz_wildcard allowzonexfr called for client '%s'", client);
if (fnmatch(cd->axfr_pattern, client, FNM_CASEFOLD) == 0)
return (ISC_R_SUCCESS);
else
return (ISC_R_NOTFOUND);
}
#if DLZ_DLOPEN_VERSION < 3
isc_result_t
dlz_findzonedb(void *dbdata, const char *name)
#else
isc_result_t
dlz_findzonedb(void *dbdata, const char *name,
dns_clientinfomethods_t *methods,
dns_clientinfo_t *clientinfo)
#endif
{
config_data_t *cd = (config_data_t *) dbdata;
const char *p;
#if DLZ_DLOPEN_VERSION >= 3
UNUSED(methods);
UNUSED(clientinfo);
#endif
p = shortest_match(cd->zone_pattern, name);
if (p == NULL)
return (ISC_R_NOTFOUND);
/* Write info message to log */
cd->log(ISC_LOG_DEBUG(1),
"dlz_wildcard findzonedb matched '%s'", p);
return (ISC_R_SUCCESS);
}
#if DLZ_DLOPEN_VERSION == 1
isc_result_t
dlz_lookup(const char *zone, const char *name,
void *dbdata, dns_sdlzlookup_t *lookup)
#else
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
{
isc_result_t result;
config_data_t *cd = (config_data_t *) dbdata;
char *querystring = NULL;
const char *p;
char *namebuf;
nrr_t *nrec;
isc_boolean_t origin = ISC_TRUE;
#if DLZ_DLOPEN_VERSION >= 2
UNUSED(methods);
UNUSED(clientinfo);
#endif
p = shortest_match(cd->zone_pattern, zone);
if (p == NULL)
return (ISC_R_NOTFOUND);
DE_CONST(name, cd->record);
DE_CONST(p, cd->zone);
if ((p != zone) && (strcmp(name, "@") == 0 || strcmp(name, zone) == 0))
{
size_t len = p - zone;
namebuf = malloc(len);
strncpy(namebuf, zone, len - 1);
namebuf[len - 1] = '\0';
cd->record = namebuf;
origin = ISC_FALSE;
} else if (p == zone)
cd->record = "@";
/* Write info message to log */
cd->log(ISC_LOG_DEBUG(1),
"dlz_wildcard_dynamic: lookup for '%s' in '%s': "
"trying '%s' in '%s'",
name, zone, cd->record, cd->zone);
result = ISC_R_NOTFOUND;
nrec = DLZ_LIST_HEAD(cd->rrs_list);
while (nrec != NULL) {
nrr_t *next = DLZ_LIST_NEXT(nrec, link);
if (strcmp(cd->record, nrec->name) == 0) {
/* We handle authority data in dlz_authority() */
if (strcmp(nrec->type, "SOA") == 0 ||
strcmp(nrec->type, "NS") == 0)
{
nrec = next;
continue;
}
querystring = build_querystring(nrec->data);
if (querystring == NULL) {
result = ISC_R_NOMEMORY;
goto done;
}
result = cd->putrr(lookup, nrec->type,
nrec->ttl, querystring);
if (result != ISC_R_SUCCESS)
goto done;
fprintf(stderr, "setting result to success on %s/%s\n", nrec->name, nrec->type);
result = ISC_R_SUCCESS;
free(querystring);
querystring = NULL;
}
nrec = next;
}
done:
cd->zone = NULL;
cd->record = NULL;
if (querystring != NULL)
free(querystring);
return (result);
}
isc_result_t
dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) {
isc_result_t result;
config_data_t *cd = (config_data_t *) dbdata;
char *querystring = NULL;
nrr_t *nrec;
const char *p, *name = "@";
p = shortest_match(cd->zone_pattern, zone);
if (p == NULL)
return (ISC_R_NOTFOUND);
DE_CONST(p, cd->zone);
/* Write info message to log */
cd->log(ISC_LOG_DEBUG(1),
"dlz_wildcard_dynamic: authority for '%s'", zone);
result = ISC_R_NOTFOUND;
nrec = DLZ_LIST_HEAD(cd->rrs_list);
while (nrec != NULL) {
isc_boolean_t origin;
if (strcmp("@", nrec->name) == 0) {
isc_result_t presult;
querystring = build_querystring(nrec->data);
if (querystring == NULL) {
result = ISC_R_NOMEMORY;
goto done;
}
presult = cd->putrr(lookup, nrec->type,
nrec->ttl, querystring);
if (presult != ISC_R_SUCCESS) {
result = presult;
goto done;
}
result = ISC_R_SUCCESS;
free(querystring);
querystring = NULL;
}
nrec = DLZ_LIST_NEXT(nrec, link);
}
done:
cd->zone = NULL;
if (querystring != NULL)
free(querystring);
return (result);
}
static void
destroy_rrlist(config_data_t *cd) {
nrr_t *trec, *nrec;
nrec = DLZ_LIST_HEAD(cd->rrs_list);
while (nrec != NULL) {
trec = nrec;
destroy_querylist(&trec->data);
if (trec->name != NULL)
free(trec->name);
if (trec->type != NULL)
free(trec->type);
trec->name = trec->type = NULL;
/* Get the next record, before we destroy this one. */
nrec = DLZ_LIST_NEXT(nrec, link);
free(trec);
}
}
isc_result_t
dlz_create(const char *dlzname, unsigned int argc, char *argv[],
void **dbdata, ...)
{
config_data_t *cd;
char *endp;
int i, def_ttl;
nrr_t *trec = NULL;
isc_result_t result;
const char *helper_name;
va_list ap;
if (argc < 8 || argc % 4 != 0)
return (ISC_R_FAILURE);
cd = calloc(1, sizeof(config_data_t));
if (cd == NULL)
return (ISC_R_NOMEMORY);
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);
/*
* Write info message to log
*/
cd->log(ISC_LOG_INFO,
"Loading '%s' using DLZ_wildcard driver. "
"Zone: %s, AXFR allowed for: %s, $TTL: %s",
dlzname, argv[1], argv[2], argv[3]);
/* initialize the records list here to simplify cleanup */
DLZ_LIST_INIT(cd->rrs_list);
cd->zone_pattern = strdup(argv[1]);
if (cd->zone_pattern == NULL)
goto cleanup;
cd->axfr_pattern = strdup(argv[2]);
if (cd->axfr_pattern == NULL)
goto cleanup;
def_ttl = strtol(argv[3], &endp, 10);
if (*endp != '\0' || def_ttl < 0) {
def_ttl = 3600;
cd->log(ISC_LOG_ERROR, "default TTL invalid, using 3600");
}
for (i = 4; i < argc; i += 4) {
result = ISC_R_NOMEMORY;
trec = malloc(sizeof(nrr_t));
if (trec == NULL)
goto full_cleanup;
memset(trec, 0, sizeof(nrr_t));
/* Initialize the record link */
DLZ_LINK_INIT(trec, link);
/* Append the record to the list */
DLZ_LIST_APPEND(cd->rrs_list, trec, link);
trec->name = strdup(argv[i]);
if (trec->name == NULL)
goto full_cleanup;
trec->type = strdup(argv[i + 2]);
if (trec->type == NULL)
goto full_cleanup;
trec->ttl = strtol(argv[i + 1], &endp, 10);
if (argv[i + 1][0] == '\0' || *endp != '\0' || trec->ttl < 0)
trec->ttl = def_ttl;
result = build_querylist(argv[i + 3], &cd->zone,
&cd->record, &cd->client,
&trec->data, 0, cd->log);
/* If unsuccessful, log err msg and cleanup */
if (result != ISC_R_SUCCESS) {
cd->log(ISC_LOG_ERROR,
"Could not build RR data list at argv[%d]",
i + 3);
goto full_cleanup;
}
}
*dbdata = cd;
return (ISC_R_SUCCESS);
full_cleanup:
destroy_rrlist(cd);
cleanup:
if (cd->zone_pattern != NULL)
free(cd->zone_pattern);
if (cd->axfr_pattern != NULL)
free(cd->axfr_pattern);
free(cd);
return (result);
}
void
dlz_destroy(void *dbdata) {
config_data_t *cd = (config_data_t *) dbdata;
/*
* Write debugging message to log
*/
cd->log(ISC_LOG_DEBUG(2), "Unloading DLZ_wildcard driver.");
destroy_rrlist(cd);
free(cd->zone_pattern);
free(cd->axfr_pattern);
free(cd);
}
/*
* Return the version of the API
*/
int
dlz_version(unsigned int *flags) {
UNUSED(flags);
/* XXX: ok to set DNS_SDLZFLAG_THREADSAFE here? */
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;
}
static const char *
shortest_match(const char *pattern, const char *string) {
const char *p = string;
if (pattern == NULL || p == NULL || *p == '\0')
return (NULL);
p += strlen(p);
while (p-- > string) {
if (*p == '.') {
if (fnmatch(pattern, p + 1, FNM_CASEFOLD) == 0)
return (p + 1);
}
}
if (fnmatch(pattern, string, FNM_CASEFOLD) == 0)
return (string);
return (NULL);
}
/*
* The helper functions stolen from the FreeBSD kernel (sys/libkern/fnmatch.c).
*
* Why don't we use fnmatch(3) from libc? Because it is not thread-safe, and
* it is not thread-safe because it supports multibyte characters. But here,
* in BIND, we want to be thread-safe and don't need multibyte - DNS names are
* always ASCII.
*/
#define EOS '\0'
#define RANGE_MATCH 1
#define RANGE_NOMATCH 0
#define RANGE_ERROR (-1)
static int
fnmatch(const char *pattern, const char *string, int flags) {
const char *stringstart;
char *newp;
char c, test;
for (stringstart = string;;)
switch (c = *pattern++) {
case EOS:
if ((flags & FNM_LEADING_DIR) && *string == '/')
return (0);
return (*string == EOS ? 0 : FNM_NOMATCH);
case '?':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && (flags & FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
++string;
break;
case '*':
c = *pattern;
/* Collapse multiple stars. */
while (c == '*')
c = *++pattern;
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
/* Optimize for pattern with * at end or before /. */
if (c == EOS)
if (flags & FNM_PATHNAME)
return ((flags & FNM_LEADING_DIR) ||
index(string, '/') == NULL ?
0 : FNM_NOMATCH);
else
return (0);
else if (c == '/' && flags & FNM_PATHNAME) {
if ((string = index(string, '/')) == NULL)
return (FNM_NOMATCH);
break;
}
/* General case, use recursion. */
while ((test = *string) != EOS) {
if (!fnmatch(pattern, string,
flags & ~FNM_PERIOD))
return (0);
if (test == '/' && flags & FNM_PATHNAME)
break;
++string;
}
return (FNM_NOMATCH);
case '[':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && (flags & FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
switch (rangematch(pattern, *string, flags, &newp)) {
case RANGE_ERROR:
goto norm;
case RANGE_MATCH:
pattern = newp;
break;
case RANGE_NOMATCH:
return (FNM_NOMATCH);
}
++string;
break;
case '\\':
if (!(flags & FNM_NOESCAPE)) {
if ((c = *pattern++) == EOS) {
c = '\\';
--pattern;
}
}
/* FALLTHROUGH */
default:
norm:
if (c == *string)
;
else if ((flags & FNM_CASEFOLD) &&
(tolower((unsigned char)c) ==
tolower((unsigned char)*string)))
;
else
return (FNM_NOMATCH);
string++;
break;
}
/* NOTREACHED */
}
static int
rangematch(const char *pattern, char test, int flags, char **newp) {
int negate, ok;
char c, c2;
/*
* A bracket expression starting with an unquoted circumflex
* character produces unspecified results (IEEE 1003.2-1992,
* 3.13.2). This implementation treats it like '!', for
* consistency with the regular expression syntax.
* J.T. Conklin (conklin@ngai.kaleida.com)
*/
if ( (negate = (*pattern == '!' || *pattern == '^')) )
++pattern;
if (flags & FNM_CASEFOLD)
test = tolower((unsigned char)test);
/*
* A right bracket shall lose its special meaning and represent
* itself in a bracket expression if it occurs first in the list.
* -- POSIX.2 2.8.3.2
*/
ok = 0;
c = *pattern++;
do {
if (c == '\\' && !(flags & FNM_NOESCAPE))
c = *pattern++;
if (c == EOS)
return (RANGE_ERROR);
if (c == '/' && (flags & FNM_PATHNAME))
return (RANGE_NOMATCH);
if (flags & FNM_CASEFOLD)
c = tolower((unsigned char)c);
if (*pattern == '-'
&& (c2 = *(pattern+1)) != EOS && c2 != ']') {
pattern += 2;
if (c2 == '\\' && !(flags & FNM_NOESCAPE))
c2 = *pattern++;
if (c2 == EOS)
return (RANGE_ERROR);
if (flags & FNM_CASEFOLD)
c2 = tolower((unsigned char)c2);
if (c <= test && test <= c2)
ok = 1;
} else if (c == test)
ok = 1;
} while ((c = *pattern++) != ']');
*newp = (char *)(uintptr_t)pattern;
return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
controls { };
options {
directory ".";
port 5300;
pid-file "named.pid";
session-keyfile "session.key";
listen-on { any; };
listen-on-v6 { none; };
recursion no;
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-md5;
};
controls {
inet 127.0.0.1 port 9953 allow { any; } keys { rndc_key; };
};
/*
* This will match any zone name containing the string "example" and
* ending with .com, such as "thisexample.com", "exampleofthat.com",
* or "anexampleoftheotherthing.com".
*/
dlz "test" {
database "dlopen ../dlz_wildcard_dynamic.so
*example*.com 10.53.* 1800
@ 3600 SOA {ns3.example.nil. support.example.nil. 42 14400 7200 2592000 600}
@ 3600 NS ns3.example.nil.
@ 3600 NS ns4.example.nil.
@ 3600 NS ns8.example.nil.
@ 3600 MX {5 mail.example.nil.}
ftp 86400 A 192.0.0.1
sql 86400 A 192.0.0.2
tmp {} A 192.0.0.3
www 86400 A 192.0.0.3
www 86400 AAAA ::1
txt 300 TXT {\"you requested $record$ in $zone$\"}
* 86400 A 192.0.0.100";
};

View file

@ -1205,7 +1205,6 @@ settask(dns_db_t *db, isc_task_t *task) {
UNUSED(task);
}
/*
* getoriginnode() is used by the update code to find the
* dns_rdatatype_dnskey record for a zone
@ -1222,7 +1221,7 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
result = findnodeext(db, &sdlz->common.origin, ISC_FALSE,
NULL, NULL, nodep);
if (result != ISC_R_SUCCESS)
sdlz_log(ISC_LOG_ERROR, "sdlz getoriginnode failed : %s",
sdlz_log(ISC_LOG_ERROR, "sdlz getoriginnode failed: %s",
isc_result_totext(result));
return (result);
}