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