Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / contrib / dlz / modules / wildcard / dlz_wildcard_dynamic.c
blob2c250066c4dfddc1f0af7e939dd8262aa599243c
1 /* $NetBSD: dlz_wildcard_dynamic.c,v 1.3 2014/12/10 04:37:55 christos Exp $ */
3 /*
4 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
5 * Copyright (C) 2012 Vadim Goncharov, Russia, vadim_nuclight@mail.ru.
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
13 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
14 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
15 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
17 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
18 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
19 * USE OR PERFORMANCE OF THIS SOFTWARE.
21 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
22 * conceived and contributed by Rob Butler.
24 * Permission to use, copy, modify, and distribute this software for any
25 * purpose with or without fee is hereby granted, provided that the
26 * above copyright notice and this permission notice appear in all
27 * copies.
29 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
30 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
32 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
33 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
34 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
35 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
36 * USE OR PERFORMANCE OF THIS SOFTWARE.
40 * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
41 * Copyright (C) 1999-2001 Internet Software Consortium.
43 * Permission to use, copy, modify, and/or distribute this software for any
44 * purpose with or without fee is hereby granted, provided that the above
45 * copyright notice and this permission notice appear in all copies.
47 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
48 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
49 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
50 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
51 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
52 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
53 * PERFORMANCE OF THIS SOFTWARE.
57 * This provides the externally loadable wildcard DLZ module.
60 #include <stdio.h>
61 #include <string.h>
62 #include <stdarg.h>
63 #include <stdlib.h>
65 #include <dlz_minimal.h>
66 #include <dlz_list.h>
67 #include <dlz_dbi.h>
69 #include <ctype.h>
71 #define DE_CONST(konst, var) \
72 do { \
73 union { const void *k; void *v; } _u; \
74 _u.k = konst; \
75 var = _u.v; \
76 } while (/*CONSTCOND*/0)
78 /* fnmatch() return values. */
79 #define FNM_NOMATCH 1 /* Match failed. */
81 /* fnmatch() flags. */
82 #define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
83 #define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
84 #define FNM_PERIOD 0x04 /* Period must be matched by period. */
85 #define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
86 #define FNM_CASEFOLD 0x10 /* Case insensitive search. */
87 #define FNM_IGNORECASE FNM_CASEFOLD
88 #define FNM_FILE_NAME FNM_PATHNAME
91 * Our data structures.
94 typedef struct named_rr nrr_t;
95 typedef DLZ_LIST(nrr_t) rr_list_t;
97 typedef struct config_data {
98 char *zone_pattern;
99 char *axfr_pattern;
100 rr_list_t rrs_list;
101 char *zone;
102 char *record;
103 char *client;
105 /* Helper functions from the dlz_dlopen driver */
106 log_t *log;
107 dns_sdlz_putrr_t *putrr;
108 dns_sdlz_putnamedrr_t *putnamedrr;
109 dns_dlz_writeablezone_t *writeable_zone;
110 } config_data_t;
112 struct named_rr {
113 char *name;
114 char *type;
115 int ttl;
116 query_list_t *data;
117 DLZ_LINK(nrr_t) link;
121 * Forward references
123 static int
124 rangematch(const char *, char, int, char **);
126 static int
127 fnmatch(const char *pattern, const char *string, int flags);
129 static void
130 b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr);
132 static const char *
133 shortest_match(const char *pattern, const char *string);
135 isc_result_t
136 dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
137 config_data_t *cd = (config_data_t *) dbdata;
138 isc_result_t result;
139 char *querystring = NULL;
140 nrr_t *nrec;
141 int i = 0;
143 DE_CONST(zone, cd->zone);
145 /* Write info message to log */
146 cd->log(ISC_LOG_DEBUG(1),
147 "dlz_wildcard allnodes called for zone '%s'", zone);
149 result = ISC_R_FAILURE;
151 nrec = DLZ_LIST_HEAD(cd->rrs_list);
152 while (nrec != NULL) {
153 cd->record = nrec->name;
155 querystring = build_querystring(nrec->data);
157 if (querystring == NULL) {
158 result = ISC_R_NOMEMORY;
159 goto done;
162 cd->log(ISC_LOG_DEBUG(2),
163 "dlz_wildcard allnodes entry num %d: calling "
164 "putnamedrr(name=%s type=%s ttl=%d qs=%s)",
165 i++, nrec->name, nrec->type, nrec->ttl, querystring);
167 result = cd->putnamedrr(allnodes, nrec->name, nrec->type,
168 nrec->ttl, querystring);
169 if (result != ISC_R_SUCCESS)
170 goto done;
172 nrec = DLZ_LIST_NEXT(nrec, link);
175 done:
176 cd->zone = NULL;
178 if (querystring != NULL)
179 free(querystring);
181 return (result);
184 isc_result_t
185 dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
186 config_data_t *cd = (config_data_t *) dbdata;
188 UNUSED(name);
190 /* Write info message to log */
191 cd->log(ISC_LOG_DEBUG(1),
192 "dlz_wildcard allowzonexfr called for client '%s'", client);
194 if (fnmatch(cd->axfr_pattern, client, FNM_CASEFOLD) == 0)
195 return (ISC_R_SUCCESS);
196 else
197 return (ISC_R_NOTFOUND);
200 #if DLZ_DLOPEN_VERSION < 3
201 isc_result_t
202 dlz_findzonedb(void *dbdata, const char *name)
203 #else
204 isc_result_t
205 dlz_findzonedb(void *dbdata, const char *name,
206 dns_clientinfomethods_t *methods,
207 dns_clientinfo_t *clientinfo)
208 #endif
210 config_data_t *cd = (config_data_t *) dbdata;
211 const char *p;
213 #if DLZ_DLOPEN_VERSION >= 3
214 UNUSED(methods);
215 UNUSED(clientinfo);
216 #endif
218 p = shortest_match(cd->zone_pattern, name);
219 if (p == NULL)
220 return (ISC_R_NOTFOUND);
222 /* Write info message to log */
223 cd->log(ISC_LOG_DEBUG(1),
224 "dlz_wildcard findzonedb matched '%s'", p);
226 return (ISC_R_SUCCESS);
229 #if DLZ_DLOPEN_VERSION == 1
230 isc_result_t
231 dlz_lookup(const char *zone, const char *name,
232 void *dbdata, dns_sdlzlookup_t *lookup)
233 #else
234 isc_result_t
235 dlz_lookup(const char *zone, const char *name,
236 void *dbdata, dns_sdlzlookup_t *lookup,
237 dns_clientinfomethods_t *methods,
238 dns_clientinfo_t *clientinfo)
239 #endif
241 isc_result_t result;
242 config_data_t *cd = (config_data_t *) dbdata;
243 char *querystring = NULL;
244 const char *p;
245 char *namebuf;
246 nrr_t *nrec;
247 isc_boolean_t origin = ISC_TRUE;
249 #if DLZ_DLOPEN_VERSION >= 2
250 UNUSED(methods);
251 UNUSED(clientinfo);
252 #endif
254 p = shortest_match(cd->zone_pattern, zone);
255 if (p == NULL)
256 return (ISC_R_NOTFOUND);
258 DE_CONST(name, cd->record);
259 DE_CONST(p, cd->zone);
261 if ((p != zone) && (strcmp(name, "@") == 0 || strcmp(name, zone) == 0))
263 size_t len = p - zone;
264 namebuf = malloc(len);
265 strncpy(namebuf, zone, len - 1);
266 namebuf[len - 1] = '\0';
267 cd->record = namebuf;
268 origin = ISC_FALSE;
269 } else if (p == zone)
270 cd->record = "@";
272 /* Write info message to log */
273 cd->log(ISC_LOG_DEBUG(1),
274 "dlz_wildcard_dynamic: lookup for '%s' in '%s': "
275 "trying '%s' in '%s'",
276 name, zone, cd->record, cd->zone);
278 result = ISC_R_NOTFOUND;
279 nrec = DLZ_LIST_HEAD(cd->rrs_list);
280 while (nrec != NULL) {
281 nrr_t *next = DLZ_LIST_NEXT(nrec, link);
282 if (strcmp(cd->record, nrec->name) == 0) {
283 /* We handle authority data in dlz_authority() */
284 if (strcmp(nrec->type, "SOA") == 0 ||
285 strcmp(nrec->type, "NS") == 0)
287 nrec = next;
288 continue;
291 querystring = build_querystring(nrec->data);
292 if (querystring == NULL) {
293 result = ISC_R_NOMEMORY;
294 goto done;
297 result = cd->putrr(lookup, nrec->type,
298 nrec->ttl, querystring);
299 if (result != ISC_R_SUCCESS)
300 goto done;
302 result = ISC_R_SUCCESS;
304 free(querystring);
305 querystring = NULL;
307 nrec = next;
310 done:
311 cd->zone = NULL;
312 cd->record = NULL;
314 if (querystring != NULL)
315 free(querystring);
317 return (result);
320 isc_result_t
321 dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) {
322 isc_result_t result;
323 config_data_t *cd = (config_data_t *) dbdata;
324 char *querystring = NULL;
325 nrr_t *nrec;
326 const char *p, *name = "@";
328 p = shortest_match(cd->zone_pattern, zone);
329 if (p == NULL)
330 return (ISC_R_NOTFOUND);
332 DE_CONST(p, cd->zone);
334 /* Write info message to log */
335 cd->log(ISC_LOG_DEBUG(1),
336 "dlz_wildcard_dynamic: authority for '%s'", zone);
338 result = ISC_R_NOTFOUND;
339 nrec = DLZ_LIST_HEAD(cd->rrs_list);
340 while (nrec != NULL) {
341 isc_boolean_t origin;
342 if (strcmp("@", nrec->name) == 0) {
343 isc_result_t presult;
345 querystring = build_querystring(nrec->data);
346 if (querystring == NULL) {
347 result = ISC_R_NOMEMORY;
348 goto done;
351 presult = cd->putrr(lookup, nrec->type,
352 nrec->ttl, querystring);
353 if (presult != ISC_R_SUCCESS) {
354 result = presult;
355 goto done;
358 result = ISC_R_SUCCESS;
360 free(querystring);
361 querystring = NULL;
363 nrec = DLZ_LIST_NEXT(nrec, link);
366 done:
367 cd->zone = NULL;
369 if (querystring != NULL)
370 free(querystring);
372 return (result);
375 static void
376 destroy_rrlist(config_data_t *cd) {
377 nrr_t *trec, *nrec;
379 nrec = DLZ_LIST_HEAD(cd->rrs_list);
381 while (nrec != NULL) {
382 trec = nrec;
384 destroy_querylist(&trec->data);
386 if (trec->name != NULL)
387 free(trec->name);
388 if (trec->type != NULL)
389 free(trec->type);
390 trec->name = trec->type = NULL;
392 /* Get the next record, before we destroy this one. */
393 nrec = DLZ_LIST_NEXT(nrec, link);
395 free(trec);
399 isc_result_t
400 dlz_create(const char *dlzname, unsigned int argc, char *argv[],
401 void **dbdata, ...)
403 config_data_t *cd;
404 char *endp;
405 int i, def_ttl;
406 nrr_t *trec = NULL;
407 isc_result_t result;
408 const char *helper_name;
409 va_list ap;
411 if (argc < 8 || argc % 4 != 0)
412 return (ISC_R_FAILURE);
414 cd = calloc(1, sizeof(config_data_t));
415 if (cd == NULL)
416 return (ISC_R_NOMEMORY);
417 memset(cd, 0, sizeof(config_data_t));
419 /* Fill in the helper functions */
420 va_start(ap, dbdata);
421 while ((helper_name = va_arg(ap, const char*)) != NULL)
422 b9_add_helper(cd, helper_name, va_arg(ap, void*));
423 va_end(ap);
426 * Write info message to log
428 cd->log(ISC_LOG_INFO,
429 "Loading '%s' using DLZ_wildcard driver. "
430 "Zone: %s, AXFR allowed for: %s, $TTL: %s",
431 dlzname, argv[1], argv[2], argv[3]);
433 /* initialize the records list here to simplify cleanup */
434 DLZ_LIST_INIT(cd->rrs_list);
436 cd->zone_pattern = strdup(argv[1]);
437 if (cd->zone_pattern == NULL)
438 goto cleanup;
440 cd->axfr_pattern = strdup(argv[2]);
441 if (cd->axfr_pattern == NULL)
442 goto cleanup;
444 def_ttl = strtol(argv[3], &endp, 10);
445 if (*endp != '\0' || def_ttl < 0) {
446 def_ttl = 3600;
447 cd->log(ISC_LOG_ERROR, "default TTL invalid, using 3600");
450 for (i = 4; i < argc; i += 4) {
451 result = ISC_R_NOMEMORY;
453 trec = malloc(sizeof(nrr_t));
454 if (trec == NULL)
455 goto full_cleanup;
457 memset(trec, 0, sizeof(nrr_t));
459 /* Initialize the record link */
460 DLZ_LINK_INIT(trec, link);
461 /* Append the record to the list */
462 DLZ_LIST_APPEND(cd->rrs_list, trec, link);
464 trec->name = strdup(argv[i]);
465 if (trec->name == NULL)
466 goto full_cleanup;
468 trec->type = strdup(argv[i + 2]);
469 if (trec->type == NULL)
470 goto full_cleanup;
472 trec->ttl = strtol(argv[i + 1], &endp, 10);
473 if (argv[i + 1][0] == '\0' || *endp != '\0' || trec->ttl < 0)
474 trec->ttl = def_ttl;
476 result = build_querylist(argv[i + 3], &cd->zone,
477 &cd->record, &cd->client,
478 &trec->data, 0, cd->log);
479 /* If unsuccessful, log err msg and cleanup */
480 if (result != ISC_R_SUCCESS) {
481 cd->log(ISC_LOG_ERROR,
482 "Could not build RR data list at argv[%d]",
483 i + 3);
484 goto full_cleanup;
488 *dbdata = cd;
490 return (ISC_R_SUCCESS);
492 full_cleanup:
493 destroy_rrlist(cd);
495 cleanup:
496 if (cd->zone_pattern != NULL)
497 free(cd->zone_pattern);
498 if (cd->axfr_pattern != NULL)
499 free(cd->axfr_pattern);
500 free(cd);
502 return (result);
505 void
506 dlz_destroy(void *dbdata) {
507 config_data_t *cd = (config_data_t *) dbdata;
510 * Write debugging message to log
512 cd->log(ISC_LOG_DEBUG(2), "Unloading DLZ_wildcard driver.");
514 destroy_rrlist(cd);
516 free(cd->zone_pattern);
517 free(cd->axfr_pattern);
518 free(cd);
523 * Return the version of the API
526 dlz_version(unsigned int *flags) {
527 UNUSED(flags);
528 /* XXX: ok to set DNS_SDLZFLAG_THREADSAFE here? */
529 return (DLZ_DLOPEN_VERSION);
533 * Register a helper function from the bind9 dlz_dlopen driver
535 static void
536 b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr) {
537 if (strcmp(helper_name, "log") == 0)
538 cd->log = (log_t *)ptr;
539 if (strcmp(helper_name, "putrr") == 0)
540 cd->putrr = (dns_sdlz_putrr_t *)ptr;
541 if (strcmp(helper_name, "putnamedrr") == 0)
542 cd->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
543 if (strcmp(helper_name, "writeable_zone") == 0)
544 cd->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
547 static const char *
548 shortest_match(const char *pattern, const char *string) {
549 const char *p = string;
550 if (pattern == NULL || p == NULL || *p == '\0')
551 return (NULL);
553 p += strlen(p);
554 while (p-- > string) {
555 if (*p == '.') {
556 if (fnmatch(pattern, p + 1, FNM_CASEFOLD) == 0)
557 return (p + 1);
560 if (fnmatch(pattern, string, FNM_CASEFOLD) == 0)
561 return (string);
563 return (NULL);
567 * The helper functions stolen from the FreeBSD kernel (sys/libkern/fnmatch.c).
569 * Why don't we use fnmatch(3) from libc? Because it is not thread-safe, and
570 * it is not thread-safe because it supports multibyte characters. But here,
571 * in BIND, we want to be thread-safe and don't need multibyte - DNS names are
572 * always ASCII.
574 #define EOS '\0'
576 #define RANGE_MATCH 1
577 #define RANGE_NOMATCH 0
578 #define RANGE_ERROR (-1)
580 static int
581 fnmatch(const char *pattern, const char *string, int flags) {
582 const char *stringstart;
583 char *newp;
584 char c, test;
586 for (stringstart = string;;)
587 switch (c = *pattern++) {
588 case EOS:
589 if ((flags & FNM_LEADING_DIR) && *string == '/')
590 return (0);
591 return (*string == EOS ? 0 : FNM_NOMATCH);
592 case '?':
593 if (*string == EOS)
594 return (FNM_NOMATCH);
595 if (*string == '/' && (flags & FNM_PATHNAME))
596 return (FNM_NOMATCH);
597 if (*string == '.' && (flags & FNM_PERIOD) &&
598 (string == stringstart ||
599 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
600 return (FNM_NOMATCH);
601 ++string;
602 break;
603 case '*':
604 c = *pattern;
605 /* Collapse multiple stars. */
606 while (c == '*')
607 c = *++pattern;
609 if (*string == '.' && (flags & FNM_PERIOD) &&
610 (string == stringstart ||
611 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
612 return (FNM_NOMATCH);
614 /* Optimize for pattern with * at end or before /. */
615 if (c == EOS)
616 if (flags & FNM_PATHNAME)
617 return ((flags & FNM_LEADING_DIR) ||
618 index(string, '/') == NULL ?
619 0 : FNM_NOMATCH);
620 else
621 return (0);
622 else if (c == '/' && flags & FNM_PATHNAME) {
623 if ((string = index(string, '/')) == NULL)
624 return (FNM_NOMATCH);
625 break;
628 /* General case, use recursion. */
629 while ((test = *string) != EOS) {
630 if (!fnmatch(pattern, string,
631 flags & ~FNM_PERIOD))
632 return (0);
633 if (test == '/' && flags & FNM_PATHNAME)
634 break;
635 ++string;
637 return (FNM_NOMATCH);
638 case '[':
639 if (*string == EOS)
640 return (FNM_NOMATCH);
641 if (*string == '/' && (flags & FNM_PATHNAME))
642 return (FNM_NOMATCH);
643 if (*string == '.' && (flags & FNM_PERIOD) &&
644 (string == stringstart ||
645 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
646 return (FNM_NOMATCH);
648 switch (rangematch(pattern, *string, flags, &newp)) {
649 case RANGE_ERROR:
650 goto norm;
651 case RANGE_MATCH:
652 pattern = newp;
653 break;
654 case RANGE_NOMATCH:
655 return (FNM_NOMATCH);
657 ++string;
658 break;
659 case '\\':
660 if (!(flags & FNM_NOESCAPE)) {
661 if ((c = *pattern++) == EOS) {
662 c = '\\';
663 --pattern;
666 /* FALLTHROUGH */
667 default:
668 norm:
669 if (c == *string)
671 else if ((flags & FNM_CASEFOLD) &&
672 (tolower((unsigned char)c) ==
673 tolower((unsigned char)*string)))
675 else
676 return (FNM_NOMATCH);
677 string++;
678 break;
680 /* NOTREACHED */
683 static int
684 rangematch(const char *pattern, char test, int flags, char **newp) {
685 int negate, ok;
686 char c, c2;
689 * A bracket expression starting with an unquoted circumflex
690 * character produces unspecified results (IEEE 1003.2-1992,
691 * 3.13.2). This implementation treats it like '!', for
692 * consistency with the regular expression syntax.
693 * J.T. Conklin (conklin@ngai.kaleida.com)
695 if ( (negate = (*pattern == '!' || *pattern == '^')) )
696 ++pattern;
698 if (flags & FNM_CASEFOLD)
699 test = tolower((unsigned char)test);
702 * A right bracket shall lose its special meaning and represent
703 * itself in a bracket expression if it occurs first in the list.
704 * -- POSIX.2 2.8.3.2
706 ok = 0;
707 c = *pattern++;
708 do {
709 if (c == '\\' && !(flags & FNM_NOESCAPE))
710 c = *pattern++;
711 if (c == EOS)
712 return (RANGE_ERROR);
714 if (c == '/' && (flags & FNM_PATHNAME))
715 return (RANGE_NOMATCH);
717 if (flags & FNM_CASEFOLD)
718 c = tolower((unsigned char)c);
720 if (*pattern == '-'
721 && (c2 = *(pattern+1)) != EOS && c2 != ']') {
722 pattern += 2;
723 if (c2 == '\\' && !(flags & FNM_NOESCAPE))
724 c2 = *pattern++;
725 if (c2 == EOS)
726 return (RANGE_ERROR);
728 if (flags & FNM_CASEFOLD)
729 c2 = tolower((unsigned char)c2);
731 if (c <= test && test <= c2)
732 ok = 1;
733 } else if (c == test)
734 ok = 1;
735 } while ((c = *pattern++) != ']');
737 *newp = (char *)(uintptr_t)pattern;
738 return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);