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