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