summaryrefslogtreecommitdiffstats
path: root/contrib/dlz/modules/wildcard
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
commitea648e70a989cca190cd7403fe892fd2dcc290b4 (patch)
treee2b6b1c647da68b0d4d66082835e256eb30970e8 /contrib/dlz/modules/wildcard
parentInitial commit. (diff)
downloadbind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.tar.xz
bind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.zip
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/dlz/modules/wildcard')
-rw-r--r--contrib/dlz/modules/wildcard/Makefile20
-rw-r--r--contrib/dlz/modules/wildcard/README31
-rw-r--r--contrib/dlz/modules/wildcard/dlz_wildcard_dynamic.c730
-rw-r--r--contrib/dlz/modules/wildcard/testing/named.conf58
4 files changed, 839 insertions, 0 deletions
diff --git a/contrib/dlz/modules/wildcard/Makefile b/contrib/dlz/modules/wildcard/Makefile
new file mode 100644
index 0000000..20a5d4e
--- /dev/null
+++ b/contrib/dlz/modules/wildcard/Makefile
@@ -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)
diff --git a/contrib/dlz/modules/wildcard/README b/contrib/dlz/modules/wildcard/README
new file mode 100644
index 0000000..b19009b
--- /dev/null
+++ b/contrib/dlz/modules/wildcard/README
@@ -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.
diff --git a/contrib/dlz/modules/wildcard/dlz_wildcard_dynamic.c b/contrib/dlz/modules/wildcard/dlz_wildcard_dynamic.c
new file mode 100644
index 0000000..07b65d2
--- /dev/null
+++ b/contrib/dlz/modules/wildcard/dlz_wildcard_dynamic.c
@@ -0,0 +1,730 @@
+/*
+ * 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) 1999-2001, 2013, 2016 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * This provides the externally loadable wildcard DLZ module.
+ */
+
+#include <inttypes.h>
+#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;
+ bool origin = 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);
+ if (namebuf == NULL)
+ return (ISC_R_NOMEMORY);
+ strncpy(namebuf, zone, len - 1);
+ namebuf[len - 1] = '\0';
+ cd->record = namebuf;
+ origin = 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;
+
+ 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) {
+ bool 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]);
+ cd->axfr_pattern = strdup(argv[2]);
+ if (cd->zone_pattern == NULL || cd->axfr_pattern == NULL) {
+ result = ISC_R_NOMEMORY;
+ 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);
+}
diff --git a/contrib/dlz/modules/wildcard/testing/named.conf b/contrib/dlz/modules/wildcard/testing/named.conf
new file mode 100644
index 0000000..0192e18
--- /dev/null
+++ b/contrib/dlz/modules/wildcard/testing/named.conf
@@ -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";
+};