1 /*	$NetBSD: dlz_bdbhpt_dynamic.c,v 1.1.1.5 2015/07/08 15:37:45 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) 2011  Internet Systems Consortium, Inc. ("ISC")
40  *
41  * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
46  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
47  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
48  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
49  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
50  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
51  * PERFORMANCE OF THIS SOFTWARE.
52  */
53 
54 /*
55  * This is simply a merge of Andrew Tridgell's dlz_example.c and the
56  * original bdb_bdbhpt_driver.c
57  *
58  * This provides the externally loadable bdbhpt DLZ driver, without
59  * update support
60  *
61  */
62 
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <stdarg.h>
67 #include <stdint.h>
68 
69 #include <db.h>
70 
71 #include "dlz_minimal.h"
72 
73 /* should the bdb driver use threads. */
74 #ifdef ISC_PLATFORM_USETHREADS
75 #define bdbhpt_threads DB_THREAD
76 #else
77 #define bdbhpt_threads 0
78 #endif
79 
80 /* bdbhpt database names */
81 #define dlz_data   "dns_data"
82 #define dlz_zone   "dns_zone"
83 #define dlz_xfr    "dns_xfr"
84 #define dlz_client "dns_client"
85 
86 #define dlz_bdbhpt_dynamic_version "0.1"
87 
88 /*
89  * This structure contains all our DB handles and helper functions we
90  * inherit from the dlz_dlopen driver
91  *
92  */
93 typedef struct bdbhpt_instance {
94 	DB_ENV    *dbenv;       /* bdbhpt environment */
95 	DB        *data;        /* dns_data database handle */
96 	DB        *zone;        /* zone database handle */
97 	DB        *xfr;         /* zone xfr database handle */
98 	DB        *client;      /* client database handle */
99 
100 	/* Helper functions from the dlz_dlopen driver */
101 	log_t *log;
102 	dns_sdlz_putrr_t *putrr;
103 	dns_sdlz_putnamedrr_t *putnamedrr;
104 	dns_dlz_writeablezone_t *writeable_zone;
105 } bdbhpt_instance_t;
106 
107 typedef struct bdbhpt_parsed_data {
108 	char *host;
109 	char *type;
110 	int ttl;
111 	char *data;
112 } bdbhpt_parsed_data_t;
113 
114 static void
115 b9_add_helper(struct bdbhpt_instance *db, const char *helper_name, void *ptr);
116 
117 /*%
118  * Reverses a string in place.
119  */
120 static char
bdbhpt_strrev(char * str)121 *bdbhpt_strrev(char *str) {
122 	char *p1, *p2;
123 
124 	if (! str || ! *str)
125 		return str;
126 	for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) {
127 		*p1 ^= *p2;
128 		*p2 ^= *p1;
129 		*p1 ^= *p2;
130 	}
131 	return str;
132 }
133 
134 /*%
135  * Parses the DBT from the Berkeley DB into a parsed_data record
136  * The parsed_data record should be allocated before and passed into the
137  * bdbhpt_parse_data function.  The char (type & data) fields should not
138  * be "free"d as that memory is part of the DBT data field.  It will be
139  * "free"d when the DBT is freed.
140  */
141 
142 static isc_result_t
bdbhpt_parse_data(log_t * log,char * in,bdbhpt_parsed_data_t * pd)143 bdbhpt_parse_data(log_t *log, char *in, bdbhpt_parsed_data_t *pd) {
144 
145 	char *endp, *ttlStr;
146 	char *tmp = in;
147 	char *lastchar = (char *) &tmp[strlen(tmp)];
148 
149 	/*%
150 	 * String should be formatted as:
151 	 *   replication_id
152 	 *   (a space)
153 	 *   host_name
154 	 *   (a space)
155 	 *   ttl
156 	 *   (a space)
157 	 *   type
158 	 *   (a space)
159 	 *   remaining data
160 	 *
161 	 * examples:
162 	 *
163 	 * 9191 host 10 A 127.0.0.1
164 	 * server1_212 host 10 A 127.0.0.2
165 	 * {xxxx-xxxx-xxxx-xxxx-xxxx} host 10 MX 20 mail.example.com
166 	 */
167 
168 	/*
169 	 * we don't need the replication id, so don't
170 	 * bother saving a pointer to it.
171 	 */
172 
173 	/* find space after replication id */
174 	tmp = strchr(tmp, ' ');
175 	/* verify we found a space */
176 	if (tmp == NULL)
177 		return ISC_R_FAILURE;
178 	/* make sure it is safe to increment pointer */
179 	if (++tmp > lastchar)
180 		return ISC_R_FAILURE;
181 
182 	/* save pointer to host */
183 	pd->host = tmp;
184 
185 	/* find space after host and change it to a '\0' */
186 	tmp = strchr(tmp, ' ');
187 	/* verify we found a space */
188 	if (tmp == NULL)
189 		return ISC_R_FAILURE;
190 	/* change the space to a null (string terminator) */
191 	tmp[0] = '\0';
192 	/* make sure it is safe to increment pointer */
193 	if (++tmp > lastchar)
194 		return ISC_R_FAILURE;
195 
196 	/* save pointer to ttl string */
197 	ttlStr = tmp;
198 
199 	/* find space after ttl and change it to a '\0' */
200 	tmp = strchr(tmp, ' ');
201 	/* verify we found a space */
202 	if (tmp == NULL)
203 		return ISC_R_FAILURE;
204 	/* change the space to a null (string terminator) */
205 	tmp[0] = '\0';
206 	/* make sure it is safe to increment pointer */
207 	if (++tmp > lastchar)
208 		return ISC_R_FAILURE;
209 
210 	/* save pointer to dns type */
211 	pd->type = tmp;
212 
213 	/* find space after type and change it to a '\0' */
214 	tmp = strchr(tmp, ' ');
215 	/* verify we found a space */
216 	if (tmp == NULL)
217 		return ISC_R_FAILURE;
218 	/* change the space to a null (string terminator) */
219 	tmp[0] = '\0';
220 	/* make sure it is safe to increment pointer */
221 	if (++tmp > lastchar)
222 		return ISC_R_FAILURE;
223 
224 	/* save pointer to remainder of DNS data */
225 	pd->data = tmp;
226 
227 	/* convert ttl string to integer */
228 	pd->ttl = strtol(ttlStr, &endp, 10);
229 	if (*endp != '\0' || pd->ttl < 0) {
230 		log(ISC_LOG_ERROR,
231 				"bdbhpt_dynamic: "
232 				"ttl must be a positive number");
233 		return ISC_R_FAILURE;
234 	}
235 
236 	/* if we get this far everything should have worked. */
237 	return ISC_R_SUCCESS;
238 }
239 
240 /*
241  * See if a zone transfer is allowed
242  */
243 isc_result_t
dlz_allowzonexfr(void * dbdata,const char * name,const char * client)244 dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
245 	isc_result_t result;
246 	bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
247 	DBT key, data;
248 
249 	/* check to see if we are authoritative for the zone first. */
250 #if DLZ_DLOPEN_VERSION >= 3
251 	result = dlz_findzonedb(dbdata, name, NULL, NULL);
252 #else
253 	result = dlz_findzonedb(dbdata, name);
254 #endif
255 	if (result != ISC_R_SUCCESS)
256 		return (ISC_R_NOTFOUND);
257 
258 	memset(&key, 0, sizeof(DBT));
259 	key.flags = DB_DBT_MALLOC;
260 	key.data = strdup(name);
261 	if (key.data == NULL) {
262 		result = ISC_R_NOMEMORY;
263 		goto xfr_cleanup;
264 	}
265 	key.size = strlen(key.data);
266 
267 	memset(&data, 0, sizeof(DBT));
268 	data.flags = DB_DBT_MALLOC;
269 	data.data = strdup(client);
270 	if (data.data == NULL) {
271 		result = ISC_R_NOMEMORY;
272 		goto xfr_cleanup;
273 	}
274 	data.size = strlen(data.data);
275 
276 	switch(db->client->get(db->client, NULL, &key, &data, DB_GET_BOTH)) {
277 	case DB_NOTFOUND:
278 		result = ISC_R_NOTFOUND;
279 		break;
280 	case 0:
281 		result = ISC_R_SUCCESS;
282 		break;
283 	default:
284 		result = ISC_R_FAILURE;
285 	}
286 
287  xfr_cleanup:
288 	/* free any memory duplicate string in the key field */
289 	if (key.data != NULL)
290 		free(key.data);
291 
292 	/* free any memory allocated to the data field. */
293 	if (data.data != NULL)
294 		free(data.data);
295 
296 	return result;
297 }
298 
299 /*%
300  * Perform a zone transfer
301  *
302  * BDB does not allow a secondary index on a database that allows
303  * duplicates.	We have a few options:
304  *
305  * 1) kill speed by having lookup method use a secondary db which
306  * is associated to the primary DB with the DNS data.	 Then have
307  * another secondary db for zone transfer which also points to
308  * the dns_data primary.	NO - The	point of this driver is
309  * lookup performance.
310  *
311  * 2) Blow up database size by storing DNS data twice.	Once for
312  * the lookup (dns_data) database, and a second time for the zone
313  * transfer (dns_xfr) database. NO - That would probably require
314  * a larger cache to provide good performance.	Also, that would
315  * make the DB larger on disk potentially slowing it as well.
316  *
317  * 3) Loop through the dns_xfr database with a cursor to get
318  * all the different hosts in a zone.	 Then use the zone & host
319  * together to lookup the data in the dns_data database. YES -
320  * This may slow down zone xfr's a little, but that's ok they
321  * don't happen as often and don't need to be as fast. We can
322  * also use this table when deleting a zone (The BDB driver
323  * is read only - the delete would be used during replication
324  * updates by a separate process).
325  */
326 isc_result_t
dlz_allnodes(const char * zone,void * dbdata,dns_sdlzallnodes_t * allnodes)327 dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
328 	isc_result_t result = ISC_R_NOTFOUND;
329 	bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
330 	DBC *xfr_cursor = NULL;
331 	DBC *dns_cursor = NULL;
332 	DBT xfr_key, xfr_data, dns_key, dns_data;
333 	int xfr_flags;
334 	int dns_flags;
335 	int bdbhptres;
336 	bdbhpt_parsed_data_t pd;
337 	char *tmp = NULL, *tmp_zone, *tmp_zone_host = NULL;
338 
339 	memset(&xfr_key, 0, sizeof(DBT));
340 	memset(&xfr_data, 0, sizeof(DBT));
341 	memset(&dns_key, 0, sizeof(DBT));
342 	memset(&dns_data, 0, sizeof(DBT));
343 
344 	xfr_key.data = tmp_zone = strdup(zone);
345 	if (xfr_key.data == NULL)
346 		return (ISC_R_NOMEMORY);
347 
348 	xfr_key.size = strlen(xfr_key.data);
349 
350 	/* get a cursor to loop through dns_xfr table */
351 	if (db->xfr->cursor(db->xfr, NULL, &xfr_cursor, 0) != 0) {
352 		result = ISC_R_FAILURE;
353 		goto allnodes_cleanup;
354 	}
355 
356 	/* get a cursor to loop through dns_data table */
357 	if (db->data->cursor(db->data, NULL, &dns_cursor, 0) != 0) {
358 		result = ISC_R_FAILURE;
359 		goto allnodes_cleanup;
360 	}
361 
362 	xfr_flags = DB_SET;
363 
364 	/* loop through xfr table for specified zone. */
365 	while ((bdbhptres = xfr_cursor->c_get(xfr_cursor, &xfr_key,
366 					      &xfr_data, xfr_flags)) == 0)
367 	{
368 		xfr_flags = DB_NEXT_DUP;
369 
370 		/* +1 to allow for space between zone and host names */
371 		dns_key.size = xfr_data.size + xfr_key.size + 1;
372 
373 		/* +1 to allow for null term at end of string. */
374 		dns_key.data = tmp_zone_host = malloc(dns_key.size + 1);
375 		if (dns_key.data == NULL)
376 			goto allnodes_cleanup;
377 
378 		/*
379 		 * construct search key for dns_data.
380 		 * zone_name(a space)host_name
381 		 */
382 		strcpy(dns_key.data, zone);
383 		strcat(dns_key.data, " ");
384 		strncat(dns_key.data, xfr_data.data, xfr_data.size);
385 
386 		dns_flags = DB_SET;
387 
388 		while ((bdbhptres = dns_cursor->c_get(dns_cursor,
389 						      &dns_key,
390 						      &dns_data,
391 						      dns_flags)) == 0)
392 		{
393 			dns_flags = DB_NEXT_DUP;
394 
395 			/* +1 to allow for null term at end of string. */
396 			tmp = realloc(tmp, dns_data.size + 1);
397 			if (tmp == NULL)
398 				goto allnodes_cleanup;
399 
400 			/* copy data to tmp string, and append null term. */
401 			strncpy(tmp, dns_data.data, dns_data.size);
402 			tmp[dns_data.size] = '\0';
403 
404 			/* split string into dns data parts. */
405 			if (bdbhpt_parse_data(db->log,
406 					      tmp, &pd) != ISC_R_SUCCESS)
407 				goto allnodes_cleanup;
408 			result = db->putnamedrr(allnodes, pd.host,
409 						pd.type, pd.ttl, pd.data);
410 			if (result != ISC_R_SUCCESS)
411 				goto allnodes_cleanup;
412 
413 		}	 /* end inner while loop */
414 
415 		/* clean up memory */
416 		if (tmp_zone_host != NULL) {
417 			free(tmp_zone_host);
418 			tmp_zone_host = NULL;
419 		}
420 	} /* end outer while loop */
421 
422  allnodes_cleanup:
423 	/* free any memory */
424 	if (tmp != NULL)
425 		free(tmp);
426 
427 	if (tmp_zone_host != NULL)
428 		free(tmp_zone_host);
429 
430 	if (tmp_zone != NULL)
431 		free(tmp_zone);
432 
433 	/* get rid of cursors */
434 	if (xfr_cursor != NULL)
435 		xfr_cursor->c_close(xfr_cursor);
436 
437 	if (dns_cursor != NULL)
438 		dns_cursor->c_close(dns_cursor);
439 
440 	return result;
441 }
442 
443 /*%
444  * Performs bdbhpt cleanup.
445  * Used by bdbhpt_create if there is an error starting up.
446  * Used by bdbhpt_destroy when the driver is shutting down.
447  */
448 static void
bdbhpt_cleanup(bdbhpt_instance_t * db)449 bdbhpt_cleanup(bdbhpt_instance_t *db) {
450 	/* close databases */
451 	if (db->data != NULL)
452 		db->data->close(db->data, 0);
453 	if (db->xfr != NULL)
454 		db->xfr->close(db->xfr, 0);
455 	if (db->zone != NULL)
456 		db->zone->close(db->zone, 0);
457 	if (db->client != NULL)
458 		db->client->close(db->client, 0);
459 
460 	/* close environment */
461 	if (db->dbenv != NULL)
462 		db->dbenv->close(db->dbenv, 0);
463 }
464 
465 /*
466  * See if we handle a given zone
467  */
468 #if DLZ_DLOPEN_VERSION < 3
469 isc_result_t
dlz_findzonedb(void * dbdata,const char * name)470 dlz_findzonedb(void *dbdata, const char *name)
471 #else
472 isc_result_t
473 dlz_findzonedb(void *dbdata, const char *name,
474 		 dns_clientinfomethods_t *methods,
475 		 dns_clientinfo_t *clientinfo)
476 #endif
477 {
478 	isc_result_t result;
479 	bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
480 	DBT key, data;
481 
482 	memset(&key, 0, sizeof(DBT));
483 	memset(&data, 0, sizeof(DBT));
484 	data.flags = DB_DBT_MALLOC;
485 
486 #if DLZ_DLOPEN_VERSION >= 3
487 	UNUSED(methods);
488 	UNUSED(clientinfo);
489 #endif
490 
491 	key.data = strdup(name);
492 
493 	if (key.data == NULL)
494 		return (ISC_R_NOMEMORY);
495 
496 	/*
497 	 * reverse string to take advantage of BDB locality of reference
498 	 * if we need futher lookups because the zone doesn't match the
499 	 * first time.
500 	 */
501 	key.data = bdbhpt_strrev(key.data);
502 	key.size = strlen(key.data);
503 
504 	switch(db->zone->get(db->zone, NULL, &key, &data, 0)) {
505 	case DB_NOTFOUND:
506 		result = ISC_R_NOTFOUND;
507 		break;
508 	case 0:
509 		result = ISC_R_SUCCESS;
510 		break;
511 	default:
512 		result = ISC_R_FAILURE;
513 	}
514 
515 	/* free any memory duplicate string in the key field */
516 	if (key.data != NULL)
517 		free(key.data);
518 
519 	/* free any memory allocated to the data field. */
520 	if (data.data != NULL)
521 		free(data.data);
522 
523 	return result;
524 }
525 
526 /*
527  * Look up one record in the database.
528  *
529  */
530 #if DLZ_DLOPEN_VERSION == 1
dlz_lookup(const char * zone,const char * name,void * dbdata,dns_sdlzlookup_t * lookup)531 isc_result_t dlz_lookup(const char *zone, const char *name,
532 			void *dbdata, dns_sdlzlookup_t *lookup)
533 #else
534 isc_result_t dlz_lookup(const char *zone, const char *name, void *dbdata,
535 			dns_sdlzlookup_t *lookup,
536 			dns_clientinfomethods_t *methods,
537 			dns_clientinfo_t *clientinfo)
538 #endif
539 {
540 	isc_result_t result = ISC_R_NOTFOUND;
541 	bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
542 	DBC *data_cursor = NULL;
543 	DBT key, data;
544 	int bdbhptres;
545 	int flags;
546 
547 	bdbhpt_parsed_data_t pd;
548 	char *tmp = NULL;
549 	char *keyStr = NULL;
550 
551 #if DLZ_DLOPEN_VERSION >= 2
552 	UNUSED(methods);
553 	UNUSED(clientinfo);
554 #endif
555 
556 	memset(&key, 0, sizeof(DBT));
557 	memset(&data, 0, sizeof(DBT));
558 
559 	key.size = strlen(zone) + strlen(name) + 1;
560 
561 	/* allocate mem for key */
562 	key.data = keyStr = malloc((key.size + 1) * sizeof(char));
563 
564 	if (keyStr == NULL)
565 		return ISC_R_NOMEMORY;
566 
567 	strcpy(keyStr, zone);
568 	strcat(keyStr, " ");
569 	strcat(keyStr, name);
570 
571 	/* get a cursor to loop through data */
572 	if (db->data->cursor(db->data, NULL, &data_cursor, 0) != 0) {
573 		result = ISC_R_FAILURE;
574 		goto lookup_cleanup;
575 	}
576 
577 	result = ISC_R_NOTFOUND;
578 
579 	flags = DB_SET;
580 	while ((bdbhptres = data_cursor->c_get(data_cursor, &key, &data,
581 					       flags)) == 0)
582 	{
583 		flags = DB_NEXT_DUP;
584 		tmp = realloc(tmp, data.size + 1);
585 		if (tmp == NULL)
586 			goto lookup_cleanup;
587 
588 		strncpy(tmp, data.data, data.size);
589 		tmp[data.size] = '\0';
590 
591 		if (bdbhpt_parse_data(db->log, tmp, &pd) != ISC_R_SUCCESS)
592 			goto lookup_cleanup;
593 
594 		result = db->putrr(lookup, pd.type, pd.ttl, pd.data);
595 		if (result != ISC_R_SUCCESS)
596 			goto lookup_cleanup;
597 	} /* end while loop */
598 
599  lookup_cleanup:
600 	/* get rid of cursor */
601 	if (data_cursor != NULL)
602 		data_cursor->c_close(data_cursor);
603 
604 	if (keyStr != NULL)
605 		free(keyStr);
606 	if (tmp != NULL)
607 		free(tmp);
608 
609 	return result;
610 }
611 
612 /*%
613  * Initialises, sets flags and then opens Berkeley databases.
614  */
615 static isc_result_t
bdbhpt_opendb(log_t * log,DB_ENV * db_env,DBTYPE db_type,DB ** db,const char * db_name,char * db_file,int flags)616 bdbhpt_opendb(log_t *log, DB_ENV *db_env, DBTYPE db_type, DB **db,
617 	      const char *db_name, char *db_file, int flags)
618 {
619 	int result;
620 
621 	/* Initialise the database. */
622 	if ((result = db_create(db, db_env, 0)) != 0) {
623 		log(ISC_LOG_ERROR,
624 		    "bdbhpt_dynamic: could not initialize %s database. "
625 		    "BerkeleyDB error: %s",
626 		    db_name, db_strerror(result));
627 		return ISC_R_FAILURE;
628 	}
629 
630 	/* set database flags. */
631 	if ((result = (*db)->set_flags(*db, flags)) != 0) {
632 		log(ISC_LOG_ERROR,
633 		    "bdbhpt_dynamic: could not set flags for %s database. "
634 		    "BerkeleyDB error: %s",
635 		    db_name, db_strerror(result));
636 		return ISC_R_FAILURE;
637 	}
638 
639 	/* open the database. */
640 	if ((result = (*db)->open(*db, NULL, db_file, db_name, db_type,
641 				  DB_RDONLY | bdbhpt_threads, 0)) != 0) {
642 		log(ISC_LOG_ERROR,
643 		    "bdbhpt_dynamic: could not open %s database in %s. "
644 		    "BerkeleyDB error: %s",
645 		    db_name, db_file, db_strerror(result));
646 		return ISC_R_FAILURE;
647 	}
648 
649 	return ISC_R_SUCCESS;
650 }
651 
652 
653 /*
654  * Called to initialize the driver
655  */
656 isc_result_t
dlz_create(const char * dlzname,unsigned int argc,char * argv[],void ** dbdata,...)657 dlz_create(const char *dlzname, unsigned int argc, char *argv[],
658 		 void **dbdata, ...)
659 {
660 	isc_result_t result;
661 	int bdbhptres;
662 	int bdbFlags = 0;
663 	bdbhpt_instance_t *db = NULL;
664 
665 	const char *helper_name;
666 	va_list ap;
667 
668 	UNUSED(dlzname);
669 
670 	/* Allocate memory for our db structures and helper functions */
671 	db = calloc(1, sizeof(struct bdbhpt_instance));
672 	if (db == NULL)
673 		return (ISC_R_NOMEMORY);
674 
675 	/* Fill in the helper functions */
676 	va_start(ap, dbdata);
677 	while ((helper_name = va_arg(ap, const char *)) != NULL)
678 		b9_add_helper(db, helper_name, va_arg(ap, void*));
679 	va_end(ap);
680 
681 	/* verify we have 4 arg's passed to the driver */
682 	if (argc != 4) {
683 		db->log(ISC_LOG_ERROR,
684 			"bdbhpt_dynamic: please supply 3 command line args. "
685 			"You supplied: %s", argc);
686 		return (ISC_R_FAILURE);
687 	}
688 
689 	switch((char) *argv[1]) {
690 		/*
691 		 * Transactional mode.	Highest safety - lowest speed.
692 		 */
693 	case 'T':
694 	case 't':
695 		bdbFlags = DB_INIT_MPOOL | DB_INIT_LOCK |
696 			DB_INIT_LOG | DB_INIT_TXN;
697 		db->log(ISC_LOG_INFO,
698 			"bdbhpt_dynamic: using transactional mode.");
699 		break;
700 
701 		/*
702 		 * Concurrent mode.	 Lower safety (no rollback) -
703 		 * higher speed.
704 		 */
705 	case 'C':
706 	case 'c':
707 		bdbFlags = DB_INIT_CDB | DB_INIT_MPOOL;
708 		db->log(ISC_LOG_INFO,
709 			"bdbhpt_dynamic: using concurrent mode.");
710 		break;
711 
712 		/*
713 		 * Private mode. No inter-process communication & no locking.
714 		 * Lowest saftey - highest speed.
715 		 */
716 	case 'P':
717 	case 'p':
718 		bdbFlags = DB_PRIVATE | DB_INIT_MPOOL;
719 		db->log(ISC_LOG_INFO,
720 			"bdbhpt_dynamic: using private mode.");
721 		break;
722 	default:
723 		db->log(ISC_LOG_ERROR,
724 			"bdbhpt_dynamic: "
725 			"operating mode must be set to P or C or T. "
726 			"You specified '%s'", argv[1]);
727 		return (ISC_R_FAILURE);
728 	}
729 
730 	/*
731 	 * create bdbhpt environment
732 	 * Basically bdbhpt allocates and assigns memory to db->dbenv
733 	 */
734 	bdbhptres = db_env_create(&db->dbenv, 0);
735 	if (bdbhptres != 0) {
736 		db->log(ISC_LOG_ERROR,
737 			"bdbhpt_dynamic: db environment could not be created. "
738 			"BerkeleyDB error: %s", db_strerror(bdbhptres));
739 		result = ISC_R_FAILURE;
740 		goto init_cleanup;
741 	}
742 
743 	/* open bdbhpt environment */
744 	bdbhptres = db->dbenv->open(db->dbenv, argv[2],
745 				    bdbFlags | bdbhpt_threads | DB_CREATE, 0);
746 	if (bdbhptres != 0) {
747 		db->log(ISC_LOG_ERROR,
748 			"bdbhpt_dynamic: "
749 			"db environment at '%s' could not be opened. "
750 			"BerkeleyDB error: %s",
751 			argv[2], db_strerror(bdbhptres));
752 		result = ISC_R_FAILURE;
753 		goto init_cleanup;
754 	}
755 
756 	/* open dlz_data database. */
757 	result = bdbhpt_opendb(db->log, db->dbenv, DB_UNKNOWN, &db->data,
758 			       dlz_data, argv[3], DB_DUP | DB_DUPSORT);
759 	if (result != ISC_R_SUCCESS)
760 		goto init_cleanup;
761 
762 	/* open dlz_xfr database. */
763 	result = bdbhpt_opendb(db->log, db->dbenv, DB_UNKNOWN, &db->xfr,
764 			       dlz_xfr, argv[3], DB_DUP | DB_DUPSORT);
765 	if (result != ISC_R_SUCCESS)
766 		goto init_cleanup;
767 
768 	/* open dlz_zone database. */
769 	result = bdbhpt_opendb(db->log, db->dbenv, DB_UNKNOWN, &db->zone,
770 			       dlz_zone, argv[3], 0);
771 	if (result != ISC_R_SUCCESS)
772 		goto init_cleanup;
773 
774 	/* open dlz_client database. */
775 	result = bdbhpt_opendb(db->log, db->dbenv, DB_UNKNOWN, &db->client,
776 			       dlz_client, argv[3], DB_DUP | DB_DUPSORT);
777 	if (result != ISC_R_SUCCESS)
778 		goto init_cleanup;
779 
780 	*dbdata = db;
781 
782 	db->log(ISC_LOG_INFO,
783 		"bdbhpt_dynamic: version %s, started",
784 		dlz_bdbhpt_dynamic_version);
785 	return(ISC_R_SUCCESS);
786 
787  init_cleanup:
788 	bdbhpt_cleanup(db);
789 	return result;
790 }
791 
792 /*
793  * Shut down the backend
794  */
795 void
dlz_destroy(void * dbdata)796 dlz_destroy(void *dbdata) {
797 	struct bdbhpt_instance *db = (struct bdbhpt_instance *)dbdata;
798 
799 	db->log(ISC_LOG_INFO,
800 		"dlz_bdbhpt_dynamic (%s): shutting down",
801 		dlz_bdbhpt_dynamic_version);
802 	bdbhpt_cleanup((bdbhpt_instance_t *) dbdata);
803 	free(db);
804 }
805 
806 /*
807  * Return the version of the API
808  */
809 int
dlz_version(unsigned int * flags)810 dlz_version(unsigned int *flags) {
811 	UNUSED(flags);
812 	return (DLZ_DLOPEN_VERSION);
813 }
814 
815 /*
816  * Register a helper function from the bind9 dlz_dlopen driver
817  */
818 static void
b9_add_helper(struct bdbhpt_instance * db,const char * helper_name,void * ptr)819 b9_add_helper(struct bdbhpt_instance *db, const char *helper_name, void *ptr) {
820 	if (strcmp(helper_name, "log") == 0)
821 		db->log = (log_t *)ptr;
822 	if (strcmp(helper_name, "putrr") == 0)
823 		db->putrr = (dns_sdlz_putrr_t *)ptr;
824 	if (strcmp(helper_name, "putnamedrr") == 0)
825 		db->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
826 	if (strcmp(helper_name, "writeable_zone") == 0)
827 		db->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
828 }
829 
830