xref: /minix3/external/bsd/bind/dist/contrib/dlz/modules/common/dlz_dbi.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1 /*	$NetBSD: dlz_dbi.c,v 1.1.1.4 2014/12/10 03:34:31 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the
8  * above copyright notice and this permission notice appear in all
9  * copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
12  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
13  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14  * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
16  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
17  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
18  * USE OR PERFORMANCE OF THIS SOFTWARE.
19  *
20  * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
21  * conceived and contributed by Rob Butler.
22  *
23  * Permission to use, copy, modify, and distribute this software for any
24  * purpose with or without fee is hereby granted, provided that the
25  * above copyright notice and this permission notice appear in all
26  * copies.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
29  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
31  * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
32  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
33  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
34  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
35  * USE OR PERFORMANCE OF THIS SOFTWARE.
36  */
37 
38 /*
39  * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
40  * Copyright (C) 1999-2001  Internet Software Consortium.
41  *
42  * Permission to use, copy, modify, and/or distribute this software for any
43  * purpose with or without fee is hereby granted, provided that the above
44  * copyright notice and this permission notice appear in all copies.
45  *
46  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
47  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
48  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
49  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
50  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
51  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
52  * PERFORMANCE OF THIS SOFTWARE.
53  */
54 
55 #include <stdio.h>
56 #include <string.h>
57 #include <stdarg.h>
58 #include <stdint.h>
59 #include <stdlib.h>
60 #include <ctype.h>
61 
62 #include <sys/errno.h>
63 
64 #include <dlz_minimal.h>
65 #include <dlz_list.h>
66 #include <dlz_dbi.h>
67 #include <dlz_pthread.h>
68 
69 /*%
70  * properly destroys a querylist by de-allocating the
71  * memory for each query segment, and then the list itself
72  */
73 
74 void
destroy_querylist(query_list_t ** querylist)75 destroy_querylist(query_list_t **querylist) {
76 	query_segment_t *tseg = NULL;
77 	query_segment_t *nseg = NULL;
78 
79 	/* if query list is null, nothing to do */
80 	if (*querylist == NULL)
81 		return;
82 
83 	/* start at the top of the list */
84 	nseg = DLZ_LIST_HEAD(**querylist);
85 	while (nseg != NULL) {	/* loop, until end of list */
86 		tseg = nseg;
87 		/*
88 		 * free the query segment's text string but only if it
89 		 * was really a query segment, and not a pointer to
90 		 * %zone%, or %record%, or %client%
91 		*/
92 		if (tseg->cmd != NULL && tseg->direct == ISC_TRUE)
93 			free(tseg->cmd);
94 		/* get the next query segment, before we destroy this one. */
95 		nseg = DLZ_LIST_NEXT(nseg, link);
96 		/* deallocate this query segment. */
97 		free(tseg);
98 	}
99 	/* deallocate the query segment list */
100 	free(*querylist);
101 }
102 
103 /*% constructs a query list by parsing a string into query segments */
104 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)105 build_querylist(const char *query_str, char **zone, char **record,
106 		char **client, query_list_t **querylist, unsigned int flags,
107 		log_t log)
108 {
109 	isc_result_t result;
110 	isc_boolean_t foundzone = ISC_FALSE;
111 	isc_boolean_t foundrecord = ISC_FALSE;
112 	isc_boolean_t foundclient = ISC_FALSE;
113 	char *temp_str = NULL;
114 	char *right_str = NULL;
115 	query_list_t *tql;
116 	query_segment_t *tseg = NULL;
117 
118 	/* if query string is null, or zero length */
119 	if (query_str == NULL || strlen(query_str) < 1) {
120 		if ((flags & REQUIRE_QUERY) == 0)
121 			/* we don't need it were ok. */
122 			return (ISC_R_SUCCESS);
123 		else
124 			/* we did need it, PROBLEM!!! */
125 			return (ISC_R_FAILURE);
126 	}
127 
128 	/* allocate memory for query list */
129 	tql = calloc(1, sizeof(query_list_t));
130 	/* couldn't allocate memory.  Problem!! */
131 	if (tql == NULL)
132 		return (ISC_R_NOMEMORY);
133 
134 	/* initialize the query segment list */
135 	DLZ_LIST_INIT(*tql);
136 
137 	/* make a copy of query_str so we can chop it up */
138 	temp_str = right_str = strdup(query_str);
139 	/* couldn't make a copy, problem!! */
140 	if (right_str == NULL) {
141 		result = ISC_R_NOMEMORY;
142 		goto cleanup;
143 	}
144 
145 	/* loop through the string and chop it up */
146 	while (right_str != NULL) {
147 		/* allocate memory for tseg */
148 		tseg = calloc(1, sizeof(query_segment_t));
149 		if (tseg  == NULL) {	/* no memory, clean everything up. */
150 			result = ISC_R_NOMEMORY;
151 			goto cleanup;
152 		}
153 		tseg->cmd = NULL;
154 		tseg->direct = ISC_FALSE;
155 		/* initialize the query segment link */
156 		DLZ_LINK_INIT(tseg, link);
157 		/* append the query segment to the list */
158 		DLZ_LIST_APPEND(*tql, tseg, link);
159 
160 		/*
161 		 * split string at the first "$". set query segment to
162 		 * left portion
163 		 */
164 		tseg->cmd = strdup(strsep(&right_str, "$"));
165 		if (tseg->cmd == NULL) {
166 			/* no memory, clean everything up. */
167 			result = ISC_R_NOMEMORY;
168 			goto cleanup;
169 		}
170 		/* tseg->cmd points directly to a string. */
171 		tseg->direct = ISC_TRUE;
172 		tseg->strlen = strlen(tseg->cmd);
173 
174 		/* check if we encountered "$zone$" token */
175 		if (strcasecmp(tseg->cmd, "zone") == 0) {
176 			/*
177 			 * we don't really need, or want the "zone"
178 			 * text, so get rid of it.
179 			 */
180 			free(tseg->cmd);
181 			/* set tseg->cmd to in-direct zone string */
182 			tseg->cmd = (char**) zone;
183 			tseg->strlen = 0;
184 			/* tseg->cmd points in-directly to a string */
185 			tseg->direct = ISC_FALSE;
186 			foundzone = ISC_TRUE;
187 			/* check if we encountered "$record$" token */
188 		} else if (strcasecmp(tseg->cmd, "record") == 0) {
189 			/*
190 			 * we don't really need, or want the "record"
191 			 * text, so get rid of it.
192 			 */
193 			free(tseg->cmd);
194 			/* set tseg->cmd to in-direct record string */
195 			tseg->cmd = (char**) record;
196 			tseg->strlen = 0;
197 			/* tseg->cmd points in-directly poinsts to a string */
198 			tseg->direct = ISC_FALSE;
199 			foundrecord = ISC_TRUE;
200 			/* check if we encountered "$client$" token */
201 		} else if (strcasecmp(tseg->cmd, "client") == 0) {
202 			/*
203 			 * we don't really need, or want the "client"
204 			 * text, so get rid of it.
205 			 */
206 			free(tseg->cmd);
207 			/* set tseg->cmd to in-direct record string */
208 			tseg->cmd = (char**) client;
209 			tseg->strlen = 0;
210 			/* tseg->cmd points in-directly poinsts to a string */
211 			tseg->direct = ISC_FALSE;
212 			foundclient = ISC_TRUE;
213 		}
214 	}
215 
216 	/* we don't need temp_str any more */
217 	free(temp_str);
218 	/*
219 	 * add checks later to verify zone and record are found if
220 	 * necessary.
221 	 */
222 
223 	/* if this query requires %client%, make sure we found it */
224 	if (((flags & REQUIRE_CLIENT) != 0) && (!foundclient) ) {
225 		/* Write error message to log */
226 		if (log != NULL)
227 			log(ISC_LOG_ERROR,
228 			    "Required token $client$ not found.");
229 		result = ISC_R_FAILURE;
230 		goto flag_fail;
231 	}
232 
233 	/* if this query requires %record%, make sure we found it */
234 	if (((flags & REQUIRE_RECORD) != 0) && (!foundrecord) ) {
235 		/* Write error message to log */
236 		if (log != NULL)
237 			log(ISC_LOG_ERROR,
238 			    "Required token $record$ not found.");
239 		result = ISC_R_FAILURE;
240 		goto flag_fail;
241 	}
242 
243 	/* if this query requires %zone%, make sure we found it */
244 	if (((flags & REQUIRE_ZONE) != 0) && (!foundzone) ) {
245 		/* Write error message to log */
246 		if (log != NULL)
247 			log(ISC_LOG_ERROR, "Required token $zone$ not found.");
248 		result = ISC_R_FAILURE;
249 		goto flag_fail;
250 	}
251 
252 	/* pass back the query list */
253 	*querylist = (query_list_t *) tql;
254 
255 	/* return success */
256 	return (ISC_R_SUCCESS);
257 
258  cleanup:
259 	/* get rid of temp_str */
260 	if (temp_str != NULL)
261 		free(temp_str);
262 
263  flag_fail:
264 	/* get rid of what was build of the query list */
265 	if (tql != NULL)
266 		destroy_querylist(&tql);
267 	return (result);
268 }
269 
270 /*%
271  * build a query string from query segments, and dynamic segments
272  * dynamic segments replace where the tokens %zone%, %record%, %client%
273  * used to be in our queries from named.conf
274  */
275 char *
build_querystring(query_list_t * querylist)276 build_querystring(query_list_t *querylist) {
277 	query_segment_t *tseg = NULL;
278 	unsigned int length = 0;
279 	char *qs = NULL;
280 
281 	/* start at the top of the list */
282 	tseg = DLZ_LIST_HEAD(*querylist);
283 	while (tseg != NULL) {
284 		/*
285 		 * if this is a query segment, use the
286 		 * precalculated string length
287 		 */
288 		if (tseg->direct == ISC_TRUE)
289 			length += tseg->strlen;
290 		else	/* calculate string length for dynamic segments. */
291 			length += strlen(* (char**) tseg->cmd);
292 		/* get the next segment */
293 		tseg = DLZ_LIST_NEXT(tseg, link);
294 	}
295 
296 	qs = malloc(length + 1);
297 	if (qs == NULL)
298 		return (NULL);
299 
300 	*qs = '\0';
301 	/* start at the top of the list again */
302 	tseg = DLZ_LIST_HEAD(*querylist);
303 	while (tseg != NULL) {
304 		if (tseg->direct == ISC_TRUE)
305 			/* query segments */
306 			strcat(qs, tseg->cmd);
307 		else
308 			/* dynamic segments */
309 			strcat(qs, * (char**) tseg->cmd);
310 		/* get the next segment */
311 		tseg = DLZ_LIST_NEXT(tseg, link);
312 	}
313 
314 	return (qs);
315 }
316 
317 /*% constructs a dbinstance (DBI) */
318 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)319 build_dbinstance(const char *allnodes_str, const char *allowxfr_str,
320 		 const char *authority_str, const char *findzone_str,
321 		 const char *lookup_str, const char *countzone_str,
322 		 dbinstance_t **dbi, log_t log)
323 {
324 
325 	isc_result_t result;
326 	dbinstance_t *db = NULL;
327 	int err;
328 
329 	/* allocate and zero memory for driver structure */
330 	db = calloc(1, sizeof(dbinstance_t));
331 	if (db == NULL) {
332 		if (log != NULL)
333 			log(ISC_LOG_ERROR,
334 			    "Could not allocate memory for "
335 			    "database instance object.");
336 		return (ISC_R_NOMEMORY);
337 	}
338 	memset(db, 0, sizeof(dbinstance_t));
339 	db->dbconn = NULL;
340 	db->client = NULL;
341 	db->record = NULL;
342 	db->zone = NULL;
343 	db->query_buf = NULL;
344 	db->allnodes_q = NULL;
345 	db->allowxfr_q = NULL;
346 	db->authority_q = NULL;
347 	db->findzone_q = NULL;
348 	db->countzone_q = NULL;
349 	db->lookup_q = NULL;
350 
351 	/* initialize the reference count mutex */
352 	err = dlz_mutex_init(&db->lock, NULL);
353 	if (err == ENOMEM) {
354 		result = ISC_R_NOMEMORY;
355 		goto cleanup;
356 	} else if (err != 0) {
357 		result = ISC_R_UNEXPECTED;
358 		goto cleanup;
359 	}
360 
361 	/* build the all nodes query list */
362 	result = build_querylist(allnodes_str, &db->zone, &db->record,
363 				 &db->client, &db->allnodes_q,
364 				 REQUIRE_ZONE, log);
365 	/* if unsuccessful, log err msg and cleanup */
366 	if (result != ISC_R_SUCCESS) {
367 		if (log != NULL)
368 			log(ISC_LOG_ERROR,
369 			    "Could not build all nodes query list");
370 		goto cleanup;
371 	}
372 
373 	/* build the allow zone transfer query list */
374 	result = build_querylist(allowxfr_str, &db->zone, &db->record,
375 				 &db->client, &db->allowxfr_q,
376 				 REQUIRE_ZONE | REQUIRE_CLIENT,
377 				 log);
378 	/* if unsuccessful, log err msg and cleanup */
379 	if (result != ISC_R_SUCCESS) {
380 		if (log != NULL)
381 			log(ISC_LOG_ERROR,
382 			    "Could not build allow xfr query list");
383 		goto cleanup;
384 	}
385 
386 	/* build the authority query, query list */
387 	result = build_querylist(authority_str, &db->zone, &db->record,
388 				 &db->client, &db->authority_q,
389 				 REQUIRE_ZONE, log);
390 	/* if unsuccessful, log err msg and cleanup */
391 	if (result != ISC_R_SUCCESS) {
392 		if (log != NULL)
393 			log(ISC_LOG_ERROR,
394 			    "Could not build authority query list");
395 		goto cleanup;
396 	}
397 
398 	/* build findzone query, query list */
399 	result = build_querylist(findzone_str, &db->zone, &db->record,
400 				 &db->client, &db->findzone_q,
401 				 REQUIRE_ZONE, log);
402 	/* if unsuccessful, log err msg and cleanup */
403 	if (result != ISC_R_SUCCESS) {
404 		if (log != NULL)
405 			log(ISC_LOG_ERROR,
406 			    "Could not build find zone query list");
407 		goto cleanup;
408 	}
409 
410 	/* build countzone query, query list */
411 	result = build_querylist(countzone_str, &db->zone, &db->record,
412 				 &db->client, &db->countzone_q,
413 				 REQUIRE_ZONE, log);
414 	/* if unsuccessful, log err msg and cleanup */
415 	if (result != ISC_R_SUCCESS) {
416 		if (log != NULL)
417 			log(ISC_LOG_ERROR,
418 			    "Could not build count zone query list");
419 		goto cleanup;
420 	}
421 
422 	/* build lookup query, query list */
423 	result = build_querylist(lookup_str, &db->zone, &db->record,
424 				 &db->client, &db->lookup_q,
425 				 REQUIRE_RECORD, log);
426 	/* if unsuccessful, log err msg and cleanup */
427 	if (result != ISC_R_SUCCESS) {
428 		if (log != NULL)
429 			log(ISC_LOG_ERROR,
430 			    "Could not build lookup query list");
431 		goto cleanup;
432 	}
433 
434 	/* pass back the db instance */
435 	*dbi = (dbinstance_t *) db;
436 
437 	/* return success */
438 	return (ISC_R_SUCCESS);
439 
440  cleanup:
441 	/* destroy whatever was build of the db instance */
442 	destroy_dbinstance(db);
443 	/* return failure */
444 	return (ISC_R_FAILURE);
445 }
446 
447 void
destroy_dbinstance(dbinstance_t * dbi)448 destroy_dbinstance(dbinstance_t *dbi) {
449 	/* destroy any query lists we created */
450 	destroy_querylist(&dbi->allnodes_q);
451 	destroy_querylist(&dbi->allowxfr_q);
452 	destroy_querylist(&dbi->authority_q);
453 	destroy_querylist(&dbi->findzone_q);
454 	destroy_querylist(&dbi->countzone_q);
455 	destroy_querylist(&dbi->lookup_q);
456 
457 	/* get rid of the mutex */
458 	(void) dlz_mutex_destroy(&dbi->lock);
459 
460 	/* return, and detach the memory */
461 	free(dbi);
462 }
463 
464 char *
get_parameter_value(const char * input,const char * key)465 get_parameter_value(const char *input, const char* key) {
466 	int keylen;
467 	char *keystart;
468 	char value[255];
469 	int i;
470 
471 	if (key == NULL || input == NULL || *input == '\0')
472 		return (NULL);
473 
474 	keylen = strlen(key);
475 
476 	if (keylen < 1)
477 		return (NULL);
478 
479 	keystart = strstr(input, key);
480 
481 	if (keystart == NULL)
482 		return (NULL);
483 
484 	for (i = 0; i < 255; i++) {
485 		value[i] = keystart[keylen + i];
486 		if (isspace(value[i]) || value[i] == '\0') {
487 			value[i] = '\0';
488 			break;
489 		}
490 	}
491 
492 	return (strdup(value));
493 }
494