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