1*4ac1c27eSchristos /*	$NetBSD: dlz_wildcard_dynamic.c,v 1.7 2023/01/25 21:43:29 christos Exp $	*/
2e2b1b9c0Schristos 
3e2b1b9c0Schristos /*
4c0b5d9fbSchristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5e2b1b9c0Schristos  *
6c0b5d9fbSchristos  * SPDX-License-Identifier: MPL-2.0 and ISC
7e2b1b9c0Schristos  *
8c0b5d9fbSchristos  * This Source Code Form is subject to the terms of the Mozilla Public
9c0b5d9fbSchristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
10c0b5d9fbSchristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11c0b5d9fbSchristos  */
12c0b5d9fbSchristos 
13c0b5d9fbSchristos /*
14c0b5d9fbSchristos  * Copyright (C) Stichting NLnet, Netherlands, stichting@nlnet.nl.
15c0b5d9fbSchristos  * Copyright (C) Vadim Goncharov, Russia, vadim_nuclight@mail.ru.
16e2b1b9c0Schristos  *
17e2b1b9c0Schristos  * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
18e2b1b9c0Schristos  * conceived and contributed by Rob Butler.
19e2b1b9c0Schristos  *
20c0b5d9fbSchristos  * Permission to use, copy, modify, and distribute this software for any purpose
21c0b5d9fbSchristos  * with or without fee is hereby granted, provided that the above copyright
22c0b5d9fbSchristos  * notice and this permission notice appear in all copies.
23e2b1b9c0Schristos  *
24c0b5d9fbSchristos  * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET DISCLAIMS ALL WARRANTIES
25c0b5d9fbSchristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
26c0b5d9fbSchristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL STICHTING NLNET BE LIABLE FOR
27c0b5d9fbSchristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
28c0b5d9fbSchristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
29c0b5d9fbSchristos  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
30c0b5d9fbSchristos  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
31e2b1b9c0Schristos  */
32e2b1b9c0Schristos 
33e2b1b9c0Schristos /*
34e2b1b9c0Schristos  * This provides the externally loadable wildcard DLZ module.
35e2b1b9c0Schristos  */
36e2b1b9c0Schristos 
379742fdb4Schristos #include <ctype.h>
38f2e20987Schristos #include <inttypes.h>
399742fdb4Schristos #include <stdarg.h>
40f2e20987Schristos #include <stdbool.h>
41e2b1b9c0Schristos #include <stdio.h>
42e2b1b9c0Schristos #include <stdlib.h>
439742fdb4Schristos #include <string.h>
44e2b1b9c0Schristos 
45e2b1b9c0Schristos #include <dlz_dbi.h>
469742fdb4Schristos #include <dlz_list.h>
479742fdb4Schristos #include <dlz_minimal.h>
48e2b1b9c0Schristos 
49e2b1b9c0Schristos #define DE_CONST(konst, var)           \
50e2b1b9c0Schristos 	do {                           \
519742fdb4Schristos 		union {                \
529742fdb4Schristos 			const void *k; \
539742fdb4Schristos 			void *v;       \
549742fdb4Schristos 		} _u;                  \
55e2b1b9c0Schristos 		_u.k = konst;          \
56e2b1b9c0Schristos 		var = _u.v;            \
57f77cbf29Srillig 	} while (0)
58e2b1b9c0Schristos 
59e2b1b9c0Schristos /* fnmatch() return values. */
60e2b1b9c0Schristos #define FNM_NOMATCH 1 /* Match failed. */
61e2b1b9c0Schristos 
62e2b1b9c0Schristos /* fnmatch() flags. */
63e2b1b9c0Schristos #define FNM_NOESCAPE	0x01 /* Disable backslash escaping. */
64e2b1b9c0Schristos #define FNM_PATHNAME	0x02 /* Slash must be matched by slash. */
65e2b1b9c0Schristos #define FNM_PERIOD	0x04 /* Period must be matched by period. */
66e2b1b9c0Schristos #define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
67e2b1b9c0Schristos #define FNM_CASEFOLD	0x10 /* Case insensitive search. */
68e2b1b9c0Schristos #define FNM_IGNORECASE	FNM_CASEFOLD
69e2b1b9c0Schristos #define FNM_FILE_NAME	FNM_PATHNAME
70e2b1b9c0Schristos 
71e2b1b9c0Schristos /*
72e2b1b9c0Schristos  * Our data structures.
73e2b1b9c0Schristos  */
74e2b1b9c0Schristos 
75e2b1b9c0Schristos typedef struct named_rr nrr_t;
76e2b1b9c0Schristos typedef DLZ_LIST(nrr_t) rr_list_t;
77e2b1b9c0Schristos 
78e2b1b9c0Schristos typedef struct config_data {
79e2b1b9c0Schristos 	char *zone_pattern;
80e2b1b9c0Schristos 	char *axfr_pattern;
81e2b1b9c0Schristos 	rr_list_t rrs_list;
82e2b1b9c0Schristos 	char *zone;
83e2b1b9c0Schristos 	char *record;
84e2b1b9c0Schristos 	char *client;
85e2b1b9c0Schristos 
86e2b1b9c0Schristos 	/* Helper functions from the dlz_dlopen driver */
87e2b1b9c0Schristos 	log_t *log;
88e2b1b9c0Schristos 	dns_sdlz_putrr_t *putrr;
89e2b1b9c0Schristos 	dns_sdlz_putnamedrr_t *putnamedrr;
90e2b1b9c0Schristos 	dns_dlz_writeablezone_t *writeable_zone;
91e2b1b9c0Schristos } config_data_t;
92e2b1b9c0Schristos 
93e2b1b9c0Schristos struct named_rr {
94e2b1b9c0Schristos 	char *name;
95e2b1b9c0Schristos 	char *type;
96e2b1b9c0Schristos 	int ttl;
97e2b1b9c0Schristos 	query_list_t *data;
98e2b1b9c0Schristos 	DLZ_LINK(nrr_t) link;
99e2b1b9c0Schristos };
100e2b1b9c0Schristos 
101e2b1b9c0Schristos /*
102e2b1b9c0Schristos  * Forward references
103e2b1b9c0Schristos  */
104e2b1b9c0Schristos static int
105e2b1b9c0Schristos rangematch(const char *, char, int, char **);
106e2b1b9c0Schristos 
107e2b1b9c0Schristos static int
108e2b1b9c0Schristos fnmatch(const char *pattern, const char *string, int flags);
109e2b1b9c0Schristos 
110e2b1b9c0Schristos static void
111e2b1b9c0Schristos b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr);
112e2b1b9c0Schristos 
113e2b1b9c0Schristos static const char *
114e2b1b9c0Schristos shortest_match(const char *pattern, const char *string);
115e2b1b9c0Schristos 
116e2b1b9c0Schristos isc_result_t
dlz_allnodes(const char * zone,void * dbdata,dns_sdlzallnodes_t * allnodes)117e2b1b9c0Schristos dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
118e2b1b9c0Schristos 	config_data_t *cd = (config_data_t *)dbdata;
119e2b1b9c0Schristos 	isc_result_t result;
120e2b1b9c0Schristos 	char *querystring = NULL;
121e2b1b9c0Schristos 	nrr_t *nrec;
122e2b1b9c0Schristos 	int i = 0;
123e2b1b9c0Schristos 
124e2b1b9c0Schristos 	DE_CONST(zone, cd->zone);
125e2b1b9c0Schristos 
126e2b1b9c0Schristos 	/* Write info message to log */
1279742fdb4Schristos 	cd->log(ISC_LOG_DEBUG(1), "dlz_wildcard allnodes called for zone '%s'",
1289742fdb4Schristos 		zone);
129e2b1b9c0Schristos 
130e2b1b9c0Schristos 	result = ISC_R_FAILURE;
131e2b1b9c0Schristos 
132e2b1b9c0Schristos 	nrec = DLZ_LIST_HEAD(cd->rrs_list);
133e2b1b9c0Schristos 	while (nrec != NULL) {
134e2b1b9c0Schristos 		cd->record = nrec->name;
135e2b1b9c0Schristos 
136e2b1b9c0Schristos 		querystring = build_querystring(nrec->data);
137e2b1b9c0Schristos 
138e2b1b9c0Schristos 		if (querystring == NULL) {
139e2b1b9c0Schristos 			result = ISC_R_NOMEMORY;
140e2b1b9c0Schristos 			goto done;
141e2b1b9c0Schristos 		}
142e2b1b9c0Schristos 
143e2b1b9c0Schristos 		cd->log(ISC_LOG_DEBUG(2),
144e2b1b9c0Schristos 			"dlz_wildcard allnodes entry num %d: calling "
145e2b1b9c0Schristos 			"putnamedrr(name=%s type=%s ttl=%d qs=%s)",
146e2b1b9c0Schristos 			i++, nrec->name, nrec->type, nrec->ttl, querystring);
147e2b1b9c0Schristos 
148e2b1b9c0Schristos 		result = cd->putnamedrr(allnodes, nrec->name, nrec->type,
149e2b1b9c0Schristos 					nrec->ttl, querystring);
1509742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
151e2b1b9c0Schristos 			goto done;
1529742fdb4Schristos 		}
153e2b1b9c0Schristos 
154e2b1b9c0Schristos 		nrec = DLZ_LIST_NEXT(nrec, link);
155e2b1b9c0Schristos 	}
156e2b1b9c0Schristos 
157e2b1b9c0Schristos done:
158e2b1b9c0Schristos 	cd->zone = NULL;
159e2b1b9c0Schristos 
1609742fdb4Schristos 	if (querystring != NULL) {
161e2b1b9c0Schristos 		free(querystring);
1629742fdb4Schristos 	}
163e2b1b9c0Schristos 
164e2b1b9c0Schristos 	return (result);
165e2b1b9c0Schristos }
166e2b1b9c0Schristos 
167e2b1b9c0Schristos isc_result_t
dlz_allowzonexfr(void * dbdata,const char * name,const char * client)168e2b1b9c0Schristos dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
169e2b1b9c0Schristos 	config_data_t *cd = (config_data_t *)dbdata;
170e2b1b9c0Schristos 
171e2b1b9c0Schristos 	UNUSED(name);
172e2b1b9c0Schristos 
173e2b1b9c0Schristos 	/* Write info message to log */
174e2b1b9c0Schristos 	cd->log(ISC_LOG_DEBUG(1),
175e2b1b9c0Schristos 		"dlz_wildcard allowzonexfr called for client '%s'", client);
176e2b1b9c0Schristos 
1779742fdb4Schristos 	if (fnmatch(cd->axfr_pattern, client, FNM_CASEFOLD) == 0) {
178e2b1b9c0Schristos 		return (ISC_R_SUCCESS);
1799742fdb4Schristos 	} else {
180e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
181e2b1b9c0Schristos 	}
1829742fdb4Schristos }
183e2b1b9c0Schristos 
184e2b1b9c0Schristos #if DLZ_DLOPEN_VERSION < 3
185e2b1b9c0Schristos isc_result_t
dlz_findzonedb(void * dbdata,const char * name)186e2b1b9c0Schristos dlz_findzonedb(void *dbdata, const char *name)
1879742fdb4Schristos #else  /* if DLZ_DLOPEN_VERSION < 3 */
188e2b1b9c0Schristos isc_result_t
1899742fdb4Schristos dlz_findzonedb(void *dbdata, const char *name, dns_clientinfomethods_t *methods,
190e2b1b9c0Schristos 	       dns_clientinfo_t *clientinfo)
1919742fdb4Schristos #endif /* if DLZ_DLOPEN_VERSION < 3 */
192e2b1b9c0Schristos {
193e2b1b9c0Schristos 	config_data_t *cd = (config_data_t *)dbdata;
194e2b1b9c0Schristos 	const char *p;
195e2b1b9c0Schristos 
196e2b1b9c0Schristos #if DLZ_DLOPEN_VERSION >= 3
197e2b1b9c0Schristos 	UNUSED(methods);
198e2b1b9c0Schristos 	UNUSED(clientinfo);
1999742fdb4Schristos #endif /* if DLZ_DLOPEN_VERSION >= 3 */
200e2b1b9c0Schristos 
201e2b1b9c0Schristos 	p = shortest_match(cd->zone_pattern, name);
2029742fdb4Schristos 	if (p == NULL) {
203e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
2049742fdb4Schristos 	}
205e2b1b9c0Schristos 
206e2b1b9c0Schristos 	/* Write info message to log */
2079742fdb4Schristos 	cd->log(ISC_LOG_DEBUG(1), "dlz_wildcard findzonedb matched '%s'", p);
208e2b1b9c0Schristos 
209e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
210e2b1b9c0Schristos }
211e2b1b9c0Schristos 
212e2b1b9c0Schristos #if DLZ_DLOPEN_VERSION == 1
213e2b1b9c0Schristos isc_result_t
dlz_lookup(const char * zone,const char * name,void * dbdata,dns_sdlzlookup_t * lookup)2149742fdb4Schristos dlz_lookup(const char *zone, const char *name, void *dbdata,
2159742fdb4Schristos 	   dns_sdlzlookup_t *lookup)
2169742fdb4Schristos #else  /* if DLZ_DLOPEN_VERSION == 1 */
217e2b1b9c0Schristos isc_result_t
2189742fdb4Schristos dlz_lookup(const char *zone, const char *name, void *dbdata,
2199742fdb4Schristos 	   dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods,
220e2b1b9c0Schristos 	   dns_clientinfo_t *clientinfo)
2219742fdb4Schristos #endif /* if DLZ_DLOPEN_VERSION == 1 */
222e2b1b9c0Schristos {
223e2b1b9c0Schristos 	isc_result_t result;
224e2b1b9c0Schristos 	config_data_t *cd = (config_data_t *)dbdata;
225e2b1b9c0Schristos 	char *querystring = NULL;
226e2b1b9c0Schristos 	const char *p;
227e2b1b9c0Schristos 	char *namebuf;
228e2b1b9c0Schristos 	nrr_t *nrec;
229e2b1b9c0Schristos 
230e2b1b9c0Schristos #if DLZ_DLOPEN_VERSION >= 2
231e2b1b9c0Schristos 	UNUSED(methods);
232e2b1b9c0Schristos 	UNUSED(clientinfo);
2339742fdb4Schristos #endif /* if DLZ_DLOPEN_VERSION >= 2 */
234e2b1b9c0Schristos 
235e2b1b9c0Schristos 	p = shortest_match(cd->zone_pattern, zone);
2369742fdb4Schristos 	if (p == NULL) {
237e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
2389742fdb4Schristos 	}
239e2b1b9c0Schristos 
240e2b1b9c0Schristos 	DE_CONST(name, cd->record);
241e2b1b9c0Schristos 	DE_CONST(p, cd->zone);
242e2b1b9c0Schristos 
243e2b1b9c0Schristos 	if ((p != zone) && (strcmp(name, "@") == 0 || strcmp(name, zone) == 0))
244e2b1b9c0Schristos 	{
245e2b1b9c0Schristos 		size_t len = p - zone;
246e2b1b9c0Schristos 		namebuf = malloc(len);
2479742fdb4Schristos 		if (namebuf == NULL) {
248e2b1b9c0Schristos 			return (ISC_R_NOMEMORY);
2499742fdb4Schristos 		}
250e2b1b9c0Schristos 		strncpy(namebuf, zone, len - 1);
251e2b1b9c0Schristos 		namebuf[len - 1] = '\0';
252e2b1b9c0Schristos 		cd->record = namebuf;
2539742fdb4Schristos 	} else if (p == zone) {
254*4ac1c27eSchristos 		cd->record = (char *)"@";
2559742fdb4Schristos 	}
256e2b1b9c0Schristos 
257e2b1b9c0Schristos 	/* Write info message to log */
258e2b1b9c0Schristos 	cd->log(ISC_LOG_DEBUG(1),
259e2b1b9c0Schristos 		"dlz_wildcard_dynamic: lookup for '%s' in '%s': "
260e2b1b9c0Schristos 		"trying '%s' in '%s'",
261e2b1b9c0Schristos 		name, zone, cd->record, cd->zone);
262e2b1b9c0Schristos 
263e2b1b9c0Schristos 	result = ISC_R_NOTFOUND;
264e2b1b9c0Schristos 	nrec = DLZ_LIST_HEAD(cd->rrs_list);
265e2b1b9c0Schristos 	while (nrec != NULL) {
266e2b1b9c0Schristos 		nrr_t *next = DLZ_LIST_NEXT(nrec, link);
267e2b1b9c0Schristos 		if (strcmp(cd->record, nrec->name) == 0) {
268e2b1b9c0Schristos 			/* We handle authority data in dlz_authority() */
269e2b1b9c0Schristos 			if (strcmp(nrec->type, "SOA") == 0 ||
270*4ac1c27eSchristos 			    strcmp(nrec->type, "NS") == 0)
271*4ac1c27eSchristos 			{
272e2b1b9c0Schristos 				nrec = next;
273e2b1b9c0Schristos 				continue;
274e2b1b9c0Schristos 			}
275e2b1b9c0Schristos 
276e2b1b9c0Schristos 			querystring = build_querystring(nrec->data);
277e2b1b9c0Schristos 			if (querystring == NULL) {
278e2b1b9c0Schristos 				result = ISC_R_NOMEMORY;
279e2b1b9c0Schristos 				goto done;
280e2b1b9c0Schristos 			}
281e2b1b9c0Schristos 
2829742fdb4Schristos 			result = cd->putrr(lookup, nrec->type, nrec->ttl,
2839742fdb4Schristos 					   querystring);
2849742fdb4Schristos 			if (result != ISC_R_SUCCESS) {
285e2b1b9c0Schristos 				goto done;
2869742fdb4Schristos 			}
287e2b1b9c0Schristos 
288e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
289e2b1b9c0Schristos 
290e2b1b9c0Schristos 			free(querystring);
291e2b1b9c0Schristos 			querystring = NULL;
292e2b1b9c0Schristos 		}
293e2b1b9c0Schristos 		nrec = next;
294e2b1b9c0Schristos 	}
295e2b1b9c0Schristos 
296e2b1b9c0Schristos done:
297e2b1b9c0Schristos 	cd->zone = NULL;
298e2b1b9c0Schristos 	cd->record = NULL;
299e2b1b9c0Schristos 
3009742fdb4Schristos 	if (querystring != NULL) {
301e2b1b9c0Schristos 		free(querystring);
3029742fdb4Schristos 	}
303e2b1b9c0Schristos 
304e2b1b9c0Schristos 	return (result);
305e2b1b9c0Schristos }
306e2b1b9c0Schristos 
307e2b1b9c0Schristos isc_result_t
dlz_authority(const char * zone,void * dbdata,dns_sdlzlookup_t * lookup)308e2b1b9c0Schristos dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) {
309e2b1b9c0Schristos 	isc_result_t result;
310e2b1b9c0Schristos 	config_data_t *cd = (config_data_t *)dbdata;
311e2b1b9c0Schristos 	char *querystring = NULL;
312e2b1b9c0Schristos 	nrr_t *nrec;
313*4ac1c27eSchristos 	const char *p;
314e2b1b9c0Schristos 
315e2b1b9c0Schristos 	p = shortest_match(cd->zone_pattern, zone);
3169742fdb4Schristos 	if (p == NULL) {
317e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
3189742fdb4Schristos 	}
319e2b1b9c0Schristos 
320e2b1b9c0Schristos 	DE_CONST(p, cd->zone);
321e2b1b9c0Schristos 
322e2b1b9c0Schristos 	/* Write info message to log */
3239742fdb4Schristos 	cd->log(ISC_LOG_DEBUG(1), "dlz_wildcard_dynamic: authority for '%s'",
3249742fdb4Schristos 		zone);
325e2b1b9c0Schristos 
326e2b1b9c0Schristos 	result = ISC_R_NOTFOUND;
327e2b1b9c0Schristos 	nrec = DLZ_LIST_HEAD(cd->rrs_list);
328e2b1b9c0Schristos 	while (nrec != NULL) {
329e2b1b9c0Schristos 		if (strcmp("@", nrec->name) == 0) {
330e2b1b9c0Schristos 			isc_result_t presult;
331e2b1b9c0Schristos 
332e2b1b9c0Schristos 			querystring = build_querystring(nrec->data);
333e2b1b9c0Schristos 			if (querystring == NULL) {
334e2b1b9c0Schristos 				result = ISC_R_NOMEMORY;
335e2b1b9c0Schristos 				goto done;
336e2b1b9c0Schristos 			}
337e2b1b9c0Schristos 
3389742fdb4Schristos 			presult = cd->putrr(lookup, nrec->type, nrec->ttl,
3399742fdb4Schristos 					    querystring);
340e2b1b9c0Schristos 			if (presult != ISC_R_SUCCESS) {
341e2b1b9c0Schristos 				result = presult;
342e2b1b9c0Schristos 				goto done;
343e2b1b9c0Schristos 			}
344e2b1b9c0Schristos 
345e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
346e2b1b9c0Schristos 
347e2b1b9c0Schristos 			free(querystring);
348e2b1b9c0Schristos 			querystring = NULL;
349e2b1b9c0Schristos 		}
350e2b1b9c0Schristos 		nrec = DLZ_LIST_NEXT(nrec, link);
351e2b1b9c0Schristos 	}
352e2b1b9c0Schristos 
353e2b1b9c0Schristos done:
354e2b1b9c0Schristos 	cd->zone = NULL;
355e2b1b9c0Schristos 
3569742fdb4Schristos 	if (querystring != NULL) {
357e2b1b9c0Schristos 		free(querystring);
3589742fdb4Schristos 	}
359e2b1b9c0Schristos 
360e2b1b9c0Schristos 	return (result);
361e2b1b9c0Schristos }
362e2b1b9c0Schristos 
363e2b1b9c0Schristos static void
destroy_rrlist(config_data_t * cd)364e2b1b9c0Schristos destroy_rrlist(config_data_t *cd) {
365e2b1b9c0Schristos 	nrr_t *trec, *nrec;
366e2b1b9c0Schristos 
367e2b1b9c0Schristos 	nrec = DLZ_LIST_HEAD(cd->rrs_list);
368e2b1b9c0Schristos 
369e2b1b9c0Schristos 	while (nrec != NULL) {
370e2b1b9c0Schristos 		trec = nrec;
371e2b1b9c0Schristos 
372e2b1b9c0Schristos 		destroy_querylist(&trec->data);
373e2b1b9c0Schristos 
3749742fdb4Schristos 		if (trec->name != NULL) {
375e2b1b9c0Schristos 			free(trec->name);
3769742fdb4Schristos 		}
3779742fdb4Schristos 		if (trec->type != NULL) {
378e2b1b9c0Schristos 			free(trec->type);
3799742fdb4Schristos 		}
380e2b1b9c0Schristos 		trec->name = trec->type = NULL;
381e2b1b9c0Schristos 
382e2b1b9c0Schristos 		/* Get the next record, before we destroy this one. */
383e2b1b9c0Schristos 		nrec = DLZ_LIST_NEXT(nrec, link);
384e2b1b9c0Schristos 
385e2b1b9c0Schristos 		free(trec);
386e2b1b9c0Schristos 	}
387e2b1b9c0Schristos }
388e2b1b9c0Schristos 
389e2b1b9c0Schristos isc_result_t
dlz_create(const char * dlzname,unsigned int argc,char * argv[],void ** dbdata,...)3909742fdb4Schristos dlz_create(const char *dlzname, unsigned int argc, char *argv[], void **dbdata,
3919742fdb4Schristos 	   ...) {
392e2b1b9c0Schristos 	config_data_t *cd;
393e2b1b9c0Schristos 	char *endp;
394*4ac1c27eSchristos 	unsigned int i;
395*4ac1c27eSchristos 	int def_ttl;
396e2b1b9c0Schristos 	nrr_t *trec = NULL;
397e2b1b9c0Schristos 	isc_result_t result;
398e2b1b9c0Schristos 	const char *helper_name;
399e2b1b9c0Schristos 	va_list ap;
400e2b1b9c0Schristos 
4019742fdb4Schristos 	if (argc < 8 || argc % 4 != 0) {
402e2b1b9c0Schristos 		return (ISC_R_FAILURE);
4039742fdb4Schristos 	}
404e2b1b9c0Schristos 
405e2b1b9c0Schristos 	cd = calloc(1, sizeof(config_data_t));
4069742fdb4Schristos 	if (cd == NULL) {
407e2b1b9c0Schristos 		return (ISC_R_NOMEMORY);
4089742fdb4Schristos 	}
409e2b1b9c0Schristos 	memset(cd, 0, sizeof(config_data_t));
410e2b1b9c0Schristos 
411e2b1b9c0Schristos 	/* Fill in the helper functions */
412e2b1b9c0Schristos 	va_start(ap, dbdata);
4139742fdb4Schristos 	while ((helper_name = va_arg(ap, const char *)) != NULL) {
414e2b1b9c0Schristos 		b9_add_helper(cd, helper_name, va_arg(ap, void *));
4159742fdb4Schristos 	}
416e2b1b9c0Schristos 	va_end(ap);
417e2b1b9c0Schristos 
418e2b1b9c0Schristos 	/*
419e2b1b9c0Schristos 	 * Write info message to log
420e2b1b9c0Schristos 	 */
421e2b1b9c0Schristos 	cd->log(ISC_LOG_INFO,
422e2b1b9c0Schristos 		"Loading '%s' using DLZ_wildcard driver. "
423e2b1b9c0Schristos 		"Zone: %s, AXFR allowed for: %s, $TTL: %s",
424e2b1b9c0Schristos 		dlzname, argv[1], argv[2], argv[3]);
425e2b1b9c0Schristos 
426e2b1b9c0Schristos 	/* initialize the records list here to simplify cleanup */
427e2b1b9c0Schristos 	DLZ_LIST_INIT(cd->rrs_list);
428e2b1b9c0Schristos 
429e2b1b9c0Schristos 	cd->zone_pattern = strdup(argv[1]);
430e2b1b9c0Schristos 	cd->axfr_pattern = strdup(argv[2]);
431e2b1b9c0Schristos 	if (cd->zone_pattern == NULL || cd->axfr_pattern == NULL) {
432e2b1b9c0Schristos 		result = ISC_R_NOMEMORY;
433e2b1b9c0Schristos 		goto cleanup;
434e2b1b9c0Schristos 	}
435e2b1b9c0Schristos 
436e2b1b9c0Schristos 	def_ttl = strtol(argv[3], &endp, 10);
437e2b1b9c0Schristos 	if (*endp != '\0' || def_ttl < 0) {
438e2b1b9c0Schristos 		def_ttl = 3600;
439e2b1b9c0Schristos 		cd->log(ISC_LOG_ERROR, "default TTL invalid, using 3600");
440e2b1b9c0Schristos 	}
441e2b1b9c0Schristos 
442e2b1b9c0Schristos 	for (i = 4; i < argc; i += 4) {
443e2b1b9c0Schristos 		result = ISC_R_NOMEMORY;
444e2b1b9c0Schristos 
445e2b1b9c0Schristos 		trec = malloc(sizeof(nrr_t));
4469742fdb4Schristos 		if (trec == NULL) {
447e2b1b9c0Schristos 			goto full_cleanup;
4489742fdb4Schristos 		}
449e2b1b9c0Schristos 
450e2b1b9c0Schristos 		memset(trec, 0, sizeof(nrr_t));
451e2b1b9c0Schristos 
452e2b1b9c0Schristos 		/* Initialize the record link */
453e2b1b9c0Schristos 		DLZ_LINK_INIT(trec, link);
454e2b1b9c0Schristos 		/* Append the record to the list */
455e2b1b9c0Schristos 		DLZ_LIST_APPEND(cd->rrs_list, trec, link);
456e2b1b9c0Schristos 
457e2b1b9c0Schristos 		trec->name = strdup(argv[i]);
4589742fdb4Schristos 		if (trec->name == NULL) {
459e2b1b9c0Schristos 			goto full_cleanup;
4609742fdb4Schristos 		}
461e2b1b9c0Schristos 
462e2b1b9c0Schristos 		trec->type = strdup(argv[i + 2]);
4639742fdb4Schristos 		if (trec->type == NULL) {
464e2b1b9c0Schristos 			goto full_cleanup;
4659742fdb4Schristos 		}
466e2b1b9c0Schristos 
467e2b1b9c0Schristos 		trec->ttl = strtol(argv[i + 1], &endp, 10);
4689742fdb4Schristos 		if (argv[i + 1][0] == '\0' || *endp != '\0' || trec->ttl < 0) {
469e2b1b9c0Schristos 			trec->ttl = def_ttl;
4709742fdb4Schristos 		}
471e2b1b9c0Schristos 
4729742fdb4Schristos 		result = build_querylist(argv[i + 3], &cd->zone, &cd->record,
4739742fdb4Schristos 					 &cd->client, &trec->data, 0, cd->log);
474e2b1b9c0Schristos 		/* If unsuccessful, log err msg and cleanup */
475e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS) {
476e2b1b9c0Schristos 			cd->log(ISC_LOG_ERROR,
477e2b1b9c0Schristos 				"Could not build RR data list at argv[%d]",
478e2b1b9c0Schristos 				i + 3);
479e2b1b9c0Schristos 			goto full_cleanup;
480e2b1b9c0Schristos 		}
481e2b1b9c0Schristos 	}
482e2b1b9c0Schristos 
483e2b1b9c0Schristos 	*dbdata = cd;
484e2b1b9c0Schristos 
485e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
486e2b1b9c0Schristos 
487e2b1b9c0Schristos full_cleanup:
488e2b1b9c0Schristos 	destroy_rrlist(cd);
489e2b1b9c0Schristos 
490e2b1b9c0Schristos cleanup:
4919742fdb4Schristos 	if (cd->zone_pattern != NULL) {
492e2b1b9c0Schristos 		free(cd->zone_pattern);
4939742fdb4Schristos 	}
4949742fdb4Schristos 	if (cd->axfr_pattern != NULL) {
495e2b1b9c0Schristos 		free(cd->axfr_pattern);
4969742fdb4Schristos 	}
497e2b1b9c0Schristos 	free(cd);
498e2b1b9c0Schristos 
499e2b1b9c0Schristos 	return (result);
500e2b1b9c0Schristos }
501e2b1b9c0Schristos 
502e2b1b9c0Schristos void
dlz_destroy(void * dbdata)503e2b1b9c0Schristos dlz_destroy(void *dbdata) {
504e2b1b9c0Schristos 	config_data_t *cd = (config_data_t *)dbdata;
505e2b1b9c0Schristos 
506e2b1b9c0Schristos 	/*
507e2b1b9c0Schristos 	 * Write debugging message to log
508e2b1b9c0Schristos 	 */
509e2b1b9c0Schristos 	cd->log(ISC_LOG_DEBUG(2), "Unloading DLZ_wildcard driver.");
510e2b1b9c0Schristos 
511e2b1b9c0Schristos 	destroy_rrlist(cd);
512e2b1b9c0Schristos 
513e2b1b9c0Schristos 	free(cd->zone_pattern);
514e2b1b9c0Schristos 	free(cd->axfr_pattern);
515e2b1b9c0Schristos 	free(cd);
516e2b1b9c0Schristos }
517e2b1b9c0Schristos 
518e2b1b9c0Schristos /*
519e2b1b9c0Schristos  * Return the version of the API
520e2b1b9c0Schristos  */
521e2b1b9c0Schristos int
dlz_version(unsigned int * flags)522e2b1b9c0Schristos dlz_version(unsigned int *flags) {
523e2b1b9c0Schristos 	UNUSED(flags);
524e2b1b9c0Schristos 	/* XXX: ok to set DNS_SDLZFLAG_THREADSAFE here? */
525e2b1b9c0Schristos 	return (DLZ_DLOPEN_VERSION);
526e2b1b9c0Schristos }
527e2b1b9c0Schristos 
528e2b1b9c0Schristos /*
529e2b1b9c0Schristos  * Register a helper function from the bind9 dlz_dlopen driver
530e2b1b9c0Schristos  */
531e2b1b9c0Schristos static void
b9_add_helper(struct config_data * cd,const char * helper_name,void * ptr)532e2b1b9c0Schristos b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr) {
5339742fdb4Schristos 	if (strcmp(helper_name, "log") == 0) {
534e2b1b9c0Schristos 		cd->log = (log_t *)ptr;
5359742fdb4Schristos 	}
5369742fdb4Schristos 	if (strcmp(helper_name, "putrr") == 0) {
537e2b1b9c0Schristos 		cd->putrr = (dns_sdlz_putrr_t *)ptr;
5389742fdb4Schristos 	}
5399742fdb4Schristos 	if (strcmp(helper_name, "putnamedrr") == 0) {
540e2b1b9c0Schristos 		cd->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
5419742fdb4Schristos 	}
5429742fdb4Schristos 	if (strcmp(helper_name, "writeable_zone") == 0) {
543e2b1b9c0Schristos 		cd->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
544e2b1b9c0Schristos 	}
5459742fdb4Schristos }
546e2b1b9c0Schristos 
547e2b1b9c0Schristos static const char *
shortest_match(const char * pattern,const char * string)548e2b1b9c0Schristos shortest_match(const char *pattern, const char *string) {
549e2b1b9c0Schristos 	const char *p = string;
5509742fdb4Schristos 	if (pattern == NULL || p == NULL || *p == '\0') {
551e2b1b9c0Schristos 		return (NULL);
5529742fdb4Schristos 	}
553e2b1b9c0Schristos 
554e2b1b9c0Schristos 	p += strlen(p);
555e2b1b9c0Schristos 	while (p-- > string) {
556e2b1b9c0Schristos 		if (*p == '.') {
5579742fdb4Schristos 			if (fnmatch(pattern, p + 1, FNM_CASEFOLD) == 0) {
558e2b1b9c0Schristos 				return (p + 1);
559e2b1b9c0Schristos 			}
560e2b1b9c0Schristos 		}
5619742fdb4Schristos 	}
5629742fdb4Schristos 	if (fnmatch(pattern, string, FNM_CASEFOLD) == 0) {
563e2b1b9c0Schristos 		return (string);
5649742fdb4Schristos 	}
565e2b1b9c0Schristos 
566e2b1b9c0Schristos 	return (NULL);
567e2b1b9c0Schristos }
568e2b1b9c0Schristos 
569e2b1b9c0Schristos /*
570e2b1b9c0Schristos  * The helper functions stolen from the FreeBSD kernel (sys/libkern/fnmatch.c).
571e2b1b9c0Schristos  *
572e2b1b9c0Schristos  * Why don't we use fnmatch(3) from libc? Because it is not thread-safe, and
573e2b1b9c0Schristos  * it is not thread-safe because it supports multibyte characters. But here,
574e2b1b9c0Schristos  * in BIND, we want to be thread-safe and don't need multibyte - DNS names are
575e2b1b9c0Schristos  * always ASCII.
576e2b1b9c0Schristos  */
577e2b1b9c0Schristos #define EOS '\0'
578e2b1b9c0Schristos 
579e2b1b9c0Schristos #define RANGE_MATCH   1
580e2b1b9c0Schristos #define RANGE_NOMATCH 0
581e2b1b9c0Schristos #define RANGE_ERROR   (-1)
582e2b1b9c0Schristos 
583e2b1b9c0Schristos static int
fnmatch(const char * pattern,const char * string,int flags)584e2b1b9c0Schristos fnmatch(const char *pattern, const char *string, int flags) {
585e2b1b9c0Schristos 	const char *stringstart;
586e2b1b9c0Schristos 	char *newp;
587e2b1b9c0Schristos 	char c, test;
588e2b1b9c0Schristos 
589c0b5d9fbSchristos 	for (stringstart = string;;) {
590e2b1b9c0Schristos 		switch (c = *pattern++) {
591e2b1b9c0Schristos 		case EOS:
5929742fdb4Schristos 			if ((flags & FNM_LEADING_DIR) && *string == '/') {
593e2b1b9c0Schristos 				return (0);
5949742fdb4Schristos 			}
595e2b1b9c0Schristos 			return (*string == EOS ? 0 : FNM_NOMATCH);
596e2b1b9c0Schristos 		case '?':
5979742fdb4Schristos 			if (*string == EOS) {
598e2b1b9c0Schristos 				return (FNM_NOMATCH);
5999742fdb4Schristos 			}
6009742fdb4Schristos 			if (*string == '/' && (flags & FNM_PATHNAME)) {
601e2b1b9c0Schristos 				return (FNM_NOMATCH);
6029742fdb4Schristos 			}
603e2b1b9c0Schristos 			if (*string == '.' && (flags & FNM_PERIOD) &&
604e2b1b9c0Schristos 			    (string == stringstart ||
605e2b1b9c0Schristos 			     ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
6069742fdb4Schristos 			{
607e2b1b9c0Schristos 				return (FNM_NOMATCH);
6089742fdb4Schristos 			}
609e2b1b9c0Schristos 			++string;
610e2b1b9c0Schristos 			break;
611e2b1b9c0Schristos 		case '*':
612e2b1b9c0Schristos 			c = *pattern;
613e2b1b9c0Schristos 			/* Collapse multiple stars. */
6149742fdb4Schristos 			while (c == '*') {
615e2b1b9c0Schristos 				c = *++pattern;
6169742fdb4Schristos 			}
617e2b1b9c0Schristos 
618e2b1b9c0Schristos 			if (*string == '.' && (flags & FNM_PERIOD) &&
619e2b1b9c0Schristos 			    (string == stringstart ||
620e2b1b9c0Schristos 			     ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
6219742fdb4Schristos 			{
622e2b1b9c0Schristos 				return (FNM_NOMATCH);
6239742fdb4Schristos 			}
624e2b1b9c0Schristos 
625e2b1b9c0Schristos 			/* Optimize for pattern with * at end or before /. */
6269742fdb4Schristos 			if (c == EOS) {
6279742fdb4Schristos 				if (flags & FNM_PATHNAME) {
628e2b1b9c0Schristos 					return ((flags & FNM_LEADING_DIR) ||
6299742fdb4Schristos 								index(string,
6309742fdb4Schristos 								      '/') ==
6319742fdb4Schristos 									NULL
6329742fdb4Schristos 							? 0
6339742fdb4Schristos 							: FNM_NOMATCH);
6349742fdb4Schristos 				} else {
635e2b1b9c0Schristos 					return (0);
6369742fdb4Schristos 				}
6379742fdb4Schristos 			} else if (c == '/' && flags & FNM_PATHNAME) {
6389742fdb4Schristos 				if ((string = index(string, '/')) == NULL) {
639e2b1b9c0Schristos 					return (FNM_NOMATCH);
6409742fdb4Schristos 				}
641e2b1b9c0Schristos 				break;
642e2b1b9c0Schristos 			}
643e2b1b9c0Schristos 
644e2b1b9c0Schristos 			/* General case, use recursion. */
645e2b1b9c0Schristos 			while ((test = *string) != EOS) {
646e2b1b9c0Schristos 				if (!fnmatch(pattern, string,
647*4ac1c27eSchristos 					     flags & ~FNM_PERIOD))
648*4ac1c27eSchristos 				{
649e2b1b9c0Schristos 					return (0);
6509742fdb4Schristos 				}
6519742fdb4Schristos 				if (test == '/' && flags & FNM_PATHNAME) {
652e2b1b9c0Schristos 					break;
6539742fdb4Schristos 				}
654e2b1b9c0Schristos 				++string;
655e2b1b9c0Schristos 			}
656e2b1b9c0Schristos 			return (FNM_NOMATCH);
657e2b1b9c0Schristos 		case '[':
6589742fdb4Schristos 			if (*string == EOS) {
659e2b1b9c0Schristos 				return (FNM_NOMATCH);
6609742fdb4Schristos 			}
6619742fdb4Schristos 			if (*string == '/' && (flags & FNM_PATHNAME)) {
662e2b1b9c0Schristos 				return (FNM_NOMATCH);
6639742fdb4Schristos 			}
664e2b1b9c0Schristos 			if (*string == '.' && (flags & FNM_PERIOD) &&
665e2b1b9c0Schristos 			    (string == stringstart ||
666e2b1b9c0Schristos 			     ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
6679742fdb4Schristos 			{
668e2b1b9c0Schristos 				return (FNM_NOMATCH);
6699742fdb4Schristos 			}
670e2b1b9c0Schristos 
671e2b1b9c0Schristos 			switch (rangematch(pattern, *string, flags, &newp)) {
672e2b1b9c0Schristos 			case RANGE_ERROR:
673e2b1b9c0Schristos 				goto norm;
674e2b1b9c0Schristos 			case RANGE_MATCH:
675e2b1b9c0Schristos 				pattern = newp;
676e2b1b9c0Schristos 				break;
677e2b1b9c0Schristos 			case RANGE_NOMATCH:
678e2b1b9c0Schristos 				return (FNM_NOMATCH);
679e2b1b9c0Schristos 			}
680e2b1b9c0Schristos 			++string;
681e2b1b9c0Schristos 			break;
682e2b1b9c0Schristos 		case '\\':
683e2b1b9c0Schristos 			if (!(flags & FNM_NOESCAPE)) {
684e2b1b9c0Schristos 				if ((c = *pattern++) == EOS) {
685e2b1b9c0Schristos 					c = '\\';
686e2b1b9c0Schristos 					--pattern;
687e2b1b9c0Schristos 				}
688e2b1b9c0Schristos 			}
689c0b5d9fbSchristos 			FALLTHROUGH;
690e2b1b9c0Schristos 		default:
691e2b1b9c0Schristos 		norm:
6929742fdb4Schristos 			if (c == *string) {
6939742fdb4Schristos 			} else if ((flags & FNM_CASEFOLD) &&
694e2b1b9c0Schristos 				   (tolower((unsigned char)c) ==
695e2b1b9c0Schristos 				    tolower((unsigned char)*string)))
6969742fdb4Schristos 			{
6979742fdb4Schristos 			} else {
698e2b1b9c0Schristos 				return (FNM_NOMATCH);
6999742fdb4Schristos 			}
700e2b1b9c0Schristos 			string++;
701e2b1b9c0Schristos 			break;
702e2b1b9c0Schristos 		}
703c0b5d9fbSchristos 	}
704c0b5d9fbSchristos 	UNREACHABLE();
705e2b1b9c0Schristos }
706e2b1b9c0Schristos 
707e2b1b9c0Schristos static int
rangematch(const char * pattern,char test,int flags,char ** newp)708e2b1b9c0Schristos rangematch(const char *pattern, char test, int flags, char **newp) {
709e2b1b9c0Schristos 	int negate, ok;
710e2b1b9c0Schristos 	char c, c2;
711e2b1b9c0Schristos 
712e2b1b9c0Schristos 	/*
713e2b1b9c0Schristos 	 * A bracket expression starting with an unquoted circumflex
714e2b1b9c0Schristos 	 * character produces unspecified results (IEEE 1003.2-1992,
715e2b1b9c0Schristos 	 * 3.13.2).  This implementation treats it like '!', for
716e2b1b9c0Schristos 	 * consistency with the regular expression syntax.
717e2b1b9c0Schristos 	 * J.T. Conklin (conklin@ngai.kaleida.com)
718e2b1b9c0Schristos 	 */
7199742fdb4Schristos 	if ((negate = (*pattern == '!' || *pattern == '^'))) {
720e2b1b9c0Schristos 		++pattern;
7219742fdb4Schristos 	}
722e2b1b9c0Schristos 
7239742fdb4Schristos 	if (flags & FNM_CASEFOLD) {
724e2b1b9c0Schristos 		test = tolower((unsigned char)test);
7259742fdb4Schristos 	}
726e2b1b9c0Schristos 
727e2b1b9c0Schristos 	/*
728e2b1b9c0Schristos 	 * A right bracket shall lose its special meaning and represent
729e2b1b9c0Schristos 	 * itself in a bracket expression if it occurs first in the list.
730e2b1b9c0Schristos 	 * -- POSIX.2 2.8.3.2
731e2b1b9c0Schristos 	 */
732e2b1b9c0Schristos 	ok = 0;
733e2b1b9c0Schristos 	c = *pattern++;
734e2b1b9c0Schristos 	do {
7359742fdb4Schristos 		if (c == '\\' && !(flags & FNM_NOESCAPE)) {
736e2b1b9c0Schristos 			c = *pattern++;
7379742fdb4Schristos 		}
7389742fdb4Schristos 		if (c == EOS) {
739e2b1b9c0Schristos 			return (RANGE_ERROR);
7409742fdb4Schristos 		}
741e2b1b9c0Schristos 
7429742fdb4Schristos 		if (c == '/' && (flags & FNM_PATHNAME)) {
743e2b1b9c0Schristos 			return (RANGE_NOMATCH);
7449742fdb4Schristos 		}
745e2b1b9c0Schristos 
7469742fdb4Schristos 		if (flags & FNM_CASEFOLD) {
747e2b1b9c0Schristos 			c = tolower((unsigned char)c);
7489742fdb4Schristos 		}
749e2b1b9c0Schristos 
7509742fdb4Schristos 		if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS &&
751*4ac1c27eSchristos 		    c2 != ']')
752*4ac1c27eSchristos 		{
753e2b1b9c0Schristos 			pattern += 2;
7549742fdb4Schristos 			if (c2 == '\\' && !(flags & FNM_NOESCAPE)) {
755e2b1b9c0Schristos 				c2 = *pattern++;
7569742fdb4Schristos 			}
7579742fdb4Schristos 			if (c2 == EOS) {
758e2b1b9c0Schristos 				return (RANGE_ERROR);
7599742fdb4Schristos 			}
760e2b1b9c0Schristos 
7619742fdb4Schristos 			if (flags & FNM_CASEFOLD) {
762e2b1b9c0Schristos 				c2 = tolower((unsigned char)c2);
7639742fdb4Schristos 			}
764e2b1b9c0Schristos 
7659742fdb4Schristos 			if (c <= test && test <= c2) {
766e2b1b9c0Schristos 				ok = 1;
7679742fdb4Schristos 			}
7689742fdb4Schristos 		} else if (c == test) {
769e2b1b9c0Schristos 			ok = 1;
7709742fdb4Schristos 		}
771e2b1b9c0Schristos 	} while ((c = *pattern++) != ']');
772e2b1b9c0Schristos 
773e2b1b9c0Schristos 	*newp = (char *)(uintptr_t)pattern;
774e2b1b9c0Schristos 	return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
775e2b1b9c0Schristos }
776