diff options
Diffstat (limited to 'contrib/dlz/bin/dlzbdb/dlzbdb.c')
-rw-r--r-- | contrib/dlz/bin/dlzbdb/dlzbdb.c | 1264 |
1 files changed, 1264 insertions, 0 deletions
diff --git a/contrib/dlz/bin/dlzbdb/dlzbdb.c b/contrib/dlz/bin/dlzbdb/dlzbdb.c new file mode 100644 index 0000000..634d0f9 --- /dev/null +++ b/contrib/dlz/bin/dlzbdb/dlzbdb.c @@ -0,0 +1,1264 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE + * USE OR PERFORMANCE OF THIS SOFTWARE. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE + * USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1999-2001, 2016 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifdef DLZ_BDB + +/* + * exit codes + * 0 everything ok + * 1 error parsing command line + * 2 Missing, too many or invalid combination of command line parameters + * 3 Unable to open BDB database. + * 4 Unable to allocate memory for, or create lexer. + * 5 unable to perform BDB cursor operation + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <isc/buffer.h> +#include <isc/commandline.h> +#include <isc/formatcheck.h> +#include <isc/lex.h> +#include <isc/mem.h> +#include <isc/result.h> +#include <isc/string.h> +#include <isc/util.h> + +#include <db.h> + +/* shut up compiler warnings about no previous prototype */ + +static void +show_usage(void); + +int +getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey); + +int +gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey); + +void +bdb_cleanup(void); + +isc_result_t +bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags); + +void +put_data(bool dns_data, char *input_key, char *input_data); + +void +insert_data(void); + +isc_result_t +openBDB(void); + +isc_result_t +open_lexer(void); + +void +close_lexer(void); + +isc_result_t +bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata); + +void +operation_add(void); + +void +operation_bulk(void); + +void +operation_listOrDelete(bool dlt); + + +/*% + * Maximum length of a single data line that + * may be inserted into database by this program. + * If you need to insert a line of data that is more + * than 10,000 characters change this definition. + */ + +#define max_data_len 10000 + +/*% + * BDB database names. If you want to use different + * database names change them here. + */ + +#define dlz_data "dns_data" +#define dlz_zone "dns_zone" +#define dlz_host "dns_host" +#define dlz_client "dns_client" + + +/*% + * Error code returned by BDB secondary index callback functions. + * This error is returned if the callback function could not create + * the secondary index for any reason. + */ + +#define BDBparseErr 1 + +/* A struct to hold all the relevant info about the database */ + +typedef struct bdb_instance { + DB_ENV *dbenv; /* BDB environment */ + DB *data; /* dns_data database handle */ + DBC *cursor; /* database cursor */ + DBC *cursor2; /* second cursor used during list operation. */ + DBC *cursor3; /* third cursor used during list operation */ + DBC *cursor4; /* fourth cursor used during list operation */ + DB *zone; /* zone database handle */ + DB *host; /* host database handle */ + DB *client; /* client database handle */ +} bdb_instance_t; + +/* Possible operations */ + +#define list 1 /* list data */ +#define dele 2 /* delete data */ +#define add 3 /* add a single piece of data */ +#define bulk 4 /* bulk load data */ + + +/*% + * quit macro is used instead of exit. quit always trys to close the lexer + * and the BDB database before exiting. + */ + +#define quit(i) close_lexer(); bdb_cleanup(); exit(i); + +/*% + * checkOp is used to verify that only one operation (list, del, add, + * bulk from file, bulk from stdin) is specified on the command line. + * This prevents a user from specifying two operations on the command + * line, which would make no sense anyway. + */ + +#define checkOp(x) if (x != 0) {fprintf(stderr, "\nonly one operation "\ + "(l e d a f s) may be specified\n"); quit(2);} + +/*% + * checkParam is used to only allow a parameter to be specified once. + * I.E. the parameter key can only be used on the command line once. + * any attempt to use it twice causes an error. + */ + +#define checkParam(x, y) if (x != NULL) {fprintf(stderr, "\n%s may only "\ + "be specified once\n", y); quit(2);} + +/*% + * checkInvalidParam is used to only allow paramters which make sense for + * the operation selected. I.E. passing the key parameter makes no sense + * for the add operation, and thus it isn't allowed. + */ + +#define checkInvalidParam(x, y, z) if (x != NULL) {fprintf(stderr, "\n%s "\ + "may not be specified %s\n", y, z); quit(2);} + +/*% + * checkInvalidOption is used to only allow paramters which make sense for + * the operation selected - but checks boolean options. + * I.E. passing the "b" bare_list parameter makes no sense for the add + * operation, and thus it isn't allowed. + * if w == x then output error message "flag", "message" + */ + +#define checkInvalidOption(w, x, y, z) if (w == x) {fprintf(stderr, "\n%s "\ + "may not be specified %s\n", y, z); quit(2);} + +/* Global Variables */ + +int operation = 0; /*%< operation to perform. */ +/*% allow new lock files or DB to be created. */ +bool create_allowed = false; +char *key = NULL; /*%< key to use in list & del operations */ + +/*% dump DB in DLZBDB bulk format */ +bool list_everything = false; +unsigned int key_val; /*%< key as unsigned int used in list & del operations */ +char *zone = NULL; /*%< zone to use in list operations */ +char *host = NULL; /*%< host to use in list operations */ +char *c_zone = NULL; /*%< client zone to use in list operations */ +char *c_ip = NULL; /*%< client IP to use in list operations */ +char *a_data = NULL; /*%< data in add operation */ +char *bulk_file = NULL; /*%< bulk data file to load */ +char *db_envdir = NULL; /*%< BDB environment location */ +char *db_file = NULL; /*%< BDB database file location. */ +bdb_instance_t db; /* BDB instance we are operating on */ +isc_lex_t *lexer = NULL; /*%< lexer for use to use in parsing input */ +isc_mem_t *lex_mctx = NULL; /*%< memory context for lexer */ +char lex_data_buf[max_data_len]; /*%< data array to use for lex_buffer below */ +isc_buffer_t lex_buffer; /*%< buffer for lexer during add operation */ + + +/*% + * Displays usage message + */ + +static void +show_usage(void) { + fprintf(stderr, "\n\n\ +---Usage:---------------------------------------------------------------------\ +\n\n\ + List data:\n\ + dlzbdb -l [-k key] [-z zone] [-h host] [-c client_zone] [-i client_ip]\n\ + BDB_environment BDB_database\n\n\ + Delete data:\n\ + dlzbdb -d [-k key] [-c client_zone] [-i client_ip]\n\ + BDB_environment BDB_database\n\n\ + Bulk load data from file:\n\ + dlzbdb -f file_to_load BDB_environment BDB_database\n\n\ + Bulk load data from stdin\n\ + dlzbdb -s BDB_environment BDB_database\n\n\ + Add data:\n\ + dlzbdb -a \"dns data to be added\" BDB_environment BDB_database\n\n\ + Export data:\n\ + dlzbdb -e BDB_environment BDB_database\n\n\ + Normally operations can only be performed on an existing database files.\n\ + Use the -n flag with any operation to allow files to be created.\n\ + Existing files will NOT be truncated by using the -n flag.\n\ + The -n flag will allow a new database to be created, or allow new\n\ + environment files to be created for an existing database.\n\n\ +---Format for -f & -a options:------------------------------------------------\ +\n\n\ +db_type zone host dns_type ttl ip\n\ +db_type zone host dns_type ttl mx_priority mail_host\n\ +db_type zone host dns_type ttl nm_svr resp_psn serial refresh retry expire min\ +\n\ +db_type zone client_ip\n\n\ +---Examples:------------------------------------------------------------------\ +\n\n\ +d mynm.com www A 10 127.0.0.1\n\ +d mynm.com @ MX 10 5 mail\n\ +d mynm.com @ SOA 10 ns1.mynm.com. root.mynm.com. 2 28800 7200 604800 86400\n\ +c mynm.com 127.0.0.1\n\ +c mynm.com 192.168.0.10\n\ +"); +quit(1); +} + + +/*% BDB callback to create zone secondary index */ + +int +getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) { + char *tmp; + char *left; + char *right; + int result=0; + + UNUSED(dbp); + UNUSED(pkey); + + /* Allocate memory to use in parsing the string */ + tmp = right = malloc(pdata->size + 1); + + /* verify memory was allocated */ + if (right == NULL) { + result = BDBparseErr; + goto getzone_cleanup; + } + + /* copy data string into newly allocated memory */ + strncpy(right, pdata->data, pdata->size); + right[pdata->size] = '\0'; + + /* split string at the first space */ + left = isc_string_separate(&right, " "); + + /* copy string for "zone" secondary index */ + skey->data = strdup(left); + if (skey->data == NULL) { + result = BDBparseErr; + goto getzone_cleanup; + } + /* set required values for BDB */ + skey->size = strlen(skey->data); + skey->flags = DB_DBT_APPMALLOC; + + getzone_cleanup: + + /* cleanup memory */ + if (tmp != NULL) + free(tmp); + + return result; +} + +/*% + * BDB callback to create host secondary index + */ + +int +gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) { + char *tmp; + char *left; + char *right; + int result=0; + + UNUSED(dbp); + UNUSED(pkey); + + /* allocate memory to use in parsing the string */ + tmp = right = malloc(pdata->size + 1); + + /* verify memory was allocated */ + if (tmp == NULL) { + result = BDBparseErr; + goto gethost_cleanup; + } + + /* copy data string into newly allocated memory */ + strncpy(right, pdata->data, pdata->size); + right[pdata->size] = '\0'; + + /* we don't care about left string. */ + /* memory of left string will be freed when tmp is freed. */ + isc_string_separate(&right, " "); + + /* verify right still has some characters left */ + if (right == NULL) { + result = BDBparseErr; + goto gethost_cleanup; + } + + /* get "host" from data string */ + left = isc_string_separate(&right, " "); + /* copy string for "host" secondary index */ + skey->data = strdup(left); + if (skey->data == NULL) { + result = BDBparseErr; + goto gethost_cleanup; + } + /* set required values for BDB */ + skey->size = strlen(skey->data); + skey->flags = DB_DBT_APPMALLOC; + + gethost_cleanup: + + /* cleanup memory */ + if (tmp != NULL) + free(tmp); + + return result; +} + +/*% + * Performs BDB cleanup. Close each database that we opened. + * Close environment. Set each var to NULL so we know they + * were closed and don't accidentally try to close them twice. + */ + +void +bdb_cleanup(void) { + + /* close cursors */ + if (db.cursor4 != NULL) { + db.cursor4->c_close(db.cursor4); + db.cursor4 = NULL; + } + + if (db.cursor3 != NULL) { + db.cursor3->c_close(db.cursor3); + db.cursor3 = NULL; + } + + if (db.cursor2 != NULL) { + db.cursor2->c_close(db.cursor2); + db.cursor2 = NULL; + } + + if (db.cursor != NULL) { + db.cursor->c_close(db.cursor); + db.cursor = NULL; + } + + /* close databases */ + if (db.data != NULL) { + db.data->close(db.data, 0); + db.data = NULL; + } + if (db.host != NULL) { + db.host->close(db.host, 0); + db.host = NULL; + } + if (db.zone != NULL) { + db.zone->close(db.zone, 0); + db.zone = NULL; + } + if (db.client != NULL) { + db.client->close(db.client, 0); + db.client = NULL; + } + + /* close environment */ + if (db.dbenv != NULL) { + db.dbenv->close(db.dbenv, 0); + db.dbenv = NULL; + } +} + +/*% Initializes, sets flags and then opens Berkeley databases. */ + +isc_result_t +bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags) { + + int result; + int createFlag = 0; + + /* Initialize the database. */ + if ((result = db_create(db_out, db.dbenv, 0)) != 0) { + fprintf(stderr, "BDB could not initialize %s database. BDB error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* set database flags. */ + if ((result = (*db_out)->set_flags(*db_out, flags)) != 0) { + fprintf(stderr, "BDB could not set flags for %s database. BDB error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + if (create_allowed == true) { + createFlag = DB_CREATE; + } + /* open the database. */ + if ((result = (*db_out)->open(*db_out, NULL, db_file, db_name, db_type, + createFlag, 0)) != 0) { + fprintf(stderr, "BDB could not open %s database in %s. BDB error: %s", + db_name, db_file, db_strerror(result)); + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +/*% + * parses input and adds it to the BDB database + * Lexer should be instantiated, and either a file or buffer opened for it. + * The insert_data function is used by both the add, and bulk insert + * operations + */ + +void +put_data(bool dns_data, char *input_key, char *input_data) { + + int bdbres; + DBT key, data; + + /* make sure key & data are completely empty */ + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + /* if client data, setup key for insertion */ + if (!dns_data && input_key != NULL) { + key.data = input_key; + key.size = strlen(input_key); + key.flags = 0; + } + /* always setup data for insertion */ + data.data = input_data; + data.size = strlen(input_data); + data.flags = 0; + + /* execute insert against appropriate database. */ + if (dns_data) { + bdbres = db.data->put(db.data, NULL, &key, &data, DB_APPEND); + } else { + bdbres = db.client->put(db.client, NULL, &key, &data, 0); + } + + /* if something went wrong, log error and quit */ + if (bdbres != 0) { + fprintf(stderr, "BDB could not insert data. Error: %s", + db_strerror(bdbres)); + quit(5); + } +} + +void +insert_data(void) { + unsigned int opt = + ISC_LEXOPT_EOL | /* Want end-of-line token. */ + ISC_LEXOPT_EOF | /* Want end-of-file token. */ + ISC_LEXOPT_QSTRING | /* Recognize qstrings. */ + ISC_LEXOPT_QSTRINGMULTILINE; /* Allow multiline "" strings */ + + isc_result_t result; + isc_token_t token; /* token from lexer */ + bool loop = true; + bool have_czone = false; + char data_arr[max_data_len]; + isc_buffer_t buf; + char data_arr2[max_data_len]; + isc_buffer_t buf2; + char data_type = 'u'; /* u =unknown, b =bad token, d/D =DNS, c/C =client IP */ + + /* Initialize buffers */ + isc_buffer_init(&buf, &data_arr, max_data_len); + isc_buffer_init(&buf2, &data_arr2, max_data_len); + + while (loop) { + result = isc_lex_gettoken(lexer, opt, &token); + if (result != ISC_R_SUCCESS) + goto data_cleanup; + + switch(token.type) { + case isc_tokentype_string: + if (data_type == 'u') { + /* store data_type */ + strncpy(&data_type, token.value.as_pointer, 1); + /* verify data_type was specified correctly on input */ + if (strlen(token.value.as_pointer) > 1 || ( + data_type != 'd' && data_type != 'D' && + data_type != 'c' && data_type != 'C') ) { + /* if not, set to 'b' so this line is ignored. */ + data_type = 'b'; + } + } else if (data_type == 'c' || data_type == 'C') { + if (have_czone == true) { + isc_buffer_putstr(&buf2, token.value.as_pointer); + /* add string terminator to buffer */ + isc_buffer_putmem(&buf2, "\0", 1); + } else { + isc_buffer_putstr(&buf, token.value.as_pointer); + /* add string terminator to buffer */ + isc_buffer_putmem(&buf, "\0", 1); + have_czone = true; + } + } else { + isc_buffer_putstr(&buf, token.value.as_pointer); + isc_buffer_putstr(&buf, " "); + } + break; + case isc_tokentype_qstring: + isc_buffer_putstr(&buf, "\""); + isc_buffer_putstr(&buf, token.value.as_pointer); + isc_buffer_putstr(&buf, "\" "); + break; + case isc_tokentype_eol: + case isc_tokentype_eof: + + if ((data_type != 'u' && isc_buffer_usedlength(&buf) > 0) || data_type == 'b') { + /* perform insert operation */ + if (data_type == 'd' || data_type == 'D') { + /* add string terminator to buffer */ + isc_buffer_putmem(&buf, "\0", 1); + put_data(true, NULL, (char *) &data_arr); + } else if (data_type == 'c' || data_type == 'C') { + put_data(false, (char *) &data_arr, + (char *) &data_arr2); + } else if (data_type == 'b') { + fprintf(stderr, "Bad / unknown token encountered on line %lu."\ + " Skipping line.", isc_lex_getsourceline(lexer) - 1); + } else { + fprintf(stderr, "Bad / unknown db data type encountered on " \ + "line %lu. Skipping line\n", isc_lex_getsourceline(lexer) - 1); + } + } + + if (token.type == isc_tokentype_eof) { + loop = false; + } + + /* reset buffer for next insert */ + isc_buffer_clear(&buf); + isc_buffer_clear(&buf2); + have_czone = false; + data_type ='u'; + break; + default: + data_type = 'b'; + break; + } + } + + return; + + data_cleanup: + /* let user know we had problems */ + fprintf(stderr, "Unknown error processing tokens during \"add\" or " \ + "\"bulk\" operation.\nStoped processing on line %lu.", + isc_lex_getsourceline(lexer)); +} + + +isc_result_t +openBDB(void) { + + int bdbres; + isc_result_t result; + + /* create BDB environment */ + /* Basically BDB allocates and assigns memory to db->dbenv */ + bdbres = db_env_create(&db.dbenv, 0); + if (bdbres != 0) { + fprintf(stderr, "BDB environment could not be created. BDB error: %s", + db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto openBDB_cleanup; + } + + /* open BDB environment */ + if (create_allowed == true) { + /* allowed to create new files */ + bdbres = db.dbenv->open(db.dbenv, db_envdir, + DB_INIT_CDB | DB_INIT_MPOOL | DB_CREATE, 0); + } else { /* not allowed to create new files. */ + bdbres = db.dbenv->open(db.dbenv, db_envdir, + DB_INIT_CDB | DB_INIT_MPOOL, 0); + } + if (bdbres != 0) { + fprintf(stderr, "BDB environment at '%s' could not be opened. BDB " \ + "error: %s", db_envdir, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto openBDB_cleanup; + } + + /* open dlz_data database. */ + + result = bdb_opendb(DB_RECNO, &db.data, dlz_data, 0); + if (result != ISC_R_SUCCESS) + goto openBDB_cleanup; + + /* open dlz_host database */ + result = bdb_opendb(DB_BTREE, &db.host, dlz_host, DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto openBDB_cleanup; + + /* open dlz_zone database. */ + result = bdb_opendb(DB_BTREE, &db.zone, dlz_zone, DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto openBDB_cleanup; + + /* open dlz_client database. */ + result = bdb_opendb(DB_BTREE, &db.client, dlz_client, DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto openBDB_cleanup; + + /* associate the host secondary database with the primary database */ + bdbres = db.data->associate(db.data, NULL, db.host, gethost, 0); + if (bdbres != 0) { + fprintf(stderr, "BDB could not associate %s database with %s. BDB "\ + "error: %s", dlz_host, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto openBDB_cleanup; + } + + /* associate the zone secondary database with the primary database */ + bdbres = db.data->associate(db.data, NULL, db.zone, getzone, 0); + if (bdbres != 0) { + fprintf(stderr, "BDB could not associate %s database with %s. BDB "\ + "error: %s", dlz_zone, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto openBDB_cleanup; + } + + return result; + + openBDB_cleanup: + + bdb_cleanup(); + return result; +} + +/*% Create & open lexer to parse input data */ + +isc_result_t +open_lexer(void) { + isc_result_t result; + + /* check if we already opened the lexer, if we did, return success */ + if (lexer != NULL) + return ISC_R_SUCCESS; + + /* allocate memory for lexer, and verify it was allocated */ + result = isc_mem_create(0, 0, &lex_mctx); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "unexpected error creating lexer\n"); + return result; + } + + /* create lexer */ + result = isc_lex_create(lex_mctx, 1500, &lexer); + if (result != ISC_R_SUCCESS) + fprintf(stderr, "unexpected error creating lexer\n"); + + /* set allowed commenting style */ + isc_lex_setcomments(lexer, ISC_LEXCOMMENT_C | /* Allow C comments */ + ISC_LEXCOMMENT_CPLUSPLUS | /* Allow C++ comments */ + ISC_LEXCOMMENT_SHELL); /* Allow shellcomments */ + + isc_buffer_init(&lex_buffer, &lex_data_buf, max_data_len); + + return result; +} + +/*% Close the lexer, and cleanup memory */ + +void +close_lexer(void) { + + /* If lexer is still open, close it & destroy it. */ + if (lexer != NULL) { + isc_lex_close(lexer); + isc_lex_destroy(&lexer); + } + + /* if lexer memory is still allocated, destroy it. */ + if (lex_mctx != NULL) + isc_mem_destroy(&lex_mctx); +} + +/*% Perform add operation */ + +void +operation_add(void) { + /* check for any parameters that are not allowed during add */ + checkInvalidParam(key, "k", "for add operation"); + checkInvalidParam(zone, "z", "for add operation"); + checkInvalidParam(host, "h", "for add operation"); + checkInvalidParam(c_zone, "c", "for add operation"); + checkInvalidParam(c_ip, "i", "for add operation"); + checkInvalidOption(list_everything, true, "e", + "for add operation"); + + /* if open lexer fails it alread prints error messages. */ + if (open_lexer() != ISC_R_SUCCESS) { + quit(4); + } + + /* copy input data to buffer */ + isc_buffer_putstr(&lex_buffer, a_data); + + /* tell lexer to use buffer as input */ + if (isc_lex_openbuffer(lexer, &lex_buffer) != ISC_R_SUCCESS) { + fprintf(stderr, "unexpected error opening lexer buffer"); + quit(4); + } + + /*common logic for "add" & "bulk" operations are handled by insert_data */ + insert_data(); +} + +/*% Perform bulk insert operation */ + +void +operation_bulk(void) { + /* check for any parameters that are not allowed during bulk */ + checkInvalidParam(key, "k", "for bulk load operation"); + checkInvalidParam(zone, "z", "for bulk load operation"); + checkInvalidParam(host, "h", "for bulk load operation"); + checkInvalidParam(c_zone, "c", "for bulk load operation"); + checkInvalidParam(c_ip, "i", "for bulk load operation"); + checkInvalidOption(list_everything, true, "e", + "for bulk load operation"); + + /* if open lexer fails it already prints error messages. */ + if (open_lexer() != ISC_R_SUCCESS) { + quit(4); + } + + if (bulk_file == NULL) { + if (isc_lex_openstream(lexer, stdin) != ISC_R_SUCCESS) { + fprintf(stderr, "unexpected error opening stdin by lexer."); + quit(4); + } + } else if (isc_lex_openfile(lexer, bulk_file) != ISC_R_SUCCESS) { + fprintf(stderr, "unexpected error opening %s by lexer.", bulk_file); + quit(4); + } + + /* common logic for "add" & "bulk" operations are handled by insert_data */ + insert_data(); +} + +isc_result_t +bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata) { + + int bdbres; + db_recno_t recNum; + char *retkey = NULL, *retdata; + size_t retklen = 0, retdlen; + void *p; + + /* use a 5MB buffer for the bulk dump */ + int buffer_size = 5 * 1024 * 1024; + + /* try to allocate a 5 MB buffer, if we fail write err msg, die. */ + bdbdata->data = malloc(buffer_size); + if (bdbdata->data == NULL) { + fprintf(stderr, + "Unable to allocate 5 MB buffer for bulk database dump\n"); + return ISC_R_FAILURE; + } + bdbdata->ulen = buffer_size; + bdbdata->flags = DB_DBT_USERMEM; + + /* get a cursor, make sure it worked. */ + bdbres = database->cursor(database, NULL, &dbcursor, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n",db_strerror(bdbres)); + free(bdbdata->data); + return ISC_R_FAILURE; + } + + /* loop and dump all data */ + for (;;) { + + /* loop through data until DB_NOTFOUND is returned */ + bdbres = dbcursor->c_get(dbcursor, bdbkey, bdbdata, + DB_MULTIPLE_KEY | DB_NEXT); + /* if not successful did we encounter DB_NOTFOUND, or */ + /* have a different problem. */ + if (bdbres != 0) { + if (bdbres != DB_NOTFOUND) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + free(bdbdata->data); + return ISC_R_FAILURE; + } + /* Hit DB_NOTFOUND which means end of data. */ + break; + } /* end of if (bdbres !=0) */ + + for (DB_MULTIPLE_INIT(p, bdbdata);;) { + if (type == 'c') + DB_MULTIPLE_KEY_NEXT(p, bdbdata, retkey, retklen, retdata, retdlen); + else + DB_MULTIPLE_RECNO_NEXT(p, bdbdata, recNum, retdata, retdlen); + + if (p == NULL) + break; + if (type == 'c') + printf("c %.*s %.*s\n",(int)retklen, retkey,(int)retdlen, retdata); + else + printf("d %.*s\n", (int)retdlen, retdata); + } /* end of for (DB_MULTIPLE_INIT....) */ + + } /* end of for (;;) */ + + /* free the buffer we created earlier */ + free(bdbdata->data); + + return ISC_R_SUCCESS; +} + +/*% + * Perform listOrDelete operation + * if dlt == true, delete data + * else list data + */ + +void +operation_listOrDelete(bool dlt) { + + int bdbres = 0; + DBC *curList[3]; + DBT bdbkey, bdbdata; + db_recno_t recno; + int curIndex = 0; + + + /* verify that only allowed parameters were passed. */ + if (dlt == true) { + checkInvalidParam(zone, "z", "for delete operation"); + checkInvalidParam(host, "h", "for delete operation"); + checkInvalidOption(list_everything, true, "e", + "for delete operation"); + checkInvalidOption(create_allowed, true, "n", + "for delete operation"); + } else if (key != NULL || zone != NULL || host != NULL) { + checkInvalidParam(c_zone, "c", "for list when k, z or h are specified"); + checkInvalidParam(c_ip, "i", "for list when k, z, or h are specified"); + checkInvalidOption(list_everything, true, "e", + "for list when k, z, or h are specified"); + checkInvalidOption(create_allowed, true, "n", + "for list operation"); + } else if (c_ip != NULL || c_zone != NULL) { + checkInvalidOption(list_everything, true, "e", + "for list when c or i are specified"); + checkInvalidOption(create_allowed, true, "n", + "for list operation"); + } + + memset(&bdbkey, 0, sizeof(bdbkey)); + memset(&bdbdata, 0, sizeof(bdbdata)); + + /* Dump database in "dlzbdb" bulk format */ + if (list_everything == true) { + if (bulk_write('c', db.client, db.cursor, &bdbkey, &bdbdata) + != ISC_R_SUCCESS) + return; + memset(&bdbkey, 0, sizeof(bdbkey)); + memset(&bdbdata, 0, sizeof(bdbdata)); + bulk_write('d', db.data, db.cursor2, &bdbkey, &bdbdata); + return; + } /* end if (list_everything) */ + + /* set NULL the 2nd and 3rd positions in curList. */ + /* that way later when add cursors to the join list */ + /* it is already null terminated. */ + curList[1] = curList[2] = NULL; + + if (key != NULL) { + /* make sure other parameters weren't */ + checkInvalidParam(zone, "z", "when k is specified"); + checkInvalidParam(host, "h", "when k is specified"); + + recno = key_val; + bdbkey.data = &recno; + bdbkey.size = sizeof(recno); + + if (dlt == true) { + bdbres = db.data->del(db.data, NULL, &bdbkey, 0); + } else { + bdbdata.flags = DB_DBT_REALLOC; + bdbres = db.data->get(db.data, NULL, &bdbkey, &bdbdata, 0); + + if (bdbres == 0) { + printf("KEY | DATA\n"); + printf("%lu | %.*s\n", *(u_long *) bdbkey.data, + (int)bdbdata.size, (char *)bdbdata.data); + } + } /* closes else of if (dlt == true) */ + if (bdbres == DB_NOTFOUND) { + printf("Key not found in database"); + } + } /* closes if (key != NULL) */ + + /* if zone is passed */ + if (zone != NULL) { + /* create a cursor and make sure it worked */ + bdbres = db.zone->cursor(db.zone, NULL, &db.cursor2, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + return; + } + + bdbkey.data = zone; + bdbkey.size = strlen(zone); + bdbres = db.cursor2->c_get(db.cursor2, &bdbkey, &bdbdata, DB_SET); + if (bdbres != 0) { + if (bdbres != DB_NOTFOUND) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + } else { + printf("Zone not found in database"); + } + return; + } + + /* add cursor to cursor list for later use in join */ + curList[curIndex++] = db.cursor2; + } + + /* if host is passed */ + if (host != NULL) { + + /* create a cursor and make sure it worked. */ + bdbres = db.host->cursor(db.host, NULL, &db.cursor3, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + return; + } + bdbkey.data = host; + bdbkey.size = strlen(host); + bdbres = db.cursor3->c_get(db.cursor3, &bdbkey, &bdbdata, DB_SET); + if (bdbres != 0) { + if (bdbres != DB_NOTFOUND) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + } else { + printf("Host not found in database"); + } + return; + } + + /* add cursor to cursor list for later use in join */ + curList[curIndex++] = db.cursor3; + } + + + if (zone != NULL || host != NULL) { + + /* join any cursors */ + bdbres = db.data->join(db.data, curList, &db.cursor4, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + return; + } + + memset(&bdbkey, 0, sizeof(bdbkey)); + bdbkey.flags = DB_DBT_REALLOC; + memset(&bdbdata, 0, sizeof(bdbdata)); + bdbdata.flags = DB_DBT_REALLOC; + + /* print a header to explain the output */ + printf("KEY | DATA\n"); + /* loop and list all results. */ + while (bdbres == 0) { + /* get data */ + bdbres = db.cursor4->c_get(db.cursor4, &bdbkey, &bdbdata, 0); + /* verify call had no errors */ + if (bdbres != 0) { + break; + } + printf("%lu | %.*s\n", *(u_long *) bdbkey.data, + (int)bdbdata.size, (char *)bdbdata.data); + } /* closes while loop */ + } + + if (c_ip != NULL && c_zone == NULL) { + fprintf(stderr, "i may only be specified when c is also specified\n"); + quit(2); + } + /* if client_zone was passed */ + if (c_zone != NULL) { + + /* create a cursor and make sure it worked. */ + if (dlt == true) { + /* open read-write cursor */ + bdbres = db.client->cursor(db.client, NULL, &db.cursor, + DB_WRITECURSOR); + } else { + /* open read only cursor */ + bdbres = db.client->cursor(db.client, NULL, &db.cursor, 0); + /* print a header to explain the output */ + printf("CLIENT_ZONE | CLIENT_IP\n"); + } + + bdbkey.data = c_zone; + bdbkey.size = strlen(c_zone); + + if (c_ip != NULL) { + bdbdata.data = c_ip; + bdbdata.size = strlen(c_ip); + bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_GET_BOTH); + if (bdbres == DB_NOTFOUND) { + printf("Client zone & IP not found in database"); + } + } else { + bdbdata.flags = DB_DBT_REALLOC; + bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_SET); + if (bdbres == DB_NOTFOUND) { + printf("Client zone not found in database"); + } + } + + while (bdbres == 0) { + if (dlt == false) { + printf("%.*s | %.*s\n", (int)bdbkey.size, (char *) bdbkey.data, + (int)bdbdata.size, (char *) bdbdata.data); + } else { + /* delete record. */ + bdbres = db.cursor->c_del(db.cursor, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + break; + } + } + if (c_ip != NULL) { + break; + } + bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_NEXT_DUP); + if (bdbres != 0) { + break; + } + } /* end while loop */ + } + + + if (bdbres != 0 && bdbres != DB_NOTFOUND) { + fprintf(stderr, "Unexpected error during list operation " \ + "BDB error: %s", db_strerror(bdbres)); + } + + if (bdbkey.flags == DB_DBT_REALLOC && bdbkey.data != NULL) { + free(bdbkey.data); + } + if (bdbdata.flags == DB_DBT_REALLOC && bdbdata.data != NULL) { + free(bdbdata.data); + } +} + + +int +main(int argc, char **argv) { + + int ch; + char *endp; + + /* there has to be at least 2 args, some operations require more */ + if (argc < 2) + show_usage(); + + /* use the ISC commandline parser to get all the program arguments */ + while ((ch= isc_commandline_parse(argc, argv, "ldesna:f:k:z:h:c:i:")) != -1) { + switch (ch) { + case 'n': + create_allowed = true; + break; + case 'l': + checkOp(operation); + operation = list; + break; + case 'd': + checkOp(operation); + operation = dele; + break; + case 'a': + checkOp(operation); + operation = add; + a_data = isc_commandline_argument; + break; + case 'f': + checkOp(operation); + operation = bulk; + bulk_file = isc_commandline_argument; + break; + case 's': + checkOp(operation); + operation = bulk; + break; + case 'k': + checkParam(key, "k"); + key = isc_commandline_argument; + key_val = strtoul(key, &endp, 10); + if (*endp != '\0' || key_val < 1) { + fprintf(stderr, "Error converting key to integer"); + } + break; + case 'z': + checkParam(zone, "z"); + zone = isc_commandline_argument; + break; + case 'h': + checkParam(host, "h"); + host = isc_commandline_argument; + break; + case 'c': + checkParam(c_zone, "c"); + c_zone = isc_commandline_argument; + break; + case 'i': + checkParam(c_ip, "i"); + c_ip = isc_commandline_argument; + break; + case 'e': + checkOp(operation); + operation = list; + list_everything = true; + break; + case '?': + show_usage(); + break; + default: + /* should never reach this point */ + fprintf(stderr, "unexpected error parsing command arguments\n"); + quit(1); + break; + } + } + + argc -= isc_commandline_index; + argv += isc_commandline_index; + + /* argc & argv have been modified, so now only "extra" parameters are */ + /* left in argc & argv. "Extra" parameters are any parameters that were */ + /* not passed using a command line flag. Exactly 2 args should be left. */ + /* The first should be the BDB environment path, the second should be the */ + /* BDB database. The BDB database path can be either relative to the */ + /* BDB environment path, or absolute. */ + if (argc < 2) { + fprintf(stderr, "Both a Berkeley DB environment and file "\ + "must be specified"); + quit(2); + } else if (argc > 2) { + fprintf(stderr, "Too many parameters. Check command line for errors."); + quit(2); + } + + /* get db_file to operate on */ + db_envdir = argv[0]; + db_file = argv[1]; + + if (openBDB() != ISC_R_SUCCESS) { + /* openBDB already prints error messages, don't do it here. */ + bdb_cleanup(); + quit(3); + } + + switch(operation) { + case list: + operation_listOrDelete(false); + break; + case dele: + operation_listOrDelete(true); + break; + case add: + operation_add(); + break; + case bulk: + operation_bulk(); + break; + default: + fprintf(stderr, "\nNo operation was selected. "\ + "Select an operation (l d a f)"); + quit(2); + break; + } + + quit(0); +} +#endif + |