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
);
122 * Parses the DBT from the Berkeley DB into a parsed_data record
123 * The parsed_data record should be allocated before and passed into the
124 * bdb_parse_data function. The char (type & data) fields should not
125 * be "free"d as that memory is part of the DBT data field. It will be
126 * "free"d when the DBT is freed.
130 bdb_parse_data(char *in
, parsed_data_t
*pd
) {
134 char *lastchar
= (char *) &tmp
[strlen(tmp
) + 1];
137 * String should be formated as:
138 * zone(a space)host(a space)ttl(a space)type(a space)remaining data
140 * example.com www 10 A 127.0.0.1
141 * example.com mail 10 A 127.0.0.2
142 * example.com @ 10 MX 20 mail.example.com
145 /* save pointer to zone */
148 /* find space after zone and change it to a '\0' */
149 tmp
= strchr(tmp
, ' ');
150 /* verify we found a space */
152 return ISC_R_FAILURE
;
153 /* change the space to a null (string terminator) */
155 /* make sure it is safe to increment pointer */
156 if (++tmp
> lastchar
)
157 return ISC_R_FAILURE
;
159 /* save pointer to host */
162 /* find space after type and change it to a '\0' */
163 tmp
= strchr(tmp
, ' ');
164 /* verify we found a space */
166 return ISC_R_FAILURE
;
167 /* change the space to a null (string terminator) */
169 /* make sure it is safe to increment pointer */
170 if (++tmp
> lastchar
)
171 return ISC_R_FAILURE
;
173 /* save pointer to dns type */
176 /* find space after type and change it to a '\0' */
177 tmp
= strchr(tmp
, ' ');
178 /* verify we found a space */
180 return ISC_R_FAILURE
;
181 /* change the space to a null (string terminator) */
183 /* make sure it is safe to increment pointer */
184 if (++tmp
> lastchar
)
185 return ISC_R_FAILURE
;
187 /* save pointer to dns ttl */
190 /* find space after ttl and change it to a '\0' */
191 tmp
= strchr(tmp
, ' ');
192 /* verify we found a space */
194 return ISC_R_FAILURE
;
195 /* change the space to a null (string terminator) */
197 /* make sure it is safe to increment pointer */
198 if (++tmp
> lastchar
)
199 return ISC_R_FAILURE
;
201 /* save pointer to remainder of DNS data */
204 /* convert ttl string to integer */
205 pd
->ttl
= strtol(ttlStr
, &endp
, 10);
206 if (*endp
!= '\0' || pd
->ttl
< 0) {
207 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
208 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
209 "BDB driver ttl must be a postive number");
210 return ISC_R_FAILURE
;
213 /* if we get this far everything should have worked. */
214 return ISC_R_SUCCESS
;
222 bdb_allowzonexfr(void *driverarg
, void *dbdata
, const char *name
,
226 bdb_instance_t
*db
= (bdb_instance_t
*) dbdata
;
227 DBC
*client_cursor
= NULL
;
230 /* check to see if we are authoritative for the zone first. */
231 result
= bdb_findzone(driverarg
, dbdata
, name
);
232 if (result
!= ISC_R_SUCCESS
)
233 return (ISC_R_NOTFOUND
);
235 memset(&key
, 0, sizeof(DBT
));
236 key
.flags
= DB_DBT_MALLOC
;
237 key
.data
= strdup(name
);
238 if (key
.data
== NULL
) {
239 result
= ISC_R_NOMEMORY
;
242 key
.size
= strlen(key
.data
);
244 memset(&data
, 0, sizeof(DBT
));
245 data
.flags
= DB_DBT_MALLOC
;
246 data
.data
= strdup(client
);
247 if (data
.data
== NULL
) {
248 result
= ISC_R_NOMEMORY
;
251 data
.size
= strlen(data
.data
);
253 /* get a cursor to loop through zone data */
254 if (db
->client
->cursor(db
->client
, NULL
, &client_cursor
, 0) != 0) {
255 result
= ISC_R_FAILURE
;
259 switch(client_cursor
->c_get(client_cursor
, &key
, &data
, DB_GET_BOTH
)) {
261 case DB_SECONDARY_BAD
:
262 result
= ISC_R_NOTFOUND
;
265 result
= ISC_R_SUCCESS
;
268 result
= ISC_R_FAILURE
;
273 /* free any memory duplicate string in the key field */
274 if (key
.data
!= NULL
)
277 /* free any memory allocated to the data field. */
278 if (data
.data
!= NULL
)
281 /* get rid of zone_cursor */
282 if (client_cursor
!= NULL
)
283 client_cursor
->c_close(client_cursor
);
290 bdb_allnodes(const char *zone
, void *driverarg
, void *dbdata
,
291 dns_sdlzallnodes_t
*allnodes
)
294 isc_result_t result
= ISC_R_NOTFOUND
;
295 bdb_instance_t
*db
= (bdb_instance_t
*) dbdata
;
296 DBC
*zone_cursor
= NULL
;
301 char *tmp
= NULL
, *tmp_zone
;
305 memset(&key
, 0, sizeof(DBT
));
306 memset(&data
, 0, sizeof(DBT
));
308 key
.data
= tmp_zone
= strdup(zone
);
310 if (key
.data
== NULL
)
311 return (ISC_R_NOMEMORY
);
313 key
.size
= strlen(key
.data
);
315 /* get a cursor to loop through zone data */
316 if (db
->zone
->cursor(db
->zone
, NULL
, &zone_cursor
, 0) != 0) {
317 result
= ISC_R_FAILURE
;
318 goto allnodes_cleanup
;
323 while ((bdbres
= zone_cursor
->c_get(zone_cursor
, &key
, &data
,
328 tmp
= realloc(tmp
, data
.size
+ 1);
330 goto allnodes_cleanup
;
332 strncpy(tmp
, data
.data
, data
.size
);
333 tmp
[data
.size
] = '\0';
335 if (bdb_parse_data(tmp
, &pd
) != ISC_R_SUCCESS
)
336 goto allnodes_cleanup
;
338 result
= dns_sdlz_putnamedrr(allnodes
, pd
.host
, pd
.type
,
340 if (result
!= ISC_R_SUCCESS
)
341 goto allnodes_cleanup
;
343 } /* end while loop */
350 /* free any memory duplicate string in the key field */
351 if (tmp_zone
!= NULL
)
354 /* get rid of zone_cursor */
355 if (zone_cursor
!= NULL
)
356 zone_cursor
->c_close(zone_cursor
);
363 * Performs BDB cleanup.
364 * Used by bdb_create if there is an error starting up.
365 * Used by bdb_destroy when the driver is shutting down.
369 bdb_cleanup(bdb_instance_t
*db
) {
373 /* close databases */
374 if (db
->data
!= NULL
)
375 db
->data
->close(db
->data
, 0);
376 if (db
->host
!= NULL
)
377 db
->host
->close(db
->host
, 0);
378 if (db
->zone
!= NULL
)
379 db
->zone
->close(db
->zone
, 0);
380 if (db
->client
!= NULL
)
381 db
->client
->close(db
->client
, 0);
383 /* close environment */
384 if (db
->dbenv
!= NULL
)
385 db
->dbenv
->close(db
->dbenv
, 0);
388 if (db
->mctx
!= NULL
) {
389 /* save mctx for later */
391 /* return, and detach the memory */
392 isc_mem_put(mctx
, db
, sizeof(bdb_instance_t
));
393 isc_mem_detach(&mctx
);
398 bdb_findzone(void *driverarg
, void *dbdata
, const char *name
)
402 bdb_instance_t
*db
= (bdb_instance_t
*) dbdata
;
403 DBC
*zone_cursor
= NULL
;
408 memset(&key
, 0, sizeof(DBT
));
409 memset(&data
, 0, sizeof(DBT
));
410 data
.flags
= DB_DBT_MALLOC
;
412 key
.data
= strdup(name
);
414 if (key
.data
== NULL
)
415 return (ISC_R_NOMEMORY
);
417 key
.size
= strlen(key
.data
);
419 /* get a cursor to loop through zone data */
420 if (db
->zone
->cursor(db
->zone
, NULL
, &zone_cursor
, 0) != 0) {
421 result
= ISC_R_NOTFOUND
;
422 goto findzone_cleanup
;
425 switch(zone_cursor
->c_get(zone_cursor
, &key
, &data
, DB_SET
)) {
427 case DB_SECONDARY_BAD
:
428 result
= ISC_R_NOTFOUND
;
431 result
= ISC_R_SUCCESS
;
434 result
= ISC_R_FAILURE
;
439 /* free any memory duplicate string in the key field */
440 if (key
.data
!= NULL
)
443 /* free any memory allocated to the data field. */
444 if (data
.data
!= NULL
)
447 /* get rid of zone_cursor */
448 if (zone_cursor
!= NULL
)
449 zone_cursor
->c_close(zone_cursor
);
455 bdb_lookup(const char *zone
, const char *name
, void *driverarg
,
456 void *dbdata
, dns_sdlzlookup_t
*lookup
)
459 isc_result_t result
= ISC_R_NOTFOUND
;
460 bdb_instance_t
*db
= (bdb_instance_t
*) dbdata
;
461 DBC
*zone_cursor
= NULL
;
462 DBC
*host_cursor
= NULL
;
463 DBC
*join_cursor
= NULL
;
468 char *tmp_zone
, *tmp_host
= NULL
;
473 memset(&key
, 0, sizeof(DBT
));
474 memset(&data
, 0, sizeof(DBT
));
477 key
.data
= tmp_zone
= strdup(zone
);
478 if (key
.data
== NULL
) {
479 result
= ISC_R_NOMEMORY
;
482 key
.size
= strlen(key
.data
);
484 /* get a cursor to loop through zone data */
485 if (db
->zone
->cursor(db
->zone
, NULL
, &zone_cursor
, 0) != 0) {
486 result
= ISC_R_FAILURE
;
490 /* initialize zone_cursor with zone_key */
491 if (zone_cursor
->c_get(zone_cursor
, &key
, &data
, DB_SET
) != 0) {
492 result
= ISC_R_NOTFOUND
;
497 key
.data
= tmp_host
= strdup(name
);
498 if (key
.data
== NULL
) {
499 result
= ISC_R_NOMEMORY
;
502 key
.size
= strlen(key
.data
);
504 /* get a cursor to loop through host data */
505 if (db
->host
->cursor(db
->host
, NULL
, &host_cursor
, 0) != 0) {
506 result
= ISC_R_FAILURE
;
510 /* initialize host_cursor with host_key */
511 if (host_cursor
->c_get(host_cursor
, &key
, &data
, DB_SET
) != 0) {
512 result
= ISC_R_NOTFOUND
;
516 cur_arr
[0] = zone_cursor
;
517 cur_arr
[1] = host_cursor
;
520 db
->data
->join(db
->data
, cur_arr
, &join_cursor
, 0);
522 while ((bdbres
= join_cursor
->c_get(join_cursor
, &key
,
525 tmp
= realloc(tmp
, data
.size
+ 1);
529 strncpy(tmp
, data
.data
, data
.size
);
530 tmp
[data
.size
] = '\0';
532 if (bdb_parse_data(tmp
, &pd
) != ISC_R_SUCCESS
)
535 result
= dns_sdlz_putrr(lookup
, pd
.type
, pd
.ttl
, pd
.data
);
537 if (result
!= ISC_R_SUCCESS
)
539 } /* end while loop */
545 if (tmp_zone
!= NULL
)
547 if (tmp_host
!= NULL
)
550 /* get rid of the joined cusor */
551 if (join_cursor
!= NULL
)
552 join_cursor
->c_close(join_cursor
);
554 /* get rid of zone_cursor */
555 if (zone_cursor
!= NULL
)
556 zone_cursor
->c_close(zone_cursor
);
558 /* get rid of host_cursor */
559 if (host_cursor
!= NULL
)
560 host_cursor
->c_close(host_cursor
);
566 /*% Initializes, sets flags and then opens Berkeley databases. */
569 bdb_opendb(DB_ENV
*db_env
, DBTYPE db_type
, DB
**db
, const char *db_name
,
570 char *db_file
, int flags
) {
574 /* Initialize the database. */
575 if ((result
= db_create(db
, db_env
, 0)) != 0) {
576 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
577 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
578 "BDB could not initialize %s database. "
580 db_name
, db_strerror(result
));
581 return ISC_R_FAILURE
;
584 /* set database flags. */
585 if ((result
= (*db
)->set_flags(*db
, flags
)) != 0) {
586 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
587 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
588 "BDB could not set flags for %s database. "
590 db_name
, db_strerror(result
));
591 return ISC_R_FAILURE
;
594 /* open the database. */
595 if ((result
= (*db
)->open(*db
, NULL
, db_file
, db_name
, db_type
,
596 DB_RDONLY
| bdb_threads
, 0)) != 0) {
597 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
598 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
599 "BDB could not open %s database in %s. "
601 db_name
, db_file
, db_strerror(result
));
602 return ISC_R_FAILURE
;
605 return ISC_R_SUCCESS
;
609 bdb_create(const char *dlzname
, unsigned int argc
, char *argv
[],
610 void *driverarg
, void **dbdata
)
614 bdb_instance_t
*db
= NULL
;
619 /* verify we have 3 arg's passed to the driver */
621 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
622 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
623 "Berkeley DB driver requires at least "
624 "2 command line args.");
625 return (ISC_R_FAILURE
);
628 /* allocate and zero memory for driver structure */
629 db
= isc_mem_get(ns_g_mctx
, sizeof(bdb_instance_t
));
631 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
632 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
633 "Could not allocate memory for "
634 "database instance object.");
635 return (ISC_R_NOMEMORY
);
637 memset(db
, 0, sizeof(bdb_instance_t
));
639 /* attach to the memory context */
640 isc_mem_attach(ns_g_mctx
, &db
->mctx
);
642 /* create BDB environment
643 * Basically BDB allocates and assigns memory to db->dbenv
645 bdbres
= db_env_create(&db
->dbenv
, 0);
647 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
648 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
649 "BDB environment could not be created. "
651 db_strerror(bdbres
));
652 result
= ISC_R_FAILURE
;
656 /* open BDB environment */
657 bdbres
= db
->dbenv
->open(db
->dbenv
, argv
[1],
658 DB_INIT_CDB
| DB_INIT_MPOOL
|
659 bdb_threads
| DB_CREATE
,
662 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
663 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
664 "BDB environment at '%s' could not be opened. "
666 argv
[1], db_strerror(bdbres
));
667 result
= ISC_R_FAILURE
;
671 /* open dlz_data database. */
672 result
= bdb_opendb(db
->dbenv
, DB_UNKNOWN
, &db
->data
,
673 dlz_data
, argv
[2], 0);
674 if (result
!= ISC_R_SUCCESS
)
677 /* open dlz_host database. */
678 result
= bdb_opendb(db
->dbenv
, DB_UNKNOWN
, &db
->host
,
680 DB_DUP
| DB_DUPSORT
);
681 if (result
!= ISC_R_SUCCESS
)
684 /* open dlz_zone database. */
685 result
= bdb_opendb(db
->dbenv
, DB_UNKNOWN
, &db
->zone
,
687 DB_DUP
| DB_DUPSORT
);
688 if (result
!= ISC_R_SUCCESS
)
691 /* open dlz_client database. */
692 result
= bdb_opendb(db
->dbenv
, DB_UNKNOWN
, &db
->client
,
693 dlz_client
, argv
[2], DB_DUP
| DB_DUPSORT
);
694 if (result
!= ISC_R_SUCCESS
)
697 /* associate the host secondary database with the primary database */
698 bdbres
= db
->data
->associate(db
->data
, NULL
, db
->host
, NULL
, 0);
700 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
701 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
702 "BDB could not associate %s database with %s. "
704 dlz_host
, dlz_data
, db_strerror(bdbres
));
705 result
= ISC_R_FAILURE
;
709 /* associate the zone secondary database with the primary database */
710 bdbres
= db
->data
->associate(db
->data
, NULL
, db
->zone
, NULL
, 0);
712 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
713 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
714 "BDB could not associate %s database with %s. "
716 dlz_zone
, dlz_data
, db_strerror(bdbres
));
717 result
= ISC_R_FAILURE
;
723 return(ISC_R_SUCCESS
);
732 bdb_destroy(void *driverarg
, void *dbdata
)
736 bdb_cleanup((bdb_instance_t
*) dbdata
);
739 /* bdb_authority not needed as authority data is returned by lookup */
740 static dns_sdlzmethods_t dlz_bdb_methods
= {
751 * Wrapper around dns_sdlzregister().
758 * Write debugging message to log
760 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
761 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
762 "Registering DLZ bdb driver.");
764 result
= dns_sdlzregister("bdb", &dlz_bdb_methods
, NULL
,
765 DNS_SDLZFLAG_RELATIVEOWNER
|
766 DNS_SDLZFLAG_RELATIVERDATA
|
767 DNS_SDLZFLAG_THREADSAFE
,
768 ns_g_mctx
, &dlz_bdb
);
769 if (result
!= ISC_R_SUCCESS
) {
770 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
771 "dns_sdlzregister() failed: %s",
772 isc_result_totext(result
));
773 result
= ISC_R_UNEXPECTED
;
781 * Wrapper around dns_sdlzunregister().
784 dlz_bdb_clear(void) {
787 * Write debugging message to log
789 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
790 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
791 "Unregistering DLZ bdb driver.");
794 dns_sdlzunregister(&dlz_bdb
);