1 /*	$NetBSD: sdlz_helper.c,v 1.5 2014/12/10 04:37:55 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) 1999-2001  Internet Software Consortium.
40  *
41  * Permission to use, copy, modify, and distribute this software for any
42  * purpose with or without fee is hereby granted, provided that the above
43  * copyright notice and this permission notice appear in all copies.
44  *
45  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
46  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
48  * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
49  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
50  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
51  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
52  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
53  */
54 
55 #include <config.h>
56 
57 #include <dns/log.h>
58 #include <dns/result.h>
59 
60 #include <isc/mem.h>
61 #include <isc/result.h>
62 #include <isc/string.h>
63 #include <isc/util.h>
64 
65 #include <dlz/sdlz_helper.h>
66 
67 /*
68  * sdlz helper methods
69  */
70 
71 /*%
72  * properly destroys a querylist by de-allocating the
73  * memory for each query segment, and then the list itself
74  */
75 
76 static void
77 destroy_querylist(isc_mem_t *mctx, query_list_t **querylist)
78 {
79 	query_segment_t *tseg = NULL;
80 	query_segment_t *nseg = NULL;
81 
82 	REQUIRE(mctx != NULL);
83 
84 	/* if query list is null, nothing to do */
85 	if (*querylist == NULL)
86 		return;
87 
88 	/* start at the top of the list */
89 	nseg = ISC_LIST_HEAD(**querylist);
90 	while (nseg != NULL) {	/* loop, until end of list */
91 		tseg = nseg;
92 		/*
93 		 * free the query segment's text string but only if it
94 		 * was really a query segment, and not a pointer to
95 		 * %zone%, or %record%, or %client%
96 		*/
97 		if (tseg->sql != NULL && tseg->direct == isc_boolean_true)
98 			isc_mem_free(mctx, tseg->sql);
99 		/* get the next query segment, before we destroy this one. */
100 		nseg = ISC_LIST_NEXT(nseg, link);
101 		/* deallocate this query segment. */
102 		isc_mem_put(mctx, tseg, sizeof(query_segment_t));
103 	}
104 	/* deallocate the query segment list */
105 	isc_mem_put(mctx, *querylist, sizeof(query_list_t));
106 }
107 
108 /*% constructs a query list by parsing a string into query segments */
109 static isc_result_t
110 build_querylist(isc_mem_t *mctx, const char *query_str, char **zone,
111 		char **record, char **client, query_list_t **querylist,
112 		unsigned int flags)
113 {
114 	isc_result_t result;
115 	isc_boolean_t foundzone = isc_boolean_false;
116 	isc_boolean_t foundrecord = isc_boolean_false;
117 	isc_boolean_t foundclient = isc_boolean_false;
118 	char *temp_str = NULL;
119 	char *right_str = NULL;
120 	query_list_t *tql;
121 	query_segment_t *tseg = NULL;
122 
123 	REQUIRE(querylist != NULL && *querylist == NULL);
124 	REQUIRE(mctx != NULL);
125 
126 	/* if query string is null, or zero length */
127 	if (query_str == NULL || strlen(query_str) < 1) {
128 		if ((flags & SDLZH_REQUIRE_QUERY) == 0)
129 			/* we don't need it were ok. */
130 			return (ISC_R_SUCCESS);
131 		else
132 			/* we did need it, PROBLEM!!! */
133 			return (ISC_R_FAILURE);
134 	}
135 
136 	/* allocate memory for query list */
137 	tql = isc_mem_get(mctx, sizeof(query_list_t));
138 	/* couldn't allocate memory.  Problem!! */
139 	if (tql == NULL)
140 		return (ISC_R_NOMEMORY);
141 
142 	/* initialize the query segment list */
143 	ISC_LIST_INIT(*tql);
144 
145 	/* make a copy of query_str so we can chop it up */
146 	temp_str = right_str = isc_mem_strdup(mctx, query_str);
147 	/* couldn't make a copy, problem!! */
148 	if (right_str == NULL) {
149 		result = ISC_R_NOMEMORY;
150 		goto cleanup;
151 	}
152 
153 	/* loop through the string and chop it up */
154 	while (right_str != NULL) {
155 		/* allocate memory for tseg */
156 		tseg = isc_mem_get(mctx, sizeof(query_segment_t));
157 		if (tseg  == NULL) {	/* no memory, clean everything up. */
158 			result = ISC_R_NOMEMORY;
159 			goto cleanup;
160 		}
161 		tseg->sql = NULL;
162 		tseg->direct = isc_boolean_false;
163 		/* initialize the query segment link */
164 		ISC_LINK_INIT(tseg, link);
165 		/* append the query segment to the list */
166 		ISC_LIST_APPEND(*tql, tseg, link);
167 
168 		/*
169 		 * split string at the first "$". set query segment to
170 		 * left portion
171 		 */
172 		tseg->sql = isc_mem_strdup(mctx,
173 					   isc_string_separate(&right_str,
174 							       "$"));
175 		if (tseg->sql == NULL) {
176 			/* no memory, clean everything up. */
177 			result = ISC_R_NOMEMORY;
178 			goto cleanup;
179 		}
180 		/* tseg->sql points directly to a string. */
181 		tseg->direct = isc_boolean_true;
182 		tseg->strlen = strlen(tseg->sql);
183 
184 		/* check if we encountered "$zone$" token */
185 		if (strcasecmp(tseg->sql, "zone") == 0) {
186 			/*
187 			 * we don't really need, or want the "zone"
188 			 * text, so get rid of it.
189 			 */
190 			isc_mem_free(mctx, tseg->sql);
191 			/* set tseg->sql to in-direct zone string */
192 			tseg->sql = (char**) zone;
193 			tseg->strlen = 0;
194 			/* tseg->sql points in-directly to a string */
195 			tseg->direct = isc_boolean_false;
196 			foundzone = isc_boolean_true;
197 			/* check if we encountered "$record$" token */
198 		} else if (strcasecmp(tseg->sql, "record") == 0) {
199 			/*
200 			 * we don't really need, or want the "record"
201 			 * text, so get rid of it.
202 			 */
203 			isc_mem_free(mctx, tseg->sql);
204 			/* set tseg->sql to in-direct record string */
205 			tseg->sql = (char**) record;
206 			tseg->strlen = 0;
207 			/* tseg->sql points in-directly poinsts to a string */
208 			tseg->direct = isc_boolean_false;
209 			foundrecord = isc_boolean_true;
210 			/* check if we encountered "$client$" token */
211 		} else if (strcasecmp(tseg->sql, "client") == 0) {
212 			/*
213 			 * we don't really need, or want the "client"
214 			 * text, so get rid of it.
215 			 */
216 			isc_mem_free(mctx, tseg->sql);
217 			/* set tseg->sql to in-direct record string */
218 			tseg->sql = (char**) client;
219 			tseg->strlen = 0;
220 			/* tseg->sql points in-directly poinsts to a string */
221 			tseg->direct = isc_boolean_false;
222 			foundclient = isc_boolean_true;
223 		}
224 	}
225 
226 	/* we don't need temp_str any more */
227 	isc_mem_free(mctx, temp_str);
228 	/*
229 	 * add checks later to verify zone and record are found if
230 	 * necessary.
231 	 */
232 
233 	/* if this query requires %client%, make sure we found it */
234 	if (((flags & SDLZH_REQUIRE_CLIENT) != 0) && (!foundclient) ) {
235 		/* Write error message to log */
236 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
237 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
238 			      "Required token $client$ not found.");
239 		result = ISC_R_FAILURE;
240 		goto flag_fail;
241 	}
242 
243 	/* if this query requires %record%, make sure we found it */
244 	if (((flags & SDLZH_REQUIRE_RECORD) != 0) && (!foundrecord) ) {
245 		/* Write error message to log */
246 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
247 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
248 			      "Required token $record$ not found.");
249 		result = ISC_R_FAILURE;
250 		goto flag_fail;
251 	}
252 
253 	/* if this query requires %zone%, make sure we found it */
254 	if (((flags & SDLZH_REQUIRE_ZONE) != 0) && (!foundzone) ) {
255 		/* Write error message to log */
256 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
257 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
258 			      "Required token $zone$ not found.");
259 		result = ISC_R_FAILURE;
260 		goto flag_fail;
261 	}
262 
263 	/* pass back the query list */
264 	*querylist = (query_list_t *) tql;
265 
266 	/* return success */
267 	return (ISC_R_SUCCESS);
268 
269  cleanup:
270 	/* get rid of temp_str */
271 	if (temp_str != NULL)
272 		isc_mem_free(mctx, temp_str);
273 
274  flag_fail:
275 	/* get rid of what was build of the query list */
276 	if (tql != NULL)
277 		destroy_querylist(mctx, &tql);
278 	return result;
279 }
280 
281 /*%
282  * build a query string from query segments, and dynamic segments
283  * dynamic segments replace where the tokens %zone%, %record%, %client%
284  * used to be in our queries from named.conf
285  */
286 char *
287 sdlzh_build_querystring(isc_mem_t *mctx, query_list_t *querylist)
288 {
289 	query_segment_t *tseg = NULL;
290 	unsigned int length = 0;
291 	char *qs = NULL;
292 
293 	REQUIRE(mctx != NULL);
294 	REQUIRE(querylist != NULL);
295 
296 	/* start at the top of the list */
297 	tseg = ISC_LIST_HEAD(*querylist);
298 	while (tseg != NULL) {
299 		/*
300 		 * if this is a query segment, use the
301 		 * precalculated string length
302 		 */
303 		if (tseg->direct == isc_boolean_true)
304 			length += tseg->strlen;
305 		else	/* calculate string length for dynamic segments. */
306 			length += strlen(* (char**) tseg->sql);
307 		/* get the next segment */
308 		tseg = ISC_LIST_NEXT(tseg, link);
309 	}
310 
311 	/* allocate memory for the string */
312 	qs = isc_mem_allocate(mctx, length + 1);
313 	/* couldn't allocate memory,  We need more ram! */
314 	if (qs == NULL)
315 		return NULL;
316 
317 	*qs = 0;
318 	/* start at the top of the list again */
319 	tseg = ISC_LIST_HEAD(*querylist);
320 	while (tseg != NULL) {
321 		if (tseg->direct == isc_boolean_true)
322 			/* query segments */
323 			strcat(qs, tseg->sql);
324 		else
325 			/* dynamic segments */
326 			strcat(qs, * (char**) tseg->sql);
327 		/* get the next segment */
328 		tseg = ISC_LIST_NEXT(tseg, link);
329 	}
330 
331 	return qs;
332 }
333 
334 /*% constructs a sql dbinstance (DBI) */
335 isc_result_t
336 sdlzh_build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str,
337 			 const char *allowxfr_str, const char *authority_str,
338 			 const char *findzone_str, const char *lookup_str,
339 			 const char *countzone_str, dbinstance_t **dbi)
340 {
341 
342 	isc_result_t result;
343 	dbinstance_t *db = NULL;
344 
345 	REQUIRE(dbi != NULL && *dbi == NULL);
346 	REQUIRE(mctx != NULL);
347 
348 	/* allocate and zero memory for driver structure */
349 	db = isc_mem_get(mctx, sizeof(dbinstance_t));
350 	if (db == NULL) {
351 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
352 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
353 			      "Could not allocate memory for "
354 			      "database instance object.");
355 		return (ISC_R_NOMEMORY);
356 	}
357 	memset(db, 0, sizeof(dbinstance_t));
358 	db->dbconn = NULL;
359 	db->client = NULL;
360 	db->record = NULL;
361 	db->zone = NULL;
362 	db->mctx = NULL;
363 	db->query_buf = NULL;
364 	db->allnodes_q = NULL;
365 	db->allowxfr_q = NULL;
366 	db->authority_q = NULL;
367 	db->findzone_q = NULL;
368 	db->countzone_q = NULL;
369 	db->lookup_q = NULL;
370 
371 	/* attach to the memory context */
372 	isc_mem_attach(mctx, &db->mctx);
373 
374 	/* initialize the reference count mutex */
375 	result = isc_mutex_init(&db->instance_lock);
376 	if (result != ISC_R_SUCCESS) {
377 		UNEXPECTED_ERROR(__FILE__, __LINE__,
378 				 "isc_mutex_init() failed: %s",
379 				 isc_result_totext(result));
380 		goto cleanup;
381 	}
382 
383 	/* build the all nodes query list */
384 	result = build_querylist(mctx, allnodes_str, &db->zone,
385 				 &db->record, &db->client,
386 				 &db->allnodes_q, SDLZH_REQUIRE_ZONE);
387 	/* if unsuccessful, log err msg and cleanup */
388 	if (result != ISC_R_SUCCESS) {
389 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
390 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
391 			      "Could not build all nodes query list");
392 		goto cleanup;
393 	}
394 
395 	/* build the allow zone transfer query list */
396 	result = build_querylist(mctx, allowxfr_str, &db->zone,
397 				 &db->record, &db->client,
398 				 &db->allowxfr_q,
399 				 SDLZH_REQUIRE_ZONE | SDLZH_REQUIRE_CLIENT);
400 	/* if unsuccessful, log err msg and cleanup */
401 	if (result != ISC_R_SUCCESS) {
402 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
403 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
404 			      "Could not build allow xfr query list");
405 		goto cleanup;
406 	}
407 
408 	/* build the authority query, query list */
409 	result = build_querylist(mctx, authority_str, &db->zone,
410 				 &db->record, &db->client,
411 				 &db->authority_q, SDLZH_REQUIRE_ZONE);
412 	/* if unsuccessful, log err msg and cleanup */
413 	if (result != ISC_R_SUCCESS) {
414 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
415 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
416 			      "Could not build authority query list");
417 		goto cleanup;
418 	}
419 
420 	/* build findzone query, query list */
421 	result = build_querylist(mctx, findzone_str, &db->zone,
422 				 &db->record, &db->client,
423 				 &db->findzone_q, SDLZH_REQUIRE_ZONE);
424 	/* if unsuccessful, log err msg and cleanup */
425 	if (result != ISC_R_SUCCESS) {
426 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
427 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
428 			      "Could not build find zone query list");
429 		goto cleanup;
430 	}
431 
432 	/* build countzone query, query list */
433 	result = build_querylist(mctx, countzone_str, &db->zone,
434 				 &db->record, &db->client,
435 				 &db->countzone_q, SDLZH_REQUIRE_ZONE);
436 	/* if unsuccessful, log err msg and cleanup */
437 	if (result != ISC_R_SUCCESS) {
438 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
439 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
440 			      "Could not build count zone query list");
441 		goto cleanup;
442 	}
443 
444 	/* build lookup query, query list */
445 	result = build_querylist(mctx, lookup_str, &db->zone,
446 				 &db->record, &db->client,
447 				 &db->lookup_q, SDLZH_REQUIRE_RECORD);
448 	/* if unsuccessful, log err msg and cleanup */
449 	if (result != ISC_R_SUCCESS) {
450 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
451 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
452 			      "Could not build lookup query list");
453 		goto cleanup;
454 	}
455 
456 	/* pass back the db instance */
457 	*dbi = (dbinstance_t *) db;
458 
459 	/* return success */
460 	return (ISC_R_SUCCESS);
461 
462  cleanup:
463 	/* destroy whatever was build of the db instance */
464 	destroy_sqldbinstance(db);
465 	/* return failure */
466 	return (ISC_R_FAILURE);
467 }
468 
469 void
470 sdlzh_destroy_sqldbinstance(dbinstance_t *dbi)
471 {
472 	isc_mem_t *mctx;
473 
474 	/* save mctx for later */
475 	mctx = dbi->mctx;
476 
477 	/* destroy any query lists we created */
478 	destroy_querylist(mctx, &dbi->allnodes_q);
479 	destroy_querylist(mctx, &dbi->allowxfr_q);
480 	destroy_querylist(mctx, &dbi->authority_q);
481 	destroy_querylist(mctx, &dbi->findzone_q);
482 	destroy_querylist(mctx, &dbi->countzone_q);
483 	destroy_querylist(mctx, &dbi->lookup_q);
484 
485 	/* get rid of the mutex */
486 	(void) isc_mutex_destroy(&dbi->instance_lock);
487 
488 	/* return, and detach the memory */
489 	isc_mem_put(mctx, dbi, sizeof(dbinstance_t));
490 	isc_mem_detach(&mctx);
491 }
492 
493 char *
494 sdlzh_get_parameter_value(isc_mem_t *mctx, const char *input, const char* key)
495 {
496 	int keylen;
497 	char *keystart;
498 	char value[255];
499 	int i;
500 
501 	if (key == NULL || input == NULL || strlen(input) < 1)
502 		return NULL;
503 
504 	keylen = strlen(key);
505 
506 	if (keylen < 1)
507 		return NULL;
508 
509 	keystart = strstr(input, key);
510 
511 	if (keystart == NULL)
512 		return NULL;
513 
514 	REQUIRE(mctx != NULL);
515 
516 	for (i = 0; i < 255; i++) {
517 		value[i] = keystart[keylen + i];
518 		if (value[i] == ' ' || value[i] == '\0') {
519 			value[i] = '\0';
520 			break;
521 		}
522 	}
523 
524 	return isc_mem_strdup(mctx, value);
525 }
526