1 /* $NetBSD: dlz_bdb_driver.c,v 1.5 2014/12/10 04:37:55 christos Exp $ */
4 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
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
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.
20 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
21 * conceived and contributed by Rob Butler.
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
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.
39 * Copyright (C) 1999-2001 Internet Software Consortium.
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.
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.
64 #include <dns/result.h>
67 #include <isc/print.h>
68 #include <isc/result.h>
71 #include <named/globals.h>
73 #include <dlz/dlz_bdb_driver.h>
77 static dns_sdlzimplementation_t
*dlz_bdb
= NULL
;
79 /* should the bdb driver use threads. */
80 #ifdef ISC_PLATFORM_USETHREADS
81 #define bdb_threads DB_THREAD
86 /* BDB database names */
87 #define dlz_data "dns_data"
88 #define dlz_zone "dns_zone"
89 #define dlz_host "dns_host"
90 #define dlz_client "dns_client"
93 * This structure contains all the Berkeley DB handles
94 * for this instance of the BDB driver.
97 typedef struct bdb_instance
{
98 DB_ENV
*dbenv
; /*%< BDB environment */
99 DB
*data
; /*%< dns_data database handle */
100 DB
*zone
; /*%< zone database handle */
101 DB
*host
; /*%< host database handle */
102 DB
*client
; /*%< client database handle */
103 isc_mem_t
*mctx
; /*%< memory context */
107 typedef struct parsed_data
{
116 /* forward reference */
119 bdb_findzone(void *driverarg
, void *dbdata
, const char *name
,
120 dns_clientinfomethods_t
*methods
, dns_clientinfo_t
*clientinfo
);
123 * Parses the DBT from the Berkeley DB into a parsed_data record
124 * The parsed_data record should be allocated before and passed into the
125 * bdb_parse_data function. The char (type & data) fields should not
126 * be "free"d as that memory is part of the DBT data field. It will be
127 * "free"d when the DBT is freed.
131 bdb_parse_data(char *in
, parsed_data_t
*pd
) {
135 char *lastchar
= (char *) &tmp
[strlen(tmp
) + 1];
138 * String should be formated as:
139 * zone(a space)host(a space)ttl(a space)type(a space)remaining data
141 * example.com www 10 A 127.0.0.1
142 * example.com mail 10 A 127.0.0.2
143 * example.com @ 10 MX 20 mail.example.com
146 /* save pointer to zone */
149 /* find space after zone and change it to a '\0' */
150 tmp
= strchr(tmp
, ' ');
151 /* verify we found a space */
153 return ISC_R_FAILURE
;
154 /* change the space to a null (string terminator) */
156 /* make sure it is safe to increment pointer */
157 if (++tmp
> lastchar
)
158 return ISC_R_FAILURE
;
160 /* save pointer to host */
163 /* find space after type and change it to a '\0' */
164 tmp
= strchr(tmp
, ' ');
165 /* verify we found a space */
167 return ISC_R_FAILURE
;
168 /* change the space to a null (string terminator) */
170 /* make sure it is safe to increment pointer */
171 if (++tmp
> lastchar
)
172 return ISC_R_FAILURE
;
174 /* save pointer to dns type */
177 /* find space after type and change it to a '\0' */
178 tmp
= strchr(tmp
, ' ');
179 /* verify we found a space */
181 return ISC_R_FAILURE
;
182 /* change the space to a null (string terminator) */
184 /* make sure it is safe to increment pointer */
185 if (++tmp
> lastchar
)
186 return ISC_R_FAILURE
;
188 /* save pointer to dns ttl */
191 /* find space after ttl and change it to a '\0' */
192 tmp
= strchr(tmp
, ' ');
193 /* verify we found a space */
195 return ISC_R_FAILURE
;
196 /* change the space to a null (string terminator) */
198 /* make sure it is safe to increment pointer */
199 if (++tmp
> lastchar
)
200 return ISC_R_FAILURE
;
202 /* save pointer to remainder of DNS data */
205 /* convert ttl string to integer */
206 pd
->ttl
= strtol(ttlStr
, &endp
, 10);
207 if (*endp
!= '\0' || pd
->ttl
< 0) {
208 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
209 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
210 "BDB driver ttl must be a postive number");
211 return ISC_R_FAILURE
;
214 /* if we get this far everything should have worked. */
215 return ISC_R_SUCCESS
;
223 bdb_allowzonexfr(void *driverarg
, void *dbdata
, const char *name
,
227 bdb_instance_t
*db
= (bdb_instance_t
*) dbdata
;
228 DBC
*client_cursor
= NULL
;
231 /* check to see if we are authoritative for the zone first. */
232 result
= bdb_findzone(driverarg
, dbdata
, name
, NULL
, NULL
);
233 if (result
!= ISC_R_SUCCESS
)
234 return (ISC_R_NOTFOUND
);
236 memset(&key
, 0, sizeof(DBT
));
237 key
.flags
= DB_DBT_MALLOC
;
238 key
.data
= strdup(name
);
239 if (key
.data
== NULL
) {
240 result
= ISC_R_NOMEMORY
;
243 key
.size
= strlen(key
.data
);
245 memset(&data
, 0, sizeof(DBT
));
246 data
.flags
= DB_DBT_MALLOC
;
247 data
.data
= strdup(client
);
248 if (data
.data
== NULL
) {
249 result
= ISC_R_NOMEMORY
;
252 data
.size
= strlen(data
.data
);
254 /* get a cursor to loop through zone data */
255 if (db
->client
->cursor(db
->client
, NULL
, &client_cursor
, 0) != 0) {
256 result
= ISC_R_FAILURE
;
260 switch(client_cursor
->c_get(client_cursor
, &key
, &data
, DB_GET_BOTH
)) {
262 case DB_SECONDARY_BAD
:
263 result
= ISC_R_NOTFOUND
;
266 result
= ISC_R_SUCCESS
;
269 result
= ISC_R_FAILURE
;
274 /* free any memory duplicate string in the key field */
275 if (key
.data
!= NULL
)
278 /* free any memory allocated to the data field. */
279 if (data
.data
!= NULL
)
282 /* get rid of zone_cursor */
283 if (client_cursor
!= NULL
)
284 client_cursor
->c_close(client_cursor
);
291 bdb_allnodes(const char *zone
, void *driverarg
, void *dbdata
,
292 dns_sdlzallnodes_t
*allnodes
)
295 isc_result_t result
= ISC_R_NOTFOUND
;
296 bdb_instance_t
*db
= (bdb_instance_t
*) dbdata
;
297 DBC
*zone_cursor
= NULL
;
302 char *tmp
= NULL
, *tmp_zone
;
306 memset(&key
, 0, sizeof(DBT
));
307 memset(&data
, 0, sizeof(DBT
));
309 key
.data
= tmp_zone
= strdup(zone
);
311 if (key
.data
== NULL
)
312 return (ISC_R_NOMEMORY
);
314 key
.size
= strlen(key
.data
);
316 /* get a cursor to loop through zone data */
317 if (db
->zone
->cursor(db
->zone
, NULL
, &zone_cursor
, 0) != 0) {
318 result
= ISC_R_FAILURE
;
319 goto allnodes_cleanup
;
324 while ((bdbres
= zone_cursor
->c_get(zone_cursor
, &key
, &data
,
329 tmp
= realloc(tmp
, data
.size
+ 1);
331 goto allnodes_cleanup
;
333 strncpy(tmp
, data
.data
, data
.size
);
334 tmp
[data
.size
] = '\0';
336 if (bdb_parse_data(tmp
, &pd
) != ISC_R_SUCCESS
)
337 goto allnodes_cleanup
;
339 result
= dns_sdlz_putnamedrr(allnodes
, pd
.host
, pd
.type
,
341 if (result
!= ISC_R_SUCCESS
)
342 goto allnodes_cleanup
;
344 } /* end while loop */
351 /* free any memory duplicate string in the key field */
352 if (tmp_zone
!= NULL
)
355 /* get rid of zone_cursor */
356 if (zone_cursor
!= NULL
)
357 zone_cursor
->c_close(zone_cursor
);
364 * Performs BDB cleanup.
365 * Used by bdb_create if there is an error starting up.
366 * Used by bdb_destroy when the driver is shutting down.
370 bdb_cleanup(bdb_instance_t
*db
) {
374 /* close databases */
375 if (db
->data
!= NULL
)
376 db
->data
->close(db
->data
, 0);
377 if (db
->host
!= NULL
)
378 db
->host
->close(db
->host
, 0);
379 if (db
->zone
!= NULL
)
380 db
->zone
->close(db
->zone
, 0);
381 if (db
->client
!= NULL
)
382 db
->client
->close(db
->client
, 0);
384 /* close environment */
385 if (db
->dbenv
!= NULL
)
386 db
->dbenv
->close(db
->dbenv
, 0);
389 if (db
->mctx
!= NULL
) {
390 /* save mctx for later */
392 /* return, and detach the memory */
393 isc_mem_put(mctx
, db
, sizeof(bdb_instance_t
));
394 isc_mem_detach(&mctx
);
399 bdb_findzone(void *driverarg
, void *dbdata
, const char *name
,
400 dns_clientinfomethods_t
*methods
, dns_clientinfo_t
*clientinfo
)
404 bdb_instance_t
*db
= (bdb_instance_t
*) dbdata
;
405 DBC
*zone_cursor
= NULL
;
412 memset(&key
, 0, sizeof(DBT
));
413 memset(&data
, 0, sizeof(DBT
));
414 data
.flags
= DB_DBT_MALLOC
;
416 key
.data
= strdup(name
);
418 if (key
.data
== NULL
)
419 return (ISC_R_NOMEMORY
);
421 key
.size
= strlen(key
.data
);
423 /* get a cursor to loop through zone data */
424 if (db
->zone
->cursor(db
->zone
, NULL
, &zone_cursor
, 0) != 0) {
425 result
= ISC_R_NOTFOUND
;
426 goto findzone_cleanup
;
429 switch(zone_cursor
->c_get(zone_cursor
, &key
, &data
, DB_SET
)) {
431 case DB_SECONDARY_BAD
:
432 result
= ISC_R_NOTFOUND
;
435 result
= ISC_R_SUCCESS
;
438 result
= ISC_R_FAILURE
;
443 /* free any memory duplicate string in the key field */
444 if (key
.data
!= NULL
)
447 /* free any memory allocated to the data field. */
448 if (data
.data
!= NULL
)
451 /* get rid of zone_cursor */
452 if (zone_cursor
!= NULL
)
453 zone_cursor
->c_close(zone_cursor
);
459 bdb_lookup(const char *zone
, const char *name
, void *driverarg
,
460 void *dbdata
, dns_sdlzlookup_t
*lookup
,
461 dns_clientinfomethods_t
*methods
, dns_clientinfo_t
*clientinfo
)
464 isc_result_t result
= ISC_R_NOTFOUND
;
465 bdb_instance_t
*db
= (bdb_instance_t
*) dbdata
;
466 DBC
*zone_cursor
= NULL
;
467 DBC
*host_cursor
= NULL
;
468 DBC
*join_cursor
= NULL
;
473 char *tmp_zone
, *tmp_host
= NULL
;
480 memset(&key
, 0, sizeof(DBT
));
481 memset(&data
, 0, sizeof(DBT
));
484 key
.data
= tmp_zone
= strdup(zone
);
485 if (key
.data
== NULL
) {
486 result
= ISC_R_NOMEMORY
;
489 key
.size
= strlen(key
.data
);
491 /* get a cursor to loop through zone data */
492 if (db
->zone
->cursor(db
->zone
, NULL
, &zone_cursor
, 0) != 0) {
493 result
= ISC_R_FAILURE
;
497 /* initialize zone_cursor with zone_key */
498 if (zone_cursor
->c_get(zone_cursor
, &key
, &data
, DB_SET
) != 0) {
499 result
= ISC_R_NOTFOUND
;
504 key
.data
= tmp_host
= strdup(name
);
505 if (key
.data
== NULL
) {
506 result
= ISC_R_NOMEMORY
;
509 key
.size
= strlen(key
.data
);
511 /* get a cursor to loop through host data */
512 if (db
->host
->cursor(db
->host
, NULL
, &host_cursor
, 0) != 0) {
513 result
= ISC_R_FAILURE
;
517 /* initialize host_cursor with host_key */
518 if (host_cursor
->c_get(host_cursor
, &key
, &data
, DB_SET
) != 0) {
519 result
= ISC_R_NOTFOUND
;
523 cur_arr
[0] = zone_cursor
;
524 cur_arr
[1] = host_cursor
;
527 db
->data
->join(db
->data
, cur_arr
, &join_cursor
, 0);
529 while ((bdbres
= join_cursor
->c_get(join_cursor
, &key
,
532 tmp
= realloc(tmp
, data
.size
+ 1);
536 strncpy(tmp
, data
.data
, data
.size
);
537 tmp
[data
.size
] = '\0';
539 if (bdb_parse_data(tmp
, &pd
) != ISC_R_SUCCESS
)
542 result
= dns_sdlz_putrr(lookup
, pd
.type
, pd
.ttl
, pd
.data
);
544 if (result
!= ISC_R_SUCCESS
)
546 } /* end while loop */
552 if (tmp_zone
!= NULL
)
554 if (tmp_host
!= NULL
)
557 /* get rid of the joined cusor */
558 if (join_cursor
!= NULL
)
559 join_cursor
->c_close(join_cursor
);
561 /* get rid of zone_cursor */
562 if (zone_cursor
!= NULL
)
563 zone_cursor
->c_close(zone_cursor
);
565 /* get rid of host_cursor */
566 if (host_cursor
!= NULL
)
567 host_cursor
->c_close(host_cursor
);
573 /*% Initializes, sets flags and then opens Berkeley databases. */
576 bdb_opendb(DB_ENV
*db_env
, DBTYPE db_type
, DB
**db
, const char *db_name
,
577 char *db_file
, int flags
) {
581 /* Initialize the database. */
582 if ((result
= db_create(db
, db_env
, 0)) != 0) {
583 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
584 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
585 "BDB could not initialize %s database. "
587 db_name
, db_strerror(result
));
588 return ISC_R_FAILURE
;
591 /* set database flags. */
592 if ((result
= (*db
)->set_flags(*db
, flags
)) != 0) {
593 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
594 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
595 "BDB could not set flags for %s database. "
597 db_name
, db_strerror(result
));
598 return ISC_R_FAILURE
;
601 /* open the database. */
602 if ((result
= (*db
)->open(*db
, NULL
, db_file
, db_name
, db_type
,
603 DB_RDONLY
| bdb_threads
, 0)) != 0) {
604 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
605 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
606 "BDB could not open %s database in %s. "
608 db_name
, db_file
, db_strerror(result
));
609 return ISC_R_FAILURE
;
612 return ISC_R_SUCCESS
;
616 bdb_create(const char *dlzname
, unsigned int argc
, char *argv
[],
617 void *driverarg
, void **dbdata
)
621 bdb_instance_t
*db
= NULL
;
626 /* verify we have 3 arg's passed to the driver */
628 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
629 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
630 "Berkeley DB driver requires at least "
631 "2 command line args.");
632 return (ISC_R_FAILURE
);
635 /* allocate and zero memory for driver structure */
636 db
= isc_mem_get(ns_g_mctx
, sizeof(bdb_instance_t
));
638 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
639 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
640 "Could not allocate memory for "
641 "database instance object.");
642 return (ISC_R_NOMEMORY
);
644 memset(db
, 0, sizeof(bdb_instance_t
));
646 /* attach to the memory context */
647 isc_mem_attach(ns_g_mctx
, &db
->mctx
);
649 /* create BDB environment
650 * Basically BDB allocates and assigns memory to db->dbenv
652 bdbres
= db_env_create(&db
->dbenv
, 0);
654 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
655 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
656 "BDB environment could not be created. "
658 db_strerror(bdbres
));
659 result
= ISC_R_FAILURE
;
663 /* open BDB environment */
664 bdbres
= db
->dbenv
->open(db
->dbenv
, argv
[1],
665 DB_INIT_CDB
| DB_INIT_MPOOL
|
666 bdb_threads
| DB_CREATE
,
669 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
670 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
671 "BDB environment at '%s' could not be opened. "
673 argv
[1], db_strerror(bdbres
));
674 result
= ISC_R_FAILURE
;
678 /* open dlz_data database. */
679 result
= bdb_opendb(db
->dbenv
, DB_UNKNOWN
, &db
->data
,
680 dlz_data
, argv
[2], 0);
681 if (result
!= ISC_R_SUCCESS
)
684 /* open dlz_host database. */
685 result
= bdb_opendb(db
->dbenv
, DB_UNKNOWN
, &db
->host
,
687 DB_DUP
| DB_DUPSORT
);
688 if (result
!= ISC_R_SUCCESS
)
691 /* open dlz_zone database. */
692 result
= bdb_opendb(db
->dbenv
, DB_UNKNOWN
, &db
->zone
,
694 DB_DUP
| DB_DUPSORT
);
695 if (result
!= ISC_R_SUCCESS
)
698 /* open dlz_client database. */
699 result
= bdb_opendb(db
->dbenv
, DB_UNKNOWN
, &db
->client
,
700 dlz_client
, argv
[2], DB_DUP
| DB_DUPSORT
);
701 if (result
!= ISC_R_SUCCESS
)
704 /* associate the host secondary database with the primary database */
705 bdbres
= db
->data
->associate(db
->data
, NULL
, db
->host
, NULL
, 0);
707 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
708 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
709 "BDB could not associate %s database with %s. "
711 dlz_host
, dlz_data
, db_strerror(bdbres
));
712 result
= ISC_R_FAILURE
;
716 /* associate the zone secondary database with the primary database */
717 bdbres
= db
->data
->associate(db
->data
, NULL
, db
->zone
, NULL
, 0);
719 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
720 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
721 "BDB could not associate %s database with %s. "
723 dlz_zone
, dlz_data
, db_strerror(bdbres
));
724 result
= ISC_R_FAILURE
;
730 return(ISC_R_SUCCESS
);
739 bdb_destroy(void *driverarg
, void *dbdata
)
743 bdb_cleanup((bdb_instance_t
*) dbdata
);
746 /* bdb_authority not needed as authority data is returned by lookup */
747 static dns_sdlzmethods_t dlz_bdb_methods
= {
765 * Wrapper around dns_sdlzregister().
772 * Write debugging message to log
774 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
775 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
776 "Registering DLZ bdb driver.");
778 result
= dns_sdlzregister("bdb", &dlz_bdb_methods
, NULL
,
779 DNS_SDLZFLAG_RELATIVEOWNER
|
780 DNS_SDLZFLAG_RELATIVERDATA
|
781 DNS_SDLZFLAG_THREADSAFE
,
782 ns_g_mctx
, &dlz_bdb
);
783 if (result
!= ISC_R_SUCCESS
) {
784 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
785 "dns_sdlzregister() failed: %s",
786 isc_result_totext(result
));
787 result
= ISC_R_UNEXPECTED
;
795 * Wrapper around dns_sdlzunregister().
798 dlz_bdb_clear(void) {
801 * Write debugging message to log
803 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
804 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
805 "Unregistering DLZ bdb driver.");
808 dns_sdlzunregister(&dlz_bdb
);