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) 1999-2001  Internet Software Consortium.
38 *
39 * Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM
44 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
46 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
47 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
48 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
49 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
50 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51 */
52
53#ifdef DLZ_BDB
54
55/*
56 * exit codes
57 * 0 everything ok
58 * 1 error parsing command line
59 * 2 Missing, too many or invalid combination of command line parameters
60 * 3 Unable to open BDB database.
61 * 4 Unable to allocate memory for, or create lexer.
62 * 5 unable to perform BDB cursor operation
63 */
64
65#include <config.h>
66#include <stdio.h>
67#include <string.h>
68#include <stdlib.h>
69
70#include <isc/buffer.h>
71#include <isc/commandline.h>
72#include <isc/formatcheck.h>
73#include <isc/lex.h>
74#include <isc/mem.h>
75#include <isc/result.h>
76#include <isc/string.h>
77#include <isc/util.h>
78
79#include <db.h>
80
81/* shut up compiler warnings about no previous prototype */
82
83static void
84show_usage(void);
85
86int
87getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
88
89int
90gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
91
92void
93bdb_cleanup(void);
94
95isc_result_t
96bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags);
97
98void
99put_data(isc_boolean_t dns_data, char *input_key, char *input_data);
100
101void
102insert_data(void);
103
104isc_result_t
105openBDB(void);
106
107isc_result_t
108open_lexer(void);
109
110void
111close_lexer(void);
112
113isc_result_t
114bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata);
115
116void
117operation_add(void);
118
119void
120operation_bulk(void);
121
122void
123operation_listOrDelete(isc_boolean_t dlt);
124
125
126/*%
127 * Maximum length of a single data line that
128 * may be inserted into database by this program.
129 * If you need to insert a line of data that is more
130 * than 10,000 characters change this definition.
131 */
132
133#define max_data_len 10000
134
135/*%
136 * BDB database names.  If you want to use different
137 * database names change them here.
138 */
139
140#define dlz_data "dns_data"
141#define dlz_zone "dns_zone"
142#define dlz_host "dns_host"
143#define dlz_client "dns_client"
144
145
146/*%
147 * Error code returned by BDB secondary index callback functions.
148 * This error is returned if the callback function could not create
149 * the secondary index for any reason.
150 */
151
152#define BDBparseErr 1
153
154/* A struct to hold all the relevant info about the database */
155
156typedef struct bdb_instance {
157	DB_ENV	*dbenv;		/* BDB environment */
158	DB	*data;		/* dns_data database handle */
159	DBC	*cursor;	/* database cursor */
160	DBC	*cursor2; /* second cursor used during list operation. */
161	DBC	*cursor3; /* third cursor used during list operation */
162	DBC	*cursor4; /* fourth cursor used during list operation */
163	DB	*zone;		/* zone database handle */
164	DB	*host;		/* host database handle */
165	DB	*client;	/* client database handle */
166} bdb_instance_t;
167
168/* Possible operations */
169
170#define list 1		/* list data */
171#define dele 2		/* delete data */
172#define add 3		/* add a single piece of data */
173#define bulk 4		/* bulk load data */
174
175
176/*%
177 * quit macro is used instead of exit.  quit always trys to close the lexer
178 * and the BDB database before exiting.
179 */
180
181#define quit(i) 	close_lexer(); bdb_cleanup(); exit(i);
182
183/*%
184 * checkOp is used to verify that only one operation (list, del, add,
185 * bulk from file, bulk from stdin) is specified on the command line.
186 * This prevents a user from specifying two operations on the command
187 * line, which would make no sense anyway.
188 */
189
190#define checkOp(x) if (x != 0) {fprintf(stderr, "\nonly one operation "\
191				"(l e d a f s) may be specified\n"); quit(2);}
192
193/*%
194 * checkParam is used to only allow a parameter to be specified once.
195 * I.E. the parameter key can only be used on the command line once.
196 * any attempt to use it twice causes an error.
197 */
198
199#define checkParam(x, y) if (x != NULL) {fprintf(stderr, "\n%s may only "\
200					 "be specified once\n", y); quit(2);}
201
202/*%
203 * checkInvalidParam is used to only allow paramters which make sense for
204 * the operation selected.  I.E. passing the key parameter makes no sense
205 * for the add operation, and thus it isn't allowed.
206 */
207
208#define checkInvalidParam(x, y, z) if (x != NULL) {fprintf(stderr, "\n%s "\
209						"may not be specified %s\n", y, z); quit(2);}
210
211/*%
212 * checkInvalidOption is used to only allow paramters which make sense for
213 * the operation selected - but checks boolean options.
214 * I.E. passing the "b" bare_list parameter makes no sense for the add
215 * operation, and thus it isn't allowed.
216 * if w == x then output error message "flag", "message"
217 */
218
219#define checkInvalidOption(w, x, y, z) if (w == x) {fprintf(stderr, "\n%s "\
220						"may not be specified %s\n", y, z); quit(2);}
221
222/* Global Variables */
223
224int operation = 0;		/*%< operation to perform. */
225/*% allow new lock files or DB to be created. */
226isc_boolean_t create_allowed = isc_boolean_false;
227char *key = NULL;		/*%< key to use in list & del operations */
228
229/*% dump DB in DLZBDB bulk format */
230isc_boolean_t list_everything = isc_boolean_false;
231unsigned int key_val; /*%< key as unsigned int used in list & del operations */
232char *zone = NULL;		/*%< zone to use in list operations */
233char *host = NULL;		/*%< host to use in list operations */
234char *c_zone = NULL;	 /*%< client zone to use in list operations */
235char *c_ip = NULL;	   /*%< client IP to use in list operations */
236char *a_data = NULL;		/*%< data in add operation */
237char *bulk_file = NULL;		/*%< bulk data file to load */
238char *db_envdir = NULL;		/*%< BDB environment location  */
239char *db_file = NULL;		/*%< BDB database file location. */
240bdb_instance_t db;		/* BDB instance we are operating on */
241isc_lex_t *lexer = NULL; /*%< lexer for use to use in parsing input */
242isc_mem_t *lex_mctx = NULL;	/*%< memory context for lexer */
243char lex_data_buf[max_data_len]; /*%< data array to use for lex_buffer below */
244isc_buffer_t lex_buffer;   /*%< buffer for lexer during add operation */
245
246
247/*%
248 * Displays usage message
249 */
250
251static void
252show_usage(void) {
253	fprintf(stderr, "\n\n\
254---Usage:---------------------------------------------------------------------\
255\n\n\
256   List data:\n\
257      dlzbdb -l [-k key] [-z zone] [-h host] [-c client_zone] [-i client_ip]\n\
258               BDB_environment BDB_database\n\n\
259   Delete data:\n\
260      dlzbdb -d [-k key] [-c client_zone] [-i client_ip]\n\
261               BDB_environment BDB_database\n\n\
262   Bulk load data from file:\n\
263      dlzbdb -f file_to_load BDB_environment BDB_database\n\n\
264   Bulk load data from stdin\n\
265      dlzbdb -s BDB_environment BDB_database\n\n\
266   Add data:\n\
267      dlzbdb -a \"dns data to be added\" BDB_environment BDB_database\n\n\
268   Export data:\n\
269      dlzbdb -e BDB_environment BDB_database\n\n\
270   Normally operations can only be performed on an existing database files.\n\
271   Use the -n flag with any operation to allow files to be created.\n\
272   Existing files will NOT be truncated by using the -n flag.\n\
273   The -n flag will allow a new database to be created, or allow new\n\
274   environment files to be created for an existing database.\n\n\
275---Format for -f & -a options:------------------------------------------------\
276\n\n\
277db_type zone host dns_type ttl ip\n\
278db_type zone host dns_type ttl mx_priority mail_host\n\
279db_type zone host dns_type ttl nm_svr resp_psn serial refresh retry expire min\
280\n\
281db_type zone client_ip\n\n\
282---Examples:------------------------------------------------------------------\
283\n\n\
284d mynm.com www A 10 127.0.0.1\n\
285d mynm.com @ MX 10 5 mail\n\
286d mynm.com @ SOA 10 ns1.mynm.com. root.mynm.com. 2 28800 7200 604800 86400\n\
287c mynm.com 127.0.0.1\n\
288c mynm.com 192.168.0.10\n\
289");
290quit(1);
291}
292
293
294/*% BDB callback to create zone secondary index */
295
296int
297getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) {
298	char *tmp;
299	char *left;
300	char *right;
301	int result=0;
302
303	UNUSED(dbp);
304	UNUSED(pkey);
305
306	/* Allocate memory to use in parsing the string */
307	tmp = right = malloc(pdata->size + 1);
308
309	/* verify memory was allocated */
310	if (right == NULL) {
311		result = BDBparseErr;
312		goto getzone_cleanup;
313	}
314
315	/* copy data string into newly allocated memory */
316	strncpy(right, pdata->data, pdata->size);
317	right[pdata->size] = '\0';
318
319	/* split string at the first space */
320	left = isc_string_separate(&right, " ");
321
322	/* copy string for "zone" secondary index */
323	skey->data = strdup(left);
324	if (skey->data == NULL) {
325		result = BDBparseErr;
326		goto getzone_cleanup;
327	}
328	/* set required values for BDB */
329	skey->size = strlen(skey->data);
330	skey->flags = DB_DBT_APPMALLOC;
331
332 getzone_cleanup:
333
334	/* cleanup memory */
335	if (tmp != NULL)
336		free(tmp);
337
338	return result;
339}
340
341/*%
342 * BDB callback to create host secondary index
343 */
344
345int
346gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) {
347	char *tmp;
348	char *left;
349	char *right;
350	int result=0;
351
352	UNUSED(dbp);
353	UNUSED(pkey);
354
355	/* allocate memory to use in parsing the string */
356	tmp = right = malloc(pdata->size + 1);
357
358	/* verify memory was allocated */
359	if (tmp == NULL) {
360		result = BDBparseErr;
361		goto gethost_cleanup;
362	}
363
364	/* copy data string into newly allocated memory */
365	strncpy(right, pdata->data, pdata->size);
366	right[pdata->size] = '\0';
367
368	/* we don't care about left string. */
369	/* memory of left string will be freed when tmp is freed. */
370	isc_string_separate(&right, " ");
371
372	/* verify right still has some characters left */
373	if (right == NULL) {
374		result = BDBparseErr;
375		goto gethost_cleanup;
376	}
377
378	/* get "host" from data string */
379	left = isc_string_separate(&right, " ");
380	/* copy string for "host" secondary index */
381	skey->data = strdup(left);
382	if (skey->data == NULL) {
383		result = BDBparseErr;
384		goto gethost_cleanup;
385	}
386	/* set required values for BDB */
387	skey->size = strlen(skey->data);
388	skey->flags = DB_DBT_APPMALLOC;
389
390 gethost_cleanup:
391
392	/* cleanup memory */
393	if (tmp != NULL)
394		free(tmp);
395
396	return result;
397}
398
399/*%
400 * Performs BDB cleanup. Close each database that we opened.
401 * Close environment.  Set each var to NULL so we know they
402 * were closed and don't accidentally try to close them twice.
403 */
404
405void
406bdb_cleanup(void) {
407
408	/* close cursors */
409	if (db.cursor4 != NULL) {
410		db.cursor4->c_close(db.cursor4);
411		db.cursor4 = NULL;
412	}
413
414	if (db.cursor3 != NULL) {
415		db.cursor3->c_close(db.cursor3);
416		db.cursor3 = NULL;
417	}
418
419	if (db.cursor2 != NULL) {
420		db.cursor2->c_close(db.cursor2);
421		db.cursor2 = NULL;
422	}
423
424	if (db.cursor != NULL) {
425		db.cursor->c_close(db.cursor);
426		db.cursor = NULL;
427	}
428
429	/* close databases */
430	if (db.data != NULL) {
431		db.data->close(db.data, 0);
432		db.data = NULL;
433	}
434	if (db.host != NULL) {
435		db.host->close(db.host, 0);
436		db.host = NULL;
437	}
438	if (db.zone != NULL) {
439		db.zone->close(db.zone, 0);
440		db.zone = NULL;
441	}
442	if (db.client != NULL) {
443		db.client->close(db.client, 0);
444		db.client = NULL;
445	}
446
447	/* close environment */
448	if (db.dbenv != NULL) {
449		db.dbenv->close(db.dbenv, 0);
450		db.dbenv = NULL;
451	}
452}
453
454/*% Initializes, sets flags and then opens Berkeley databases. */
455
456isc_result_t
457bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags) {
458
459	int result;
460	int createFlag = 0;
461
462	/* Initialize the database. */
463	if ((result = db_create(db_out, db.dbenv, 0)) != 0) {
464		fprintf(stderr, "BDB could not initialize %s database. BDB error: %s",
465			db_name, db_strerror(result));
466		return ISC_R_FAILURE;
467	}
468
469	/* set database flags. */
470	if ((result = (*db_out)->set_flags(*db_out, flags)) != 0) {
471		fprintf(stderr, "BDB could not set flags for %s database. BDB error: %s",
472			db_name, db_strerror(result));
473		return ISC_R_FAILURE;
474	}
475
476	if (create_allowed == isc_boolean_true) {
477		createFlag = DB_CREATE;
478	}
479	/* open the database. */
480	if ((result = (*db_out)->open(*db_out, NULL, db_file, db_name, db_type,
481				      createFlag, 0)) != 0) {
482		fprintf(stderr, "BDB could not open %s database in %s. BDB error: %s",
483			db_name, db_file, db_strerror(result));
484		return ISC_R_FAILURE;
485	}
486
487	return ISC_R_SUCCESS;
488}
489
490/*%
491 * parses input and adds it to the BDB database
492 * Lexer should be instantiated, and either a file or buffer opened for it.
493 * The insert_data function is used by both the add, and bulk insert
494 * operations
495 */
496
497void
498put_data(isc_boolean_t dns_data, char *input_key, char *input_data) {
499
500	int bdbres;
501	DBT key, data;
502
503	/* make sure key & data are completely empty */
504	memset(&key, 0, sizeof(key));
505	memset(&data, 0, sizeof(data));
506
507	/* if client data, setup key for insertion */
508	if (!dns_data && input_key != NULL) {
509		key.data = input_key;
510		key.size = strlen(input_key);
511		key.flags = 0;
512	}
513	/* always setup data for insertion */
514	data.data = input_data;
515	data.size = strlen(input_data);
516	data.flags = 0;
517
518	/* execute insert against appropriate database. */
519	if (dns_data) {
520		bdbres = db.data->put(db.data, NULL, &key, &data, DB_APPEND);
521	} else {
522		bdbres = db.client->put(db.client, NULL, &key, &data, 0);
523	}
524
525	/* if something went wrong, log error and quit */
526	if (bdbres != 0) {
527		fprintf(stderr, "BDB could not insert data.  Error: %s",
528			db_strerror(bdbres));
529		quit(5);
530	}
531}
532
533void
534insert_data(void) {
535	unsigned int opt =
536		ISC_LEXOPT_EOL |		/* Want end-of-line token. */
537		ISC_LEXOPT_EOF | 		/* Want end-of-file token. */
538		ISC_LEXOPT_QSTRING |		/* Recognize qstrings. */
539		ISC_LEXOPT_QSTRINGMULTILINE;	/* Allow multiline "" strings */
540
541	isc_result_t result;
542	isc_token_t token;	/* token from lexer */
543	isc_boolean_t loop = isc_boolean_true;
544	isc_boolean_t have_czone = isc_boolean_false;
545	char data_arr[max_data_len];
546	isc_buffer_t buf;
547	char data_arr2[max_data_len];
548	isc_buffer_t buf2;
549	char data_type = 'u'; /* u =unknown, b =bad token, d/D =DNS, c/C =client IP */
550
551	/* Initialize buffers */
552	isc_buffer_init(&buf, &data_arr, max_data_len);
553	isc_buffer_init(&buf2, &data_arr2, max_data_len);
554
555	while (loop) {
556		result = isc_lex_gettoken(lexer, opt, &token);
557		if (result != ISC_R_SUCCESS)
558			goto data_cleanup;
559
560		switch(token.type) {
561		case isc_tokentype_string:
562			if (data_type == 'u') {
563				/* store data_type */
564				strncpy(&data_type, token.value.as_pointer, 1);
565				/* verify data_type was specified correctly on input */
566				if (strlen(token.value.as_pointer) > 1 || (
567					    data_type != 'd' && data_type != 'D' &&
568					    data_type != 'c' && data_type != 'C') ) {
569					/* if not, set to 'b' so this line is ignored. */
570					data_type = 'b';
571				}
572			} else if (data_type == 'c' || data_type == 'C') {
573				if (have_czone == isc_boolean_true) {
574					isc_buffer_putstr(&buf2, token.value.as_pointer);
575					/* add string terminator to buffer */
576					isc_buffer_putmem(&buf2, "\0", 1);
577				} else {
578					isc_buffer_putstr(&buf, token.value.as_pointer);
579					/* add string terminator to buffer */
580					isc_buffer_putmem(&buf, "\0", 1);
581					have_czone = isc_boolean_true;
582				}
583			} else {
584				isc_buffer_putstr(&buf, token.value.as_pointer);
585				isc_buffer_putstr(&buf, " ");
586			}
587			break;
588		case isc_tokentype_qstring:
589			isc_buffer_putstr(&buf, "\"");
590			isc_buffer_putstr(&buf, token.value.as_pointer);
591			isc_buffer_putstr(&buf, "\" ");
592			break;
593		case isc_tokentype_eol:
594		case isc_tokentype_eof:
595
596			if ((data_type != 'u' && isc_buffer_usedlength(&buf) > 0) || data_type == 'b') {
597				/* perform insert operation */
598				if (data_type == 'd' || data_type == 'D') {
599					/* add string terminator to buffer */
600					isc_buffer_putmem(&buf, "\0", 1);
601					put_data(isc_boolean_true, NULL, (char *) &data_arr);
602				} else if (data_type == 'c' || data_type == 'C') {
603					put_data(isc_boolean_false, (char *) &data_arr,
604						 (char *) &data_arr2);
605				} else if (data_type == 'b') {
606					fprintf(stderr, "Bad / unknown token encountered on line %lu."\
607						"  Skipping line.",	isc_lex_getsourceline(lexer) - 1);
608				} else {
609					fprintf(stderr, "Bad / unknown db data type encountered on " \
610						"line %lu.  Skipping line\n", isc_lex_getsourceline(lexer) - 1);
611				}
612			}
613
614			if (token.type == isc_tokentype_eof) {
615				loop = isc_boolean_false;
616			}
617
618			/* reset buffer for next insert */
619			isc_buffer_clear(&buf);
620			isc_buffer_clear(&buf2);
621			have_czone = isc_boolean_false;
622			data_type ='u';
623			break;
624		default:
625			data_type = 'b';
626			break;
627		}
628	}
629
630	return;
631
632 data_cleanup:
633	/* let user know we had problems */
634	fprintf(stderr, "Unknown error processing tokens during \"add\" or " \
635		"\"bulk\" operation.\nStoped processing on line %lu.",
636		isc_lex_getsourceline(lexer));
637}
638
639
640isc_result_t
641openBDB(void) {
642
643	int bdbres;
644	isc_result_t result;
645
646	/* create BDB environment  */
647	/* Basically BDB allocates and assigns memory to db->dbenv */
648	bdbres = db_env_create(&db.dbenv, 0);
649	if (bdbres != 0) {
650		fprintf(stderr, "BDB environment could not be created. BDB error: %s",
651			db_strerror(bdbres));
652		result = ISC_R_FAILURE;
653		goto openBDB_cleanup;
654	}
655
656	/* open BDB environment */
657	if (create_allowed == isc_boolean_true) {
658		/* allowed to create new files */
659		bdbres = db.dbenv->open(db.dbenv, db_envdir,
660					DB_INIT_CDB | DB_INIT_MPOOL | DB_CREATE, 0);
661	} else {	/* not allowed to create new files. */
662		bdbres = db.dbenv->open(db.dbenv, db_envdir,
663					DB_INIT_CDB | DB_INIT_MPOOL, 0);
664	}
665	if (bdbres != 0) {
666		fprintf(stderr, "BDB environment at '%s' could not be opened. BDB " \
667			"error: %s", db_envdir, db_strerror(bdbres));
668		result = ISC_R_FAILURE;
669		goto openBDB_cleanup;
670	}
671
672	/* open dlz_data database. */
673
674	result = bdb_opendb(DB_RECNO, &db.data, dlz_data, 0);
675	if (result != ISC_R_SUCCESS)
676		goto openBDB_cleanup;
677
678	/* open dlz_host database */
679	result = bdb_opendb(DB_BTREE, &db.host, dlz_host, DB_DUP | DB_DUPSORT);
680	if (result != ISC_R_SUCCESS)
681		goto openBDB_cleanup;
682
683	/* open dlz_zone database. */
684	result = bdb_opendb(DB_BTREE, &db.zone, dlz_zone, DB_DUP | DB_DUPSORT);
685	if (result != ISC_R_SUCCESS)
686		goto openBDB_cleanup;
687
688	/* open dlz_client database. */
689	result = bdb_opendb(DB_BTREE, &db.client, dlz_client, DB_DUP | DB_DUPSORT);
690	if (result != ISC_R_SUCCESS)
691		goto openBDB_cleanup;
692
693	/* associate the host secondary database with the primary database */
694	bdbres = db.data->associate(db.data, NULL, db.host, gethost, 0);
695	if (bdbres != 0) {
696		fprintf(stderr, "BDB could not associate %s database with %s. BDB "\
697			"error: %s", dlz_host, dlz_data, db_strerror(bdbres));
698		result = ISC_R_FAILURE;
699		goto openBDB_cleanup;
700	}
701
702	/* associate the zone secondary database with the primary database */
703	bdbres = db.data->associate(db.data, NULL, db.zone, getzone, 0);
704	if (bdbres != 0) {
705		fprintf(stderr, "BDB could not associate %s database with %s. BDB "\
706			"error: %s", dlz_zone, dlz_data, db_strerror(bdbres));
707		result = ISC_R_FAILURE;
708		goto openBDB_cleanup;
709	}
710
711	return result;
712
713 openBDB_cleanup:
714
715	bdb_cleanup();
716	return result;
717}
718
719/*% Create & open lexer to parse input data */
720
721isc_result_t
722open_lexer(void) {
723	isc_result_t result;
724
725	/* check if we already opened the lexer, if we did, return success */
726	if (lexer != NULL)
727		return ISC_R_SUCCESS;
728
729	/* allocate memory for lexer, and verify it was allocated */
730	result = isc_mem_create(0, 0, &lex_mctx);
731	if (result != ISC_R_SUCCESS) {
732		fprintf(stderr, "unexpected error creating lexer\n");
733		return result;
734	}
735
736	/* create lexer */
737	result = isc_lex_create(lex_mctx, 1500, &lexer);
738	if (result != ISC_R_SUCCESS)
739		fprintf(stderr, "unexpected error creating lexer\n");
740
741	/* set allowed commenting style */
742	isc_lex_setcomments(lexer, ISC_LEXCOMMENT_C |	/* Allow C comments */
743			    ISC_LEXCOMMENT_CPLUSPLUS |	/* Allow C++ comments */
744			    ISC_LEXCOMMENT_SHELL);		/* Allow shellcomments */
745
746	isc_buffer_init(&lex_buffer, &lex_data_buf, max_data_len);
747
748	return result;
749}
750
751/*% Close the lexer, and cleanup memory */
752
753void
754close_lexer(void) {
755
756	/* If lexer is still open, close it & destroy it. */
757	if (lexer != NULL) {
758		isc_lex_close(lexer);
759		isc_lex_destroy(&lexer);
760	}
761
762	/* if lexer memory is still allocated, destroy it. */
763	if (lex_mctx != NULL)
764		isc_mem_destroy(&lex_mctx);
765}
766
767/*% Perform add operation */
768
769void
770operation_add(void) {
771	/* check for any parameters that are not allowed during add */
772	checkInvalidParam(key, "k", "for add operation");
773	checkInvalidParam(zone, "z", "for add operation");
774	checkInvalidParam(host, "h", "for add operation");
775	checkInvalidParam(c_zone, "c", "for add operation");
776	checkInvalidParam(c_ip, "i", "for add operation");
777	checkInvalidOption(list_everything, isc_boolean_true, "e",
778			   "for add operation");
779
780	/* if open lexer fails it alread prints error messages. */
781	if (open_lexer() != ISC_R_SUCCESS) {
782		quit(4);
783	}
784
785	/* copy input data to buffer */
786	isc_buffer_putstr(&lex_buffer, a_data);
787
788	/* tell lexer to use buffer as input  */
789	if (isc_lex_openbuffer(lexer, &lex_buffer) != ISC_R_SUCCESS) {
790		fprintf(stderr, "unexpected error opening lexer buffer");
791		quit(4);
792	}
793
794	/*common logic for "add" & "bulk" operations are handled by insert_data */
795	insert_data();
796}
797
798/*% Perform bulk insert operation */
799
800void
801operation_bulk(void) {
802	/* check for any parameters that are not allowed during bulk */
803	checkInvalidParam(key, "k", "for bulk load operation");
804	checkInvalidParam(zone, "z", "for bulk load operation");
805	checkInvalidParam(host, "h", "for bulk load operation");
806	checkInvalidParam(c_zone, "c", "for bulk load operation");
807	checkInvalidParam(c_ip, "i", "for bulk load operation");
808	checkInvalidOption(list_everything, isc_boolean_true, "e",
809			   "for bulk load operation");
810
811	/* if open lexer fails it already prints error messages. */
812	if (open_lexer() != ISC_R_SUCCESS) {
813		quit(4);
814	}
815
816	if (bulk_file == NULL) {
817		if (isc_lex_openstream(lexer, stdin) != ISC_R_SUCCESS) {
818			fprintf(stderr, "unexpected error opening stdin by lexer.");
819			quit(4);
820		}
821	} else if (isc_lex_openfile(lexer, bulk_file) != ISC_R_SUCCESS) {
822		fprintf(stderr, "unexpected error opening %s by lexer.", bulk_file);
823		quit(4);
824	}
825
826	/* common logic for "add" & "bulk" operations are handled by insert_data */
827	insert_data();
828}
829
830isc_result_t
831bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata) {
832
833	int bdbres;
834	db_recno_t recNum;
835	char *retkey = NULL, *retdata;
836	size_t retklen = 0, retdlen;
837	void *p;
838
839	/* use a 5MB buffer for the bulk dump */
840	int buffer_size = 5 * 1024 * 1024;
841
842	/* try to allocate a 5 MB buffer, if we fail write err msg, die. */
843	bdbdata->data = malloc(buffer_size);
844	if (bdbdata->data == NULL) {
845		fprintf(stderr,
846			"Unable to allocate 5 MB buffer for bulk database dump\n");
847		return ISC_R_FAILURE;
848	}
849	bdbdata->ulen = buffer_size;
850	bdbdata->flags = DB_DBT_USERMEM;
851
852	/* get a cursor, make sure it worked. */
853	bdbres = database->cursor(database, NULL, &dbcursor, 0);
854	if (bdbres != 0) {
855		fprintf(stderr, "Unexpected error. BDB Error: %s\n",db_strerror(bdbres));
856		free(bdbdata->data);
857		return ISC_R_FAILURE;
858	}
859
860	/* loop and dump all data */
861	for (;;) {
862
863		/* loop through data until DB_NOTFOUND is returned */
864		bdbres = dbcursor->c_get(dbcursor, bdbkey, bdbdata,
865					 DB_MULTIPLE_KEY | DB_NEXT);
866		/* if not successful did we encounter DB_NOTFOUND, or */
867		/* have  a different problem. */
868		if (bdbres != 0) {
869			if (bdbres != DB_NOTFOUND) {
870				fprintf(stderr, "Unexpected error. BDB Error: %s\n",
871					db_strerror(bdbres));
872				free(bdbdata->data);
873				return ISC_R_FAILURE;
874			}
875			/* Hit DB_NOTFOUND which means end of data. */
876			break;
877		} /* end of if (bdbres !=0) */
878
879		for (DB_MULTIPLE_INIT(p, bdbdata);;) {
880			if (type == 'c')
881				DB_MULTIPLE_KEY_NEXT(p, bdbdata, retkey, retklen, retdata, retdlen);
882			else
883				DB_MULTIPLE_RECNO_NEXT(p, bdbdata, recNum, retdata, retdlen);
884
885			if (p == NULL)
886				break;
887			if (type == 'c')
888				printf("c %.*s %.*s\n",(int)retklen, retkey,(int)retdlen, retdata);
889			else
890				printf("d %.*s\n", (int)retdlen, retdata);
891		} /* end of for (DB_MULTIPLE_INIT....) */
892
893	} /* end of for (;;) */
894
895	/* free the buffer we created earlier */
896	free(bdbdata->data);
897
898	return ISC_R_SUCCESS;
899}
900
901/*%
902 * Perform listOrDelete operation
903 * if dlt == true, delete data
904 * else list data
905 */
906
907void
908operation_listOrDelete(isc_boolean_t dlt) {
909
910	int bdbres = 0;
911	DBC *curList[3];
912	DBT bdbkey, bdbdata;
913	db_recno_t recno;
914	int curIndex = 0;
915
916
917	/* verify that only allowed parameters were passed. */
918	if (dlt == isc_boolean_true) {
919		checkInvalidParam(zone, "z", "for delete operation");
920		checkInvalidParam(host, "h", "for delete operation");
921		checkInvalidOption(list_everything, isc_boolean_true, "e",
922				   "for delete operation");
923		checkInvalidOption(create_allowed, isc_boolean_true, "n",
924				   "for delete operation");
925	} else if (key != NULL || zone != NULL || host != NULL) {
926		checkInvalidParam(c_zone, "c", "for list when k, z or h are specified");
927		checkInvalidParam(c_ip, "i", "for list when k, z, or h are specified");
928		checkInvalidOption(list_everything, isc_boolean_true, "e",
929				   "for list when k, z, or h are specified");
930		checkInvalidOption(create_allowed, isc_boolean_true, "n",
931				   "for list operation");
932	} else if (c_ip != NULL || c_zone != NULL) {
933		checkInvalidOption(list_everything, isc_boolean_true, "e",
934				   "for list when c or i are specified");
935		checkInvalidOption(create_allowed, isc_boolean_true, "n",
936				   "for list operation");
937	}
938
939	memset(&bdbkey, 0, sizeof(bdbkey));
940	memset(&bdbdata, 0, sizeof(bdbdata));
941
942	/* Dump database in "dlzbdb" bulk format */
943	if (list_everything == isc_boolean_true) {
944		if (bulk_write('c', db.client, db.cursor, &bdbkey, &bdbdata)
945		    != ISC_R_SUCCESS)
946			return;
947		memset(&bdbkey, 0, sizeof(bdbkey));
948		memset(&bdbdata, 0, sizeof(bdbdata));
949		bulk_write('d', db.data, db.cursor2, &bdbkey, &bdbdata);
950		return;
951	} /* end if (list_everything) */
952
953	/* set NULL the 2nd and 3rd positions in curList. */
954	/* that way later when add cursors to the join list */
955	/* it is already null terminated. */
956	curList[1] = curList[2] = NULL;
957
958	if (key != NULL) {
959		/* make sure other parameters weren't */
960		checkInvalidParam(zone, "z", "when k is specified");
961		checkInvalidParam(host, "h", "when k is specified");
962
963		recno = key_val;
964		bdbkey.data = &recno;
965		bdbkey.size = sizeof(recno);
966
967		if (dlt == isc_boolean_true) {
968			bdbres = db.data->del(db.data, NULL, &bdbkey, 0);
969		} else {
970			bdbdata.flags = DB_DBT_REALLOC;
971			bdbres = db.data->get(db.data, NULL, &bdbkey, &bdbdata, 0);
972
973			if (bdbres == 0) {
974				printf("KEY | DATA\n");
975				printf("%lu | %.*s\n", *(u_long *) bdbkey.data,
976				       (int)bdbdata.size, (char *)bdbdata.data);
977			}
978		} /* closes else of if (dlt == isc_boolean_true) */
979		if (bdbres == DB_NOTFOUND) {
980			printf("Key not found in database");
981		}
982	}	/* closes if (key != NULL) */
983
984		/* if zone is passed */
985	if (zone != NULL) {
986		/* create a cursor and make sure it worked */
987		bdbres = db.zone->cursor(db.zone, NULL, &db.cursor2, 0);
988		if (bdbres != 0) {
989			fprintf(stderr, "Unexpected error. BDB Error: %s\n",
990				db_strerror(bdbres));
991			return;
992		}
993
994		bdbkey.data = zone;
995		bdbkey.size = strlen(zone);
996		bdbres = db.cursor2->c_get(db.cursor2, &bdbkey, &bdbdata, DB_SET);
997		if (bdbres != 0) {
998			if (bdbres != DB_NOTFOUND) {
999				fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1000					db_strerror(bdbres));
1001			} else {
1002				printf("Zone not found in database");
1003			}
1004			return;
1005		}
1006
1007		/* add cursor to cursor list for later use in join */
1008		curList[curIndex++] = db.cursor2;
1009	}
1010
1011	/* if host is passed */
1012	if (host != NULL) {
1013
1014		/* create a cursor and make sure it worked. */
1015		bdbres = db.host->cursor(db.host, NULL, &db.cursor3, 0);
1016		if (bdbres != 0) {
1017			fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1018				db_strerror(bdbres));
1019			return;
1020		}
1021		bdbkey.data = host;
1022		bdbkey.size = strlen(host);
1023		bdbres = db.cursor3->c_get(db.cursor3, &bdbkey, &bdbdata, DB_SET);
1024		if (bdbres != 0) {
1025			if (bdbres != DB_NOTFOUND) {
1026				fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1027					db_strerror(bdbres));
1028			} else {
1029				printf("Host not found in database");
1030			}
1031			return;
1032		}
1033
1034		/* add cursor to cursor list for later use in join */
1035		curList[curIndex++] = db.cursor3;
1036	}
1037
1038
1039	if (zone != NULL || host != NULL) {
1040
1041		/* join any cursors */
1042		bdbres = db.data->join(db.data, curList, &db.cursor4, 0);
1043		if (bdbres != 0) {
1044			fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1045				db_strerror(bdbres));
1046			return;
1047		}
1048
1049		memset(&bdbkey, 0, sizeof(bdbkey));
1050		bdbkey.flags = DB_DBT_REALLOC;
1051		memset(&bdbdata, 0, sizeof(bdbdata));
1052		bdbdata.flags = DB_DBT_REALLOC;
1053
1054		/* print a header to explain the output */
1055		printf("KEY | DATA\n");
1056		/* loop and list all results. */
1057		while (bdbres == 0) {
1058			/* get data */
1059			bdbres = db.cursor4->c_get(db.cursor4, &bdbkey, &bdbdata, 0);
1060			/* verify call had no errors */
1061			if (bdbres != 0) {
1062				break;
1063			}
1064			printf("%lu | %.*s\n", *(u_long *) bdbkey.data,
1065			       (int)bdbdata.size, (char *)bdbdata.data);
1066		} /* closes while loop */
1067	}
1068
1069	if (c_ip != NULL && c_zone == NULL) {
1070		fprintf(stderr, "i may only be specified when c is also specified\n");
1071		quit(2);
1072	}
1073	/* if client_zone was passed */
1074	if (c_zone != NULL) {
1075
1076		/* create a cursor and make sure it worked. */
1077		if (dlt == isc_boolean_true) {
1078			/* open read-write cursor */
1079			bdbres = db.client->cursor(db.client, NULL, &db.cursor,
1080						   DB_WRITECURSOR);
1081		} else {
1082			/* open read only cursor */
1083			bdbres = db.client->cursor(db.client, NULL, &db.cursor, 0);
1084			/* print a header to explain the output */
1085			printf("CLIENT_ZONE | CLIENT_IP\n");
1086		}
1087
1088		bdbkey.data = c_zone;
1089		bdbkey.size = strlen(c_zone);
1090
1091		if (c_ip != NULL) {
1092			bdbdata.data = c_ip;
1093			bdbdata.size = strlen(c_ip);
1094			bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_GET_BOTH);
1095			if (bdbres == DB_NOTFOUND) {
1096				printf("Client zone & IP not found in database");
1097			}
1098		} else {
1099			bdbdata.flags = DB_DBT_REALLOC;
1100			bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_SET);
1101			if (bdbres == DB_NOTFOUND) {
1102				printf("Client zone not found in database");
1103			}
1104		}
1105
1106		while (bdbres == 0) {
1107			if (dlt == isc_boolean_false) {
1108				printf("%.*s | %.*s\n", (int)bdbkey.size, (char *) bdbkey.data,
1109				       (int)bdbdata.size, (char *) bdbdata.data);
1110			} else {
1111				/* delete record. */
1112				bdbres = db.cursor->c_del(db.cursor, 0);
1113				if (bdbres != 0) {
1114					fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1115						db_strerror(bdbres));
1116					break;
1117				}
1118			}
1119			if (c_ip != NULL) {
1120				break;
1121			}
1122			bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_NEXT_DUP);
1123			if (bdbres != 0) {
1124				break;
1125			}
1126		} /* end while loop */
1127	}
1128
1129
1130	if (bdbres != 0 && bdbres != DB_NOTFOUND) {
1131		fprintf(stderr, "Unexpected error during list operation " \
1132			"BDB error: %s", db_strerror(bdbres));
1133	}
1134
1135	if (bdbkey.flags == DB_DBT_REALLOC && bdbkey.data != NULL) {
1136		free(bdbkey.data);
1137	}
1138	if (bdbdata.flags == DB_DBT_REALLOC && bdbdata.data != NULL) {
1139		free(bdbdata.data);
1140	}
1141}
1142
1143
1144int
1145main(int argc, char **argv) {
1146
1147	int ch;
1148	char *endp;
1149
1150	/* there has to be at least 2 args, some operations require more */
1151	if (argc < 2)
1152		show_usage();
1153
1154	/* use the ISC commandline parser to get all the program arguments */
1155	while ((ch= isc_commandline_parse(argc, argv, "ldesna:f:k:z:h:c:i:")) != -1) {
1156		switch (ch) {
1157		case 'n':
1158			create_allowed = isc_boolean_true;
1159			break;
1160		case 'l':
1161			checkOp(operation);
1162			operation = list;
1163			break;
1164		case 'd':
1165			checkOp(operation);
1166			operation = dele;
1167			break;
1168		case 'a':
1169			checkOp(operation);
1170			operation = add;
1171			a_data = isc_commandline_argument;
1172			break;
1173		case 'f':
1174			checkOp(operation);
1175			operation = bulk;
1176			bulk_file = isc_commandline_argument;
1177			break;
1178		case 's':
1179			checkOp(operation);
1180			operation = bulk;
1181			break;
1182		case 'k':
1183			checkParam(key, "k");
1184			key = isc_commandline_argument;
1185			key_val = strtoul(key, &endp, 10);
1186			if (*endp != '\0' || key_val < 1) {
1187				fprintf(stderr, "Error converting key to integer");
1188			}
1189			break;
1190		case 'z':
1191			checkParam(zone, "z");
1192			zone = isc_commandline_argument;
1193			break;
1194		case 'h':
1195			checkParam(host, "h");
1196			host = isc_commandline_argument;
1197			break;
1198		case 'c':
1199			checkParam(c_zone, "c");
1200			c_zone = isc_commandline_argument;
1201			break;
1202		case 'i':
1203			checkParam(c_ip, "i");
1204			c_ip = isc_commandline_argument;
1205			break;
1206		case 'e':
1207			checkOp(operation);
1208			operation = list;
1209			list_everything = isc_boolean_true;
1210			break;
1211		case '?':
1212			show_usage();
1213			break;
1214		default:
1215			/* should never reach this point */
1216			fprintf(stderr, "unexpected error parsing command arguments\n");
1217			quit(1);
1218			break;
1219		}
1220	}
1221
1222	argc -= isc_commandline_index;
1223	argv += isc_commandline_index;
1224
1225	/* argc & argv have been modified, so now only "extra" parameters are */
1226	/* left in argc & argv.  "Extra" parameters are any parameters that were */
1227	/* not passed using a command line flag.  Exactly 2 args should be left. */
1228	/* The first should be the BDB environment path, the second should be the */
1229	/* BDB database.  The BDB database path can be either relative to the */
1230	/* BDB environment path, or absolute. */
1231	if (argc < 2) {
1232		fprintf(stderr, "Both a Berkeley DB environment and file "\
1233			"must be specified");
1234		quit(2);
1235	} else if (argc > 2) {
1236		fprintf(stderr, "Too many parameters. Check command line for errors.");
1237		quit(2);
1238	}
1239
1240	/* get db_file to operate on */
1241	db_envdir = argv[0];
1242	db_file = argv[1];
1243
1244	if (openBDB() != ISC_R_SUCCESS) {
1245		/* openBDB already prints error messages, don't do it here. */
1246		bdb_cleanup();
1247		quit(3);
1248	}
1249
1250	switch(operation) {
1251	case list:
1252		operation_listOrDelete(isc_boolean_false);
1253		break;
1254	case dele:
1255		operation_listOrDelete(isc_boolean_true);
1256		break;
1257	case add:
1258		operation_add();
1259		break;
1260	case bulk:
1261		operation_bulk();
1262		break;
1263	default:
1264		fprintf(stderr, "\nNo operation was selected. "\
1265			"Select an operation (l d a f)");
1266		quit(2);
1267		break;
1268	}
1269
1270	quit(0);
1271}
1272#endif
1273
1274