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