diff -Nuar bind-9.3.2-orig/DLZ-CHANGELOG bind-9.3.2-mod/DLZ-CHANGELOG --- bind-9.3.2-orig/DLZ-CHANGELOG 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/DLZ-CHANGELOG 2006-02-20 19:44:14.000000000 +0100 @@ -0,0 +1,15 @@ + +ctrix-dlz-9.3.2-1 + - patch by Grzegorz Piotr Jaskiewicz + makes sure bind won't crash if one of percent + params is not delivered by engine. + - path by misterbawb at gmail:com + limited amount of wildcards in subdomains + speedup + + fix theoric denial of service + - patch by Benjamin GIGON + added two tags to LDAP config: %zone_tld% and %zone_domain%. + %zone_tld% contains the TLD into a request (com, net, for example) + %zone_domain%. contains ... the domain :-) without tld, of course + Some config separate tld and domain in database. + Using Concat on multiple tables can provoke a slowdown. + diff -Nuar bind-9.3.2-orig/bin/Makefile.in bind-9.3.2-mod/bin/Makefile.in --- bind-9.3.2-orig/bin/Makefile.in 2004-03-06 11:21:10.000000000 +0100 +++ bind-9.3.2-mod/bin/Makefile.in 2006-02-20 19:13:47.000000000 +0100 @@ -19,7 +19,7 @@ VPATH = @srcdir@ top_srcdir = @top_srcdir@ -SUBDIRS = named rndc dig dnssec tests nsupdate check +SUBDIRS = @DLZ_BDB_UTIL@ named rndc dig dnssec tests nsupdate check TARGETS = @BIND9_MAKE_RULES@ diff -Nuar bind-9.3.2-orig/bin/dlzbdb/Makefile.in bind-9.3.2-mod/bin/dlzbdb/Makefile.in --- bind-9.3.2-orig/bin/dlzbdb/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/dlzbdb/Makefile.in 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,73 @@ +# Copyright (C) 1998-2001 Internet Software Consortium. +# +# 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 INTERNET SOFTWARE CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SOFTWARE CONSORTIUM 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. + +# $Id: Makefile.in,v 1.74 2001/06/01 00:45:00 bwelling Exp $ + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +@BIND9_VERSION@ + +# @BIND9_INCLUDES@ + +DLZINCLUDES = @DLZ_BDB_INC@ + +CINCLUDES = -I${srcdir}/include -I${srcdir}/unix/include \ + ${ISC_INCLUDES} ${DLZINCLUDES} + +CDEFINES = @USE_DLZ_BDB@ +CWARNINGS = + +DLZLIBS = @DLZ_BDB_LIBS@ +ISCLIBS = ../../lib/isc/libisc.@A@ + +DEPLIBS = ${ISCDEPLIBS} + +LIBS = ${ISCLIBS} ${DLZLIBS} @LIBS@ + +TARGETS = dlzbdb + +SRCS = dlzbdb.c + +#MANPAGES = + +#HTMLPAGES = + +#MANOBJS = ${MANPAGES} ${HTMLPAGES} + +@BIND9_MAKE_RULES@ + +dlzbdb.@O@: dlzbdb.c + ${LIBTOOL} ${CC} ${ALL_CFLAGS} -DVERSION=\"${VERSION}\" \ + -c ${srcdir}/dlzbdb.c + +dlzbdb: dlzbdb.@O@ ${DEPLIBS} + ${LIBTOOL} ${PURIFY} ${CC} ${CFLAGS} -o $@ dlzbdb.@O@ ${LIBS} + +doc man:: ${MANOBJS} + +#docclean manclean maintainer-clean:: +# rm -f ${MANOBJS} + +clean distclean maintainer-clean:: + rm -f ${TARGETS} + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${sbindir} +# $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man8 + +install:: dlzbdb installdirs + ${LIBTOOL} ${INSTALL_PROGRAM} dlzbdb ${DESTDIR}${sbindir} \ No newline at end of file diff -Nuar bind-9.3.2-orig/bin/dlzbdb/dlzbdb.c bind-9.3.2-mod/bin/dlzbdb/dlzbdb.c --- bind-9.3.2-orig/bin/dlzbdb/dlzbdb.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/dlzbdb/dlzbdb.c 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,1255 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 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(isc_boolean_t 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(isc_boolean_t 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. +isc_boolean_t create_allowed = isc_boolean_false; +char *key = NULL; // key to use in list & del operations + +// dump DB in DLZBDB bulk format +isc_boolean_t list_everything = isc_boolean_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 == isc_boolean_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(isc_boolean_t 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 + isc_boolean_t loop = isc_boolean_true; + isc_boolean_t have_czone = isc_boolean_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 == isc_boolean_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 = isc_boolean_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(isc_boolean_true, NULL, (char *) &data_arr); + } else if (data_type == 'c' || data_type == 'C'){ + put_data(isc_boolean_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 = isc_boolean_false; + } + + // reset buffer for next insert + isc_buffer_clear(&buf); + isc_buffer_clear(&buf2); + have_czone = isc_boolean_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 == isc_boolean_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, isc_boolean_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, isc_boolean_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, *retdata; + size_t retklen, 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(isc_boolean_t 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 == isc_boolean_true){ + checkInvalidParam(zone, "z", "for delete operation"); + checkInvalidParam(host, "h", "for delete operation"); + checkInvalidOption(list_everything, isc_boolean_true, "e", + "for delete operation"); + checkInvalidOption(create_allowed, isc_boolean_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, isc_boolean_true, "e", + "for list when k, z, or h are specified"); + checkInvalidOption(create_allowed, isc_boolean_true, "n", + "for list operation"); + } else if(c_ip != NULL || c_zone != NULL){ + checkInvalidOption(list_everything, isc_boolean_true, "e", + "for list when c or i are specified"); + checkInvalidOption(create_allowed, isc_boolean_true, "n", + "for list operation"); + } + + memset(&bdbkey, 0, sizeof(bdbkey)); + memset(&bdbdata, 0, sizeof(bdbdata)); + + // Dump database in "dlzbdb" bulk format + if(list_everything == isc_boolean_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 == isc_boolean_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 == isc_boolean_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 == isc_boolean_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 == isc_boolean_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 = isc_boolean_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 = isc_boolean_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(isc_boolean_false); + break; + case dele: + operation_listOrDelete(isc_boolean_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 + diff -Nuar bind-9.3.2-orig/bin/named/Makefile.in bind-9.3.2-mod/bin/named/Makefile.in --- bind-9.3.2-orig/bin/named/Makefile.in 2004-09-06 23:47:25.000000000 +0200 +++ bind-9.3.2-mod/bin/named/Makefile.in 2006-02-20 19:13:47.000000000 +0100 @@ -31,14 +31,25 @@ DBDRIVER_INCLUDES = DBDRIVER_LIBS = +DLZINCLUDES = @DLZ_BDB_INC@ @DLZ_POSTGRES_INC@ @DLZ_ODBC_INC@ \ + @DLZ_MYSQL_INC@ @DLZ_LDAP_INC@ + + + CINCLUDES = -I${srcdir}/include -I${srcdir}/unix/include \ ${LWRES_INCLUDES} ${DNS_INCLUDES} ${BIND9_INCLUDES} \ ${ISCCFG_INCLUDES} ${ISCCC_INCLUDES} ${ISC_INCLUDES} \ - ${DBDRIVER_INCLUDES} + ${DLZINCLUDES} ${DBDRIVER_INCLUDES} + +CDEFINES = @USE_DLZ_POSTGRES@ @USE_DLZ_MYSQL@ @USE_DLZ_FILESYSTEM@ \ + @USE_DLZ_BDB@ @USE_DLZ_ODBC@ @USE_DLZ_LDAP@ @USE_DLZ_STUB@ \ + @USE_DLZ@ -CDEFINES = CWARNINGS = +DLZLIBS = @DLZ_BDB_LIBS@ @DLZ_POSTGRES_LIBS@ @DLZ_ODBC_LIBS@ \ + @DLZ_MYSQL_LIBS@ @DLZ_LDAP_LIBS@ + DNSLIBS = ../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@ ISCCFGLIBS = ../../lib/isccfg/libisccfg.@A@ ISCCCLIBS = ../../lib/isccc/libisccc.@A@ @@ -57,7 +68,8 @@ ${ISCCFGDEPLIBS} ${ISCCCDEPLIBS} ${ISCDEPLIBS} LIBS = ${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} \ - ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCLIBS} ${DBDRIVER_LIBS} @LIBS@ + ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCLIBS} ${DLZLIBS} \ + ${DBDRIVER_LIBS} @LIBS@ SUBDIRS = unix @@ -71,8 +83,15 @@ zoneconf.@O@ \ lwaddr.@O@ lwresd.@O@ lwdclient.@O@ lwderror.@O@ lwdgabn.@O@ \ lwdgnba.@O@ lwdgrbn.@O@ lwdnoop.@O@ lwsearch.@O@ \ + dlz_postgres_driver.@O@ \ + dlz_stub_driver.@O@ sdlz_helper.@O@ dlz_filesystem_driver.@O@ \ + dlz_mysql_driver.@O@ dlz_odbc_driver.@O@ dlz_bdb_driver.@O@ \ + dlz_bdbhpt_driver.@O@ dlz_ldap_driver.@O@ \ $(DBDRIVER_OBJS) + + + UOBJS = unix/os.@O@ SRCS = aclconf.c builtin.c client.c config.c control.c \ @@ -83,7 +102,9 @@ zoneconf.c \ lwaddr.c lwresd.c lwdclient.c lwderror.c lwdgabn.c \ lwdgnba.c lwdgrbn.c lwdnoop.c lwsearch.c \ - $(DBDRIVER_SRCS) + dlz_postgres_driver.c dlz_stub_driver.c sdlz_helper.c \ + dlz_filesystem_driver.c dlz_mysql_driver.c dlz_odbc_driver.c \ + dlz_bdb_driver.c dlz_bdbhpt_driver.c dlz_ldap_driver.c $(DBDRIVER_SRCS) MANPAGES = named.8 lwresd.8 named.conf.5 diff -Nuar bind-9.3.2-orig/bin/named/dlz_bdb_driver.c bind-9.3.2-mod/bin/named/dlz_bdb_driver.c --- bind-9.3.2-orig/bin/named/dlz_bdb_driver.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/dlz_bdb_driver.c 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,799 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ_BDB + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +dns_sdlzimplementation_t *dlz_bdb = NULL; + +// should the bdb driver use threads. +#ifdef ISC_PLATFORM_USETHREADS +#define bdb_threads DB_THREAD +#else +#define bdb_threads 0 +#endif + +// BDB database names +#define dlz_data "dns_data" +#define dlz_zone "dns_zone" +#define dlz_host "dns_host" +#define dlz_client "dns_client" + + /* This structure contains all the Berkeley DB handles + * for this instance of the BDB driver. + */ + +typedef struct bdb_instance { + DB_ENV *dbenv; // BDB environment + DB *data; // dns_data database handle + DB *zone; // zone database handle + DB *host; // host database handle + DB *client; // client database handle + isc_mem_t *mctx; // memory context + +}bdb_instance_t; + +typedef struct parsed_data { + char *zone; + char *host; + char *type; + int ttl; + char *data; +}parsed_data_t; + + + /* shut up compiler warnings about no previous prototype */ + +void +bdb_cleanup(bdb_instance_t *db); + +isc_result_t +bdb_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name, + char *db_file, int flags); + +isc_result_t +bdb_findzone(void *driverarg, void *dbdata, const char *name); + +isc_result_t +bdb_parse_data(char *in, parsed_data_t *pd); + +isc_result_t +bdb_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client); + +isc_result_t +bdb_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +isc_result_t +bdb_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +isc_result_t +bdb_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +void +bdb_destroy(void *driverarg, void *dbdata); + + /* Parses the DBT from the Berkeley DB into a parsed_data record + * The parsed_data record should be allocated before and passed into the + * bdb_parse_data function. The char (type & data) fields should not + * be "free"d as that memory is part of the DBT data field. It will be + * "free"d when the DBT is freed. + */ + +isc_result_t +bdb_parse_data(char *in, parsed_data_t *pd){ + + char *endp, *ttlStr; + char *tmp = in; + char *lastchar = (char *) &tmp[strlen(tmp) + 1]; + + // String should be formated as: + // zone(a space)host(a space)ttl(a space)type(a space)remaining data + // examples: + // example.com www 10 A 127.0.0.1 + // example.com mail 10 A 127.0.0.2 + // example.com @ 10 MX 20 mail.example.com + + // save pointer to zone + pd->zone = tmp; + + /* find space after zone and change it to a '\0' */ + tmp = strchr(tmp, ' '); + // verify we found a space + if(tmp == NULL) + return ISC_R_FAILURE; + // change the space to a null (string terminator) + tmp[0] = '\0'; + // make sure it is safe to increment pointer + if(++tmp > lastchar) + return ISC_R_FAILURE; + + // save pointer to host + pd->host = tmp; + + /* find space after type and change it to a '\0' */ + tmp = strchr(tmp, ' '); + // verify we found a space + if(tmp == NULL) + return ISC_R_FAILURE; + // change the space to a null (string terminator) + tmp[0] = '\0'; + // make sure it is safe to increment pointer + if(++tmp > lastchar) + return ISC_R_FAILURE; + + // save pointer to dns type + pd->type = tmp; + + /* find space after type and change it to a '\0' */ + tmp = strchr(tmp, ' '); + // verify we found a space + if(tmp == NULL) + return ISC_R_FAILURE; + // change the space to a null (string terminator) + tmp[0] = '\0'; + // make sure it is safe to increment pointer + if(++tmp > lastchar) + return ISC_R_FAILURE; + + // save pointer to dns ttl + ttlStr = tmp; + + /* find space after ttl and change it to a '\0' */ + tmp = strchr(tmp, ' '); + // verify we found a space + if(tmp == NULL) + return ISC_R_FAILURE; + // change the space to a null (string terminator) + tmp[0] = '\0'; + // make sure it is safe to increment pointer + if(++tmp > lastchar) + return ISC_R_FAILURE; + + // save pointer to remainder of DNS data + pd->data = tmp; + + // convert ttl string to integer + pd->ttl = strtol(ttlStr, &endp, 10); + if (*endp != '\0' || pd->ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB driver ttl must be a postive number"); + return ISC_R_FAILURE; + } + + // if we get this far everything should have worked. + return ISC_R_SUCCESS; +} + +/*** + *** DLZ methods + ***/ + +isc_result_t +bdb_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + bdb_instance_t *db = (bdb_instance_t *) dbdata; + DBC *client_cursor = NULL; + DBT key, data; + + // check to see if we are authoritative for the zone first. + result = bdb_findzone(driverarg, dbdata, name); + if(result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + memset(&key, 0, sizeof(DBT)); + key.flags = DB_DBT_MALLOC; + key.data = strdup(name); + if(key.data == NULL){ + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + key.size = strlen(key.data); + + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + data.data = strdup(client); + if(data.data == NULL){ + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + data.size = strlen(data.data); + + // get a cursor to loop through zone data + if(db->client->cursor(db->client, NULL, &client_cursor, 0) != 0){ + result = ISC_R_FAILURE; + goto xfr_cleanup; + } + + switch(client_cursor->c_get(client_cursor, &key, &data, DB_GET_BOTH)){ + case DB_NOTFOUND: + case DB_SECONDARY_BAD: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + +xfr_cleanup: + + // free any memory duplicate string in the key field + if(key.data != NULL) + free(key.data); + + // free any memory allocated to the data field. + if(data.data != NULL) + free(data.data); + + // get rid of zone_cursor + if(client_cursor != NULL) + client_cursor->c_close(client_cursor); + + return result; + +} + +isc_result_t +bdb_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + + isc_result_t result = ISC_R_NOTFOUND; + bdb_instance_t *db = (bdb_instance_t *) dbdata; + DBC *zone_cursor = NULL; + DBT key, data; + int flags; + int bdbres; + parsed_data_t pd; + char *tmp = NULL, *tmp_zone; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.data = tmp_zone = strdup(zone); + + if(key.data == NULL) + return (ISC_R_NOMEMORY); + + key.size = strlen(key.data); + + // get a cursor to loop through zone data + if(db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0){ + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + flags = DB_SET; + + while((bdbres = zone_cursor->c_get(zone_cursor, &key, &data, flags)) == 0){ + + flags = DB_NEXT_DUP; + + tmp = realloc(tmp, data.size + 1); + if(tmp == NULL) + goto allnodes_cleanup; + + strncpy(tmp, data.data, data.size); + tmp[data.size] = '\0'; + + if(bdb_parse_data(tmp, &pd) != ISC_R_SUCCESS) + goto allnodes_cleanup; + + result = dns_sdlz_putnamedrr(allnodes, pd.host, pd.type, pd.ttl, pd.data); + if(result != ISC_R_SUCCESS) + goto allnodes_cleanup; + + } // end while loop + +allnodes_cleanup: + + if(tmp != NULL) + free(tmp); + + // free any memory duplicate string in the key field + if(tmp_zone != NULL) + free(tmp_zone); + + // get rid of zone_cursor + if(zone_cursor != NULL) + zone_cursor->c_close(zone_cursor); + + return result; + +} + + /* Performs BDB cleanup. + * Used by bdb_create if there is an error starting up. + * Used by bdb_destroy when the driver is shutting down. + */ + +void +bdb_cleanup(bdb_instance_t *db){ + + isc_mem_t *mctx; + + /* close databases */ + if(db->data != NULL) + db->data->close(db->data, 0); + if(db->host != NULL) + db->host->close(db->host, 0); + if(db->zone != NULL) + db->zone->close(db->zone, 0); + if(db->client != NULL) + db->client->close(db->client, 0); + + /* close environment */ + if(db->dbenv != NULL) + db->dbenv->close(db->dbenv, 0); + + /* cleanup memory */ + if(db->mctx != NULL){ + /* save mctx for later */ + mctx = db->mctx; + /* return, and detach the memory */ + isc_mem_put(mctx, db, sizeof(bdb_instance_t)); + isc_mem_detach(&mctx); + } +} + +isc_result_t +bdb_findzone(void *driverarg, void *dbdata, const char *name) +{ + + isc_result_t result; + bdb_instance_t *db = (bdb_instance_t *) dbdata; + DBC *zone_cursor = NULL; + DBT key, data; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + + key.data = strdup(name); + + if(key.data == NULL) + return (ISC_R_NOMEMORY); + + key.size = strlen(key.data); + + // get a cursor to loop through zone data + if(db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0){ + result = ISC_R_NOTFOUND; + goto findzone_cleanup; + } + + switch(zone_cursor->c_get(zone_cursor, &key, &data, DB_SET)){ + case DB_NOTFOUND: + case DB_SECONDARY_BAD: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + +findzone_cleanup: + + // free any memory duplicate string in the key field + if(key.data != NULL) + free(key.data); + + // free any memory allocated to the data field. + if(data.data != NULL) + free(data.data); + + // get rid of zone_cursor + if(zone_cursor != NULL) + zone_cursor->c_close(zone_cursor); + + return result; +} + +isc_result_t +bdb_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + + isc_result_t result = ISC_R_NOTFOUND; + bdb_instance_t *db = (bdb_instance_t *) dbdata; + DBC *zone_cursor = NULL; + DBC *host_cursor = NULL; + DBC *join_cursor = NULL; + DBT key, data; + DBC *cur_arr[3]; + int bdbres; + parsed_data_t pd; + char *tmp_zone, *tmp_host = NULL; + char *tmp = NULL; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + // set zone key + key.data = tmp_zone = strdup(zone); + if(key.data == NULL){ + result = ISC_R_NOMEMORY; + goto lookup_cleanup; + } + key.size = strlen(key.data); + + // get a cursor to loop through zone data + if(db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0){ + result = ISC_R_FAILURE; + goto lookup_cleanup; + } + + // initialize zone_cursor with zone_key + if(zone_cursor->c_get(zone_cursor, &key, &data, DB_SET) != 0){ + result = ISC_R_NOTFOUND; + goto lookup_cleanup; + } + + // set host key + key.data = tmp_host = strdup(name); + if(key.data == NULL){ + result = ISC_R_NOMEMORY; + goto lookup_cleanup; + } + key.size = strlen(key.data); + + // get a cursor to loop through host data + if(db->host->cursor(db->host, NULL, &host_cursor, 0) != 0){ + result = ISC_R_FAILURE; + goto lookup_cleanup; + } + + // initialize host_cursor with host_key + if(host_cursor->c_get(host_cursor, &key, &data, DB_SET) != 0){ + result = ISC_R_NOTFOUND; + goto lookup_cleanup; + } + + cur_arr[0] = zone_cursor; + cur_arr[1] = host_cursor; + cur_arr[2] = NULL; + + db->data->join(db->data, cur_arr, &join_cursor, 0); + + while((bdbres = join_cursor->c_get(join_cursor, &key, &data, 0)) == 0){ + + tmp = realloc(tmp, data.size + 1); + if(tmp == NULL) + goto lookup_cleanup; + + strncpy(tmp, data.data, data.size); + tmp[data.size] = '\0'; + + if(bdb_parse_data(tmp, &pd) != ISC_R_SUCCESS) + goto lookup_cleanup; + + result = dns_sdlz_putrr(lookup, pd.type, pd.ttl, pd.data); + + if(result != ISC_R_SUCCESS) + goto lookup_cleanup; + } // end while loop + +lookup_cleanup: + + if(tmp != NULL) + free(tmp); + if(tmp_zone != NULL) + free(tmp_zone); + if(tmp_host != NULL) + free(tmp_host); + + // get rid of the joined cusor + if(join_cursor != NULL) + join_cursor->c_close(join_cursor); + + // get rid of zone_cursor + if(zone_cursor != NULL) + zone_cursor->c_close(zone_cursor); + + // get rid of host_cursor + if(host_cursor != NULL) + host_cursor->c_close(host_cursor); + + return result; + +return ISC_R_NOTFOUND; +} + + + /* Initializes, sets flags and then opens Berkeley databases. */ + +isc_result_t +bdb_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name, + char *db_file, int flags){ + + int result; + + /* Initialize the database. */ + if ((result = db_create(db, db_env, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not initialize %s database. BDB error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* set database flags. */ + if ((result = (*db)->set_flags(*db, flags)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not set flags for %s database. BDB error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* open the database. */ + if ((result = (*db)->open(*db, NULL, db_file, db_name, db_type, + DB_RDONLY | bdb_threads, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "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; +} + +isc_result_t +bdb_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + int bdbres; + bdb_instance_t *db = NULL; + + UNUSED(dlzname); + UNUSED(driverarg); + + /* verify we have 3 arg's passed to the driver */ + if(argc != 3){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Berkeley DB driver requires at least 2 command line args."); + return (ISC_R_FAILURE); + } + + /* allocate and zero memory for driver structure */ + db = isc_mem_get(ns_g_mctx, sizeof(bdb_instance_t)); + if (db == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not allocate memory for database instance object."); + return (ISC_R_NOMEMORY); + } + memset(db, 0, sizeof(bdb_instance_t)); + + /* attach to the memory context */ + isc_mem_attach(ns_g_mctx, &db->mctx); + + /* create BDB environment + * Basically BDB allocates and assigns memory to db->dbenv + */ + bdbres = db_env_create(&db->dbenv, 0); + if(bdbres != 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB environment could not be created. BDB error: %s", + db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open BDB environment */ + bdbres = db->dbenv->open(db->dbenv, argv[1], + DB_INIT_CDB | DB_INIT_MPOOL | bdb_threads | DB_CREATE, 0); + if(bdbres != 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB environment at '%s' could not be opened. BDB error: %s", + argv[1], db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open dlz_data database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->data, dlz_data, argv[2], 0); + if(result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_host database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->host, dlz_host, argv[2], + DB_DUP | DB_DUPSORT); + if(result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_zone database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->zone, dlz_zone, argv[2], + DB_DUP | DB_DUPSORT); + if(result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_client database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->client, + dlz_client, argv[2], DB_DUP | DB_DUPSORT); + if(result != ISC_R_SUCCESS) + goto init_cleanup; + + /* associate the host secondary database with the primary database */ + bdbres = db->data->associate(db->data, NULL, db->host, NULL, 0); + if(bdbres != 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not associate %s database with %s. BDB error: %s", + dlz_host, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* associate the zone secondary database with the primary database */ + bdbres = db->data->associate(db->data, NULL, db->zone, NULL, 0); + if(bdbres != 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not associate %s database with %s. BDB error: %s", + dlz_zone, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + *dbdata = db; + + return(ISC_R_SUCCESS); + +init_cleanup: + + bdb_cleanup(db); + return result; +} + +void +bdb_destroy(void *driverarg, void *dbdata) +{ + UNUSED(driverarg); + + bdb_cleanup((bdb_instance_t *) dbdata); +} + +static dns_sdlzmethods_t dlz_bdb_methods = { + bdb_create, + bdb_destroy, + bdb_findzone, + bdb_lookup, + NULL, // bdb_authority not needed as authority data is returned by lookup + bdb_allnodes, + bdb_allowzonexfr}; + +/* + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_bdb_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ bdb driver."); + + result = dns_sdlzregister("bdb", &dlz_bdb_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA + | DNS_SDLZFLAG_THREADSAFE, ns_g_mctx, &dlz_bdb); + if(result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_bdb_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ bdb driver."); + + if (dlz_bdb != NULL) + dns_sdlzunregister(&dlz_bdb); +} + +#endif diff -Nuar bind-9.3.2-orig/bin/named/dlz_bdbhpt_driver.c bind-9.3.2-mod/bin/named/dlz_bdbhpt_driver.c --- bind-9.3.2-orig/bin/named/dlz_bdbhpt_driver.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/dlz_bdbhpt_driver.c 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,842 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ_BDB + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +dns_sdlzimplementation_t *dlz_bdbhpt = NULL; + +// should the bdb driver use threads. +#ifdef ISC_PLATFORM_USETHREADS +#define bdbhpt_threads DB_THREAD +#else +#define bdbhpt_threads 0 +#endif + +// bdbhpt database names +#define dlz_data "dns_data" +#define dlz_zone "dns_zone" +#define dlz_xfr "dns_xfr" +#define dlz_client "dns_client" + + /* This structure contains all the Berkeley DB handles + * for this instance of the bdbhpt driver. + */ + +typedef struct bdbhpt_instance { + DB_ENV *dbenv; // bdbhpt environment + DB *data; // dns_data database handle + DB *zone; // zone database handle + DB *xfr; // zone xfr database handle + DB *client; // client database handle + isc_mem_t *mctx; // memory context + +}bdbhpt_instance_t; + +typedef struct bdbhpt_parsed_data { + char *host; + char *type; + int ttl; + char *data; +}bdbhpt_parsed_data_t; + + + /* shut up compiler warnings about no previous prototype */ + +char +*bdbhpt_strrev(char *str); + +void +bdbhpt_cleanup(bdbhpt_instance_t *db); + +isc_result_t +bdbhpt_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name, + char *db_file, int flags); + +isc_result_t +bdbhpt_findzone(void *driverarg, void *dbdata, const char *name); + +isc_result_t +bdbhpt_parse_data(char *in, bdbhpt_parsed_data_t *pd); + +isc_result_t +bdbhpt_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client); + +isc_result_t +bdbhpt_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +isc_result_t +bdbhpt_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +isc_result_t +bdbhpt_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +void +bdbhpt_destroy(void *driverarg, void *dbdata); + + /* reverses a string in place */ + +char *bdbhpt_strrev(char *str) +{ + char *p1, *p2; + + if (! str || ! *str) + return str; + for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) + { + *p1 ^= *p2; + *p2 ^= *p1; + *p1 ^= *p2; + } + return str; +} + + +/* Parses the DBT from the Berkeley DB into a parsed_data record + * The parsed_data record should be allocated before and passed into the + * bdbhpt_parse_data function. The char (type & data) fields should not + * be "free"d as that memory is part of the DBT data field. It will be + * "free"d when the DBT is freed. + */ + +isc_result_t +bdbhpt_parse_data(char *in, bdbhpt_parsed_data_t *pd){ + + char *endp, *ttlStr; + char *tmp = in; + char *lastchar = (char *) &tmp[strlen(tmp)]; + + // String should be formated as: + // replication_id(a space)host_name(a space)ttl(a space)type(a space)remaining data + // examples: + // 9191 host 10 A 127.0.0.1 + // server1_212 host 10 A 127.0.0.2 + // {xxxx-xxxx-xxxx-xxxx-xxxx} host 10 MX 20 mail.example.com + + // we don't need the replication id, so don't + // bother saving a pointer to it. + + // find space after replication id + tmp = strchr(tmp, ' '); + // verify we found a space + if (tmp == NULL) + return ISC_R_FAILURE; + // make sure it is safe to increment pointer + if (++tmp > lastchar) + return ISC_R_FAILURE; + + // save pointer to host + pd->host = tmp; + + /* find space after host and change it to a '\0' */ + tmp = strchr(tmp, ' '); + // verify we found a space + if(tmp == NULL) + return ISC_R_FAILURE; + // change the space to a null (string terminator) + tmp[0] = '\0'; + // make sure it is safe to increment pointer + if(++tmp > lastchar) + return ISC_R_FAILURE; + + // save pointer to ttl string + ttlStr = tmp; + + // find space after ttl and change it to a '\0' + tmp = strchr(tmp, ' '); + // verify we found a space + if (tmp == NULL) + return ISC_R_FAILURE; + // change the space to a null (string terminator) + tmp[0] = '\0'; + // make sure it is safe to increment pointer + if (++tmp > lastchar) + return ISC_R_FAILURE; + + // save pointer to dns type + pd->type = tmp; + + // find space after type and change it to a '\0' + tmp = strchr(tmp, ' '); + // verify we found a space + if (tmp == NULL) + return ISC_R_FAILURE; + // change the space to a null (string terminator) + tmp[0] = '\0'; + // make sure it is safe to increment pointer + if (++tmp > lastchar) + return ISC_R_FAILURE; + + // save pointer to remainder of DNS data + pd->data = tmp; + + // convert ttl string to integer + pd->ttl = strtol(ttlStr, &endp, 10); + if (*endp != '\0' || pd->ttl < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt driver ttl must be a postive number"); + return ISC_R_FAILURE; + } + + // if we get this far everything should have worked. + return ISC_R_SUCCESS; +} + +/*** + *** DLZ methods + ***/ + +isc_result_t +bdbhpt_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata; + DBT key, data; + + // check to see if we are authoritative for the zone first. + result = bdbhpt_findzone(driverarg, dbdata, name); + if(result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + memset(&key, 0, sizeof(DBT)); + key.flags = DB_DBT_MALLOC; + key.data = strdup(name); + if(key.data == NULL){ + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + key.size = strlen(key.data); + + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + data.data = strdup(client); + if(data.data == NULL){ + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + data.size = strlen(data.data); + + switch(db->client->get(db->client, NULL, &key, &data, DB_GET_BOTH)){ + case DB_NOTFOUND: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + +xfr_cleanup: + + // free any memory duplicate string in the key field + if(key.data != NULL) + free(key.data); + + // free any memory allocated to the data field. + if(data.data != NULL) + free(data.data); + + return result; + +} + + /* BDB does not allow a secondary index on a database that allows + * duplicates. We have a few options: + * + * 1) kill speed by having lookup method use a secondary db which + * is associated to the primary DB with the DNS data. Then have + * another secondary db for zone transfer which also points to + * the dns_data primary. NO - The point of this driver is + * lookup performance. + * + * 2) Blow up database size by storing DNS data twice. Once for + * the lookup (dns_data) database, and a second time for the zone + * transfer (dns_xfr) database. NO - That would probably require + * a larger cache to provide good performance. Also, that would + * make the DB larger on disk potentially slowing it as well. + * + * 3) Loop through the dns_xfr database with a cursor to get + * all the different hosts in a zone. Then use the zone & host + * together to lookup the data in the dns_data database. YES - + * This may slow down zone xfr's a little, but that's ok they + * don't happen as often and don't need to be as fast. We can + * also use this table when deleting a zone (The BDB driver + * is read only - the delete would be used during replication + * updates by a separate process). + */ + +isc_result_t +bdbhpt_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + + isc_result_t result = ISC_R_NOTFOUND; + bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata; + DBC *xfr_cursor = NULL; + DBC *dns_cursor = NULL; + DBT xfr_key, xfr_data, dns_key, dns_data; + int xfr_flags; + int dns_flags; + int bdbhptres; + bdbhpt_parsed_data_t pd; + char *tmp = NULL, *tmp_zone, *tmp_zone_host; + + UNUSED(driverarg); + + memset(&xfr_key, 0, sizeof(DBT)); + memset(&xfr_data, 0, sizeof(DBT)); + memset(&dns_key, 0, sizeof(DBT)); + memset(&dns_data, 0, sizeof(DBT)); + + xfr_key.data = tmp_zone = strdup(zone); + if(xfr_key.data == NULL) + return (ISC_R_NOMEMORY); + + xfr_key.size = strlen(xfr_key.data); + + // get a cursor to loop through dns_xfr table + if(db->xfr->cursor(db->xfr, NULL, &xfr_cursor, 0) != 0){ + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + // get a cursor to loop through dns_data table + if(db->data->cursor(db->data, NULL, &dns_cursor, 0) != 0){ + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + xfr_flags = DB_SET; + + // loop through xfr table for specified zone. + while((bdbhptres = xfr_cursor->c_get(xfr_cursor, &xfr_key, &xfr_data, xfr_flags)) == 0){ + + xfr_flags = DB_NEXT_DUP; + + // +1 to allow for space between zone and host names + dns_key.size = xfr_data.size + xfr_key.size + 1; + + // +1 to allow for null term at end of string. + dns_key.data = tmp_zone_host = malloc(dns_key.size + 1); + if(dns_key.data == NULL) + goto allnodes_cleanup; + + // construct search key for dns_data. + // zone_name(a space)host_name + strcpy(dns_key.data, zone); // copy zone name + strcat(dns_key.data, " "); // add space + strncat(dns_key.data, xfr_data.data, xfr_data.size); // add host name + + dns_flags = DB_SET; + + while((bdbhptres = dns_cursor->c_get(dns_cursor, &dns_key, &dns_data, dns_flags)) == 0){ + + dns_flags = DB_NEXT_DUP; + + // +1 to allow for null term at end of string. + tmp = realloc(tmp, dns_data.size + 1); + if(tmp == NULL) + goto allnodes_cleanup; + + // copy data to tmp string, and append null term. + strncpy(tmp, dns_data.data, dns_data.size); + tmp[dns_data.size] = '\0'; + + // split string into dns data parts. + if(bdbhpt_parse_data(tmp, &pd) != ISC_R_SUCCESS) + goto allnodes_cleanup; + + result = dns_sdlz_putnamedrr(allnodes, pd.host, pd.type, pd.ttl, pd.data); + if(result != ISC_R_SUCCESS) + goto allnodes_cleanup; + + } // end inner while loop + + // clean up memory + if(tmp_zone_host != NULL){ + free(tmp_zone_host); + tmp_zone_host = NULL; + } + } // end outter while loop + +allnodes_cleanup: + + // free any memory + if(tmp != NULL) + free(tmp); + + if(tmp_zone_host != NULL) + free(tmp_zone_host); + + if(tmp_zone != NULL) + free(tmp_zone); + + // get rid of cursors + if(xfr_cursor != NULL) + xfr_cursor->c_close(xfr_cursor); + + if(dns_cursor != NULL) + dns_cursor->c_close(xfr_cursor); + + return result; +} + + /* Performs bdbhpt cleanup. + * Used by bdbhpt_create if there is an error starting up. + * Used by bdbhpt_destroy when the driver is shutting down. + */ + +void +bdbhpt_cleanup(bdbhpt_instance_t *db){ + + isc_mem_t *mctx; + + /* close databases */ + if(db->data != NULL) + db->data->close(db->data, 0); + if(db->xfr != NULL) + db->xfr->close(db->xfr, 0); + if(db->zone != NULL) + db->zone->close(db->zone, 0); + if(db->client != NULL) + db->client->close(db->client, 0); + + /* close environment */ + if(db->dbenv != NULL) + db->dbenv->close(db->dbenv, 0); + + /* cleanup memory */ + if(db->mctx != NULL){ + /* save mctx for later */ + mctx = db->mctx; + /* return, and detach the memory */ + isc_mem_put(mctx, db, sizeof(bdbhpt_instance_t)); + isc_mem_detach(&mctx); + } +} + +isc_result_t +bdbhpt_findzone(void *driverarg, void *dbdata, const char *name) +{ + + isc_result_t result; + bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata; + DBT key, data; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + + key.data = strdup(name); + + if(key.data == NULL) + return (ISC_R_NOMEMORY); + + // reverse string to take advantage of BDB locality of reference + // if we need futher lookups because the zone doesn't match the + // first time. + key.data = bdbhpt_strrev(key.data); + key.size = strlen(key.data); + + switch(db->zone->get(db->zone, NULL, &key, &data, 0)){ + case DB_NOTFOUND: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + + // free any memory duplicate string in the key field + if(key.data != NULL) + free(key.data); + + // free any memory allocated to the data field. + if(data.data != NULL) + free(data.data); + + return result; +} + +isc_result_t +bdbhpt_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + + isc_result_t result = ISC_R_NOTFOUND; + bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata; + DBC *data_cursor = NULL; + DBT key, data; + int bdbhptres; + int flags; + + bdbhpt_parsed_data_t pd; + char *tmp = NULL; + char *keyStr = NULL; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.size = strlen(zone) + strlen(name) + 1; + + // allocate mem for key + key.data = keyStr = malloc((key.size + 1) * sizeof(char)); + + if (keyStr == NULL) + return ISC_R_NOMEMORY; + + strcpy(keyStr, zone); + strcat(keyStr, " "); + strcat(keyStr, name); + + // get a cursor to loop through data + if (db->data->cursor(db->data, NULL, &data_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto lookup_cleanup; + } + + result = ISC_R_NOTFOUND; + + flags = DB_SET; + while ((bdbhptres = data_cursor->c_get(data_cursor, &key, &data, + flags)) == 0) { + + flags = DB_NEXT_DUP; + tmp = realloc(tmp, data.size + 1); + if (tmp == NULL) + goto lookup_cleanup; + + strncpy(tmp, data.data, data.size); + tmp[data.size] = '\0'; + + if (bdbhpt_parse_data(tmp, &pd) != ISC_R_SUCCESS) + goto lookup_cleanup; + + result = dns_sdlz_putrr(lookup, pd.type, pd.ttl, pd.data); + + if (result != ISC_R_SUCCESS) + goto lookup_cleanup; + } // end while loop + +lookup_cleanup: + + // get rid of cursor + if (data_cursor != NULL) + data_cursor->c_close(data_cursor); + + if (keyStr != NULL) + free(keyStr); + if (tmp != NULL) + free(tmp); + + return result; +} + + /* Initializes, sets flags and then opens Berkeley databases. */ + +isc_result_t +bdbhpt_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name, + char *db_file, int flags){ + + int result; + + /* Initialize the database. */ + if ((result = db_create(db, db_env, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt could not initialize %s database. bdbhpt error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* set database flags. */ + if ((result = (*db)->set_flags(*db, flags)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt could not set flags for %s database. bdbhpt error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* open the database. */ + if ((result = (*db)->open(*db, NULL, db_file, db_name, db_type, + DB_RDONLY | bdbhpt_threads, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt could not open %s database in %s. bdbhpt error: %s", + db_name, db_file, db_strerror(result)); + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +isc_result_t +bdbhpt_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + int bdbhptres; + int bdbFlags = 0; + bdbhpt_instance_t *db = NULL; + + UNUSED(dlzname); + UNUSED(driverarg); + + /* verify we have 4 arg's passed to the driver */ + if(argc != 4){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt driver requires at least 3 command line args."); + return (ISC_R_FAILURE); + } + + switch((char) *argv[1]){ + // Transactional mode. Highest safety - lowest speed. + case 'T': + case 't': + bdbFlags = DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "bdbhpt driver using transactional mode."); + break; + // Concurrent mode. Lower saftey (no rollback) - higher speed. + case 'C': + case 'c': + bdbFlags = DB_INIT_CDB | DB_INIT_MPOOL; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "bdbhpt driver using concurrent mode."); + break; + // Private mode. No inter-process communication & no locking. + // Lowest saftey - highest speed. + case 'P': + case 'p': + bdbFlags = DB_PRIVATE | DB_INIT_MPOOL; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "bdbhpt driver using private mode."); + break; + default: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt driver requires the operating mode be set to P or C or T. You specified '%s'", + argv[1]); + return (ISC_R_FAILURE); + } + + /* allocate and zero memory for driver structure */ + db = isc_mem_get(ns_g_mctx, sizeof(bdbhpt_instance_t)); + if (db == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not allocate memory for database instance object."); + return (ISC_R_NOMEMORY); + } + memset(db, 0, sizeof(bdbhpt_instance_t)); + + /* attach to the memory context */ + isc_mem_attach(ns_g_mctx, &db->mctx); + + /* create bdbhpt environment + * Basically bdbhpt allocates and assigns memory to db->dbenv + */ + bdbhptres = db_env_create(&db->dbenv, 0); + if(bdbhptres != 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt environment could not be created. bdbhpt error: %s", + db_strerror(bdbhptres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open bdbhpt environment */ + bdbhptres = db->dbenv->open(db->dbenv, argv[2], + bdbFlags | bdbhpt_threads | DB_CREATE, 0); + if(bdbhptres != 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt environment at '%s' could not be opened. bdbhpt error: %s", + argv[2], db_strerror(bdbhptres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open dlz_data database. */ + result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->data, + dlz_data, argv[3], DB_DUP | DB_DUPSORT); + if(result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_xfr database. */ + result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->xfr, + dlz_xfr, argv[3], DB_DUP | DB_DUPSORT); + if(result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_zone database. */ + result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->zone, + dlz_zone, argv[3], 0); + if(result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_client database. */ + result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->client, + dlz_client, argv[3], DB_DUP | DB_DUPSORT); + if(result != ISC_R_SUCCESS) + goto init_cleanup; + + *dbdata = db; + + return(ISC_R_SUCCESS); + +init_cleanup: + + bdbhpt_cleanup(db); + return result; +} + +void +bdbhpt_destroy(void *driverarg, void *dbdata) +{ + UNUSED(driverarg); + + bdbhpt_cleanup((bdbhpt_instance_t *) dbdata); +} + +static dns_sdlzmethods_t dlz_bdbhpt_methods = { + bdbhpt_create, + bdbhpt_destroy, + bdbhpt_findzone, + bdbhpt_lookup, + NULL, // bdbhpt_authority not needed as authority data is returned by lookup + bdbhpt_allnodes, + bdbhpt_allowzonexfr}; + +/* + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_bdbhpt_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ bdbhpt driver."); + + result = dns_sdlzregister("bdbhpt", &dlz_bdbhpt_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA + | DNS_SDLZFLAG_THREADSAFE, ns_g_mctx, &dlz_bdbhpt); + if(result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_bdbhpt_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ bdbhpt driver."); + + if (dlz_bdbhpt != NULL) + dns_sdlzunregister(&dlz_bdbhpt); +} + +#endif diff -Nuar bind-9.3.2-orig/bin/named/dlz_filesystem_driver.c bind-9.3.2-mod/bin/named/dlz_filesystem_driver.c --- bind-9.3.2-orig/bin/named/dlz_filesystem_driver.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/dlz_filesystem_driver.c 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,994 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ_FILESYSTEM + +#include +#include +#include +#include + + +#include + + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include + +#include +#include + +dns_sdlzimplementation_t *dlz_fs = NULL; + +typedef struct config_data { + char *basedir; + int basedirsize; + char *datadir; + int datadirsize; + char *xfrdir; + int xfrdirsize; + int splitcnt; + char separator; + char pathsep; + isc_mem_t *mctx; +}config_data_t; + +typedef struct dir_entry dir_entry_t; + +struct dir_entry { + char dirpath[ISC_DIR_PATHMAX]; + ISC_LINK(dir_entry_t) link; +}; + +typedef ISC_LIST(dir_entry_t) dlist_t; + +/*** + *** method prototypes + *** Declared here to shut up compiler warnings + *** about "no previous prototype" + ***/ + +isc_boolean_t is_safe(const char *input); + +isc_result_t create_path_helper(char *out, const char *in, config_data_t *cd); + +isc_result_t create_path(const char *zone, const char *host, + const char *client, config_data_t *cd, char **path); + +isc_result_t process_dir(isc_dir_t dir, void *passback, config_data_t *cd, + dlist_t *dir_list, unsigned int basedirlen); + +isc_result_t fs_allowzonexfr(void *driverarg, void *dbdata, + const char *name, const char *client); + +isc_result_t fs_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +isc_result_t fs_findzone(void *driverarg, void *dbdata, const char *name); + +isc_result_t fs_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +isc_result_t fs_create(const char *dlzname, unsigned int argc, + char *argv[], void *driverarg, void **dbdata); + +void fs_destroy(void *driverarg, void *dbdata); + +isc_result_t dlz_fs_init(void); + +void dlz_fs_clear(void); + +/*** + *** Private methods + ***/ + +isc_boolean_t +is_safe(const char *input) +{ + unsigned int i; + unsigned int len = strlen(input); + + // check that only allowed characters are in the domain name + for (i=0; i < len; i++) { + // '.' is allowed, but has special requirements + if(input[i] == '.'){ + // '.' is not allowed as first char + if(i == 0) + return ISC_FALSE; + // '..', two dots together is not allowed. + else if (input[i-1] == '.') + return ISC_FALSE; + // '.' is not allowed as last char + if(i == len) + return ISC_FALSE; + // only 1 dot in ok location, continue at next char + continue; + } + // '-' is allowed, continue at next char + if(input[i] == '-') + continue; + // 0-9 is allowed, continue at next char + if(input[i] >= '0' && input[i] <= '9') + continue; + // A-Z uppercase is allowed, continue at next char + if(input[i] >= 'A' && input[i] <= 'Z') + continue; + // a-z lowercase is allowed, continue at next char + if(input[i] >= 'a' && input[i] <= 'z') + continue; + + // colon needs to be allowed for IPV6 client addresses. + // Not dangerous in domain names, as not a special char. + if(input[i] == ':') + continue; + + // '@' needs to be allowed for in zone data. + // Not dangerous in domain names, as not a special char. + if(input[i] == '@') + continue; + + // if we reach this point we have encountered a disallowed char! + return ISC_FALSE; + } + // everything ok. + return ISC_TRUE; +} + +isc_result_t +create_path_helper(char *out, const char *in, config_data_t *cd) +{ + + char *tmpString; + char *tmpPtr; + int i; + + tmpString = isc_mem_strdup(ns_g_mctx, in); + if(tmpString == NULL) + return (ISC_R_NOMEMORY); + + // don't forget is_safe guarantees '.' will NOT be the first/last char + while( (tmpPtr = strrchr(tmpString, '.')) != NULL){ + i = 0; + while(tmpPtr[i+1] != '\0'){ + if(cd->splitcnt < 1) + strcat(out, (char *) &tmpPtr[i+1]); + else + strncat(out, (char *) &tmpPtr[i+1], cd->splitcnt); + strncat(out, (char *) &cd->pathsep, 1); + if(cd->splitcnt == 0) + break; + if(strlen( (char *) &tmpPtr[i+1]) <= (unsigned int) cd->splitcnt) + break; + i += cd->splitcnt; + } + tmpPtr[0] = '\0'; + } + + // handle the "first" label properly + i=0; + tmpPtr = tmpString; + while(tmpPtr[i] != '\0'){ + if(cd->splitcnt < 1) + strcat(out, (char *) &tmpPtr[i]); + else + strncat(out, (char *) &tmpPtr[i], cd->splitcnt); + strncat(out, (char *) &cd->pathsep, 1); + if(cd->splitcnt == 0) + break; + if(strlen( (char *) &tmpPtr[i]) <= (unsigned int) cd->splitcnt) + break; + i += cd->splitcnt; + } + + isc_mem_free(ns_g_mctx, tmpString); + return (ISC_R_SUCCESS); +} + + /* + * Checks to make sure zone and host are safe. If safe, then + * hashes zone and host strings to build a path. If zone / host + * are not safe an error is returned. + */ + +isc_result_t +create_path(const char *zone, const char *host, const char *client, + config_data_t *cd, char **path) +{ + + char *tmpPath; + int pathsize; + int len; + isc_result_t result; + + // we require a zone & cd parameter + REQUIRE(zone != NULL); + REQUIRE(cd != NULL); + // require path to be a pointer to NULL + REQUIRE(path != NULL && *path == NULL); + // client and host may both be NULL, but they can't both be NON-NULL + REQUIRE( (host == NULL && client == NULL) || + (host != NULL && client == NULL) || + (host == NULL && client != NULL) ); + + // if the requested zone is "unsafe", return error + if(is_safe(zone) != ISC_TRUE) + return (ISC_R_FAILURE); + + // if host was passed, verify that it is safe + if( (host != NULL) && (is_safe(host) != ISC_TRUE) ) + return (ISC_R_FAILURE); + + // if host was passed, verify that it is safe + if( (client != NULL) && (is_safe(client) != ISC_TRUE) ) + return (ISC_R_FAILURE); + + // Determine how much memory the split up string will require + if(host != NULL) + len = strlen(zone) + strlen(host); + else if(client != NULL) + len = strlen(zone) + strlen(client); + else + len = strlen(zone); + + // even though datadir and xfrdir will never be in the same string + // we only waste a few bytes by allocating for both, and then we are + // safe from buffer overruns. + pathsize = len + cd->basedirsize + cd->datadirsize + cd->xfrdirsize + 4; + + // if we are splitting names, we will need extra space. + if(cd->splitcnt > 0) + pathsize += len/cd->splitcnt; + + tmpPath = isc_mem_allocate(ns_g_mctx , pathsize * sizeof(char)); + if(tmpPath == NULL){ + // write error message + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver unable to allocate memory in create_path()."); + result = ISC_R_NOMEMORY; + goto cleanup_mem; + } + + // build path string + // start out with base directory. + strcpy(tmpPath, cd->basedir); + + // add zone name - parsed properly + if((result = create_path_helper(tmpPath, zone, cd)) != ISC_R_SUCCESS) + goto cleanup_mem; + + /* + * When neither client or host is passed we are building a path + * to see if a zone is supported. We require that a zone path + * have the "data dir" directory contained within it so that we + * know this zone is really supported. Otherwise, this zone may not + * really be supported because we are supporting a delagated sub zone. + * Example: + * We are supporting long.domain.com and using a splitcnt of 0. + * the base dir is "/base-dir/" and the data dir is "/.datadir" + * We want to see if we are authoritative for domain.com. + * Path /base-dir/com/domain/.datadir + * since /base-dir/com/domain/.datadir does not exist, we are not + * authoritative for the domain "domain.com". However we are + * authoritative for the domain "long.domain.com" because the path + * /base-dir/com/domain/long/.datadir does exist! + */ + + // if client is passed append xfr dir, otherwise append data dir + if(client != NULL){ + strcat(tmpPath, cd->xfrdir); + strncat(tmpPath, (char *) &cd->pathsep, 1); + strcat(tmpPath, client); + }else{ + strcat(tmpPath, cd->datadir); + } + + // if host not null, add it. + if(host != NULL){ + strncat(tmpPath, (char *) &cd->pathsep, 1); + if((result = create_path_helper(tmpPath, host, cd)) != ISC_R_SUCCESS) + goto cleanup_mem; + } + + //return the path we built. + *path = tmpPath; + + // return success + result = ISC_R_SUCCESS; + +cleanup_mem: // cleanup memory + + // free tmpPath memory + if(tmpPath != NULL && result != ISC_R_SUCCESS) + isc_mem_free(ns_g_mctx, tmpPath); + + // free tmpPath memory + return result; +} + +isc_result_t +process_dir(isc_dir_t dir, void *passback, config_data_t *cd, + dlist_t *dir_list, unsigned int basedirlen) +{ + + char tmp[ISC_DIR_PATHMAX + ISC_DIR_NAMEMAX]; + int astPos; + struct stat sb; + isc_result_t result = ISC_R_FAILURE; + char *endp; + char *type; + char *ttlStr; + char *data; + char host[ISC_DIR_NAMEMAX]; + char *tmpString; + char *tmpPtr; + int ttl; + int i; + int len; + dir_entry_t *direntry; + isc_boolean_t foundHost; + + tmp[0] = '\0'; // set 1st byte to '\0' so strcpy works right. + host[0] = '\0'; + foundHost = ISC_FALSE; + + // copy base directory name to tmp. + strcpy(tmp, dir.dirname); + + // dir.dirname will always have '*' as the last char. + astPos = strlen(dir.dirname) - 1; + + // if dir_list != NULL, were are performing a zone xfr + if(dir_list != NULL){ + // if splitcnt == 0, determine host from path. + if(cd->splitcnt == 0){ + if(strlen(tmp) - 3 > basedirlen){ + tmp[astPos-1] = '\0'; + tmpString = (char *) &tmp[basedirlen+1]; + // handle filesystem's special wildcard "-" + if(strcmp(tmpString, "-") == 0){ + strcpy(host, "*"); + }else{ // not special wildcard -- normal name + while( (tmpPtr = strrchr(tmpString, cd->pathsep)) != NULL){ + strcat(host, tmpPtr + 1); + strcat(host, "."); + tmpPtr[0] = '\0'; + } + strcat(host, tmpString); + } + + foundHost = ISC_TRUE; + // set tmp again for use later + strcpy(tmp, dir.dirname); + } + }else{ // if splitcnt != 0 + // determine host from ".host" directory entry + while (isc_dir_read(&dir) == ISC_R_SUCCESS){ + if(strncasecmp(".host", dir.entry.name, 5) == 0){ + // handle filesystem's special wildcard "-" + if(strcmp( (char *) &dir.entry.name[6], "-") == 0) + strcpy(host, "*"); + else + strcpy(host, (char *) &dir.entry.name[6]); + foundHost = ISC_TRUE; + break; + } + } + // reset dir list for use later + isc_dir_reset(&dir); + } // end of else + } + + while (isc_dir_read(&dir) == ISC_R_SUCCESS) { + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Filesystem driver Dir name: '%s' Dir entry: '%s'\n", + dir.dirname, dir.entry.name); + + // skip any entries starting with "." + if(dir.entry.name[0] == '.') + continue; + + // get rid of '*', set to NULL. Effectively trims string from + // previous loop to base directory only while still leaving + // memory for concat to be performed next. + + tmp[astPos] = '\0'; + + // add name to base directory name. + strcat(tmp, dir.entry.name); + + // make sure we can stat entry + if (stat(tmp, &sb) == 0 ){ + // if entry is a directory + if((sb.st_mode & S_IFDIR) != 0){ + // if dir list is NOT NULL, add dir to dir list + if(dir_list != NULL){ + direntry = isc_mem_get(ns_g_mctx, sizeof(dir_entry_t)); + if(direntry == NULL) + return (ISC_R_NOMEMORY); + strcpy(direntry->dirpath, tmp); + ISC_LINK_INIT(direntry, link); + ISC_LIST_APPEND(*dir_list, direntry, link); + result = ISC_R_SUCCESS; + } + continue; + // if entry is a file be sure we do not add entry + // to DNS results if we are performing a zone xfr + // and we could not find a host entry. + } else if(dir_list != NULL && foundHost == ISC_FALSE){ + continue; + } + }else // if we cannot stat entry, skip it. + continue; + + type = dir.entry.name; + ttlStr = strchr(type, cd->separator); + if(ttlStr == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver: %s could not be parsed properly", tmp); + return ISC_R_FAILURE; + } + + // replace separator char with NULL to split string + ttlStr[0] = '\0'; + // start string after NULL of previous string + ttlStr = (char *) &ttlStr[1]; + + data = strchr(ttlStr, cd->separator); + if(data == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver: %s could not be parsed properly", tmp); + return ISC_R_FAILURE; + } + + // replace separator char with NULL to split string + data[0] = '\0'; + + // start string after NULL of previous string + data = (char *) &data[1]; + + // replace all cd->separator chars with a space. + len = strlen(data); + + for(i=0; i < len; i++){ + if(data[i] == cd->separator) + data[i] = ' '; + } + + // convert text to int, make sure it worked right + ttl = strtol(ttlStr, &endp, 10); + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver ttl must be a postive number"); + } + + // pass data back to Bind + if(dir_list == NULL) + result = dns_sdlz_putrr( (dns_sdlzlookup_t *) passback, type, + ttl, data); + else + result = dns_sdlz_putnamedrr( (dns_sdlzallnodes_t *) passback, + (char *) host, type, ttl, data); + + // if error, return error right away + if(result != ISC_R_SUCCESS) + return result; + } // end of while loop + + return result; +} + +/*** + *** SDLZ interface methods + ***/ + +isc_result_t +fs_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + + isc_result_t result; + char *path; + struct stat sb; + config_data_t *cd; + path = NULL; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + if(create_path(name, NULL, client, cd, &path) != ISC_R_SUCCESS){ + return (ISC_R_NOTFOUND); + } + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_AXFR; + } + + if( (sb.st_mode & S_IFREG) != 0){ + result = ISC_R_SUCCESS; + goto complete_AXFR; + } + + result = ISC_R_NOTFOUND; + +complete_AXFR: + isc_mem_free(ns_g_mctx, path); + return result; +} + +isc_result_t +fs_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + + isc_result_t result; + dlist_t *dir_list; + config_data_t *cd; + char *basepath; + unsigned int basepathlen; + struct stat sb; + isc_dir_t dir; + dir_entry_t *dir_entry; + dir_entry_t *next_de; + + basepath = NULL; + dir_list = NULL; + + UNUSED(driverarg); + UNUSED(allnodes); + + cd = (config_data_t *) dbdata; + + /* allocate memory for list */ + dir_list = isc_mem_get(ns_g_mctx, sizeof(dlist_t)); + if (dir_list == NULL){ + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + /* initialize list */ + ISC_LIST_INIT(*dir_list); + + if(create_path(zone, NULL, NULL, cd, &basepath) != ISC_R_SUCCESS){ + return (ISC_R_NOTFOUND); + } + + // remove path separator at end of path so stat works properly + basepathlen = strlen(basepath); + + if (stat(basepath, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + if( (sb.st_mode & S_IFDIR) == 0){ + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + // initialize and open directory + isc_dir_init(&dir); + result = isc_dir_open(&dir, basepath); + + // if directory open failed, return error. + if (result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Unable to open %s directory to read entries.", basepath); + result = ISC_R_FAILURE; + goto complete_allnds; + } + + // process the directory + result = process_dir(dir, allnodes, cd, dir_list, basepathlen); + + // close the directory + isc_dir_close(&dir); + + if(result != ISC_R_SUCCESS) + goto complete_allnds; + + // get first dir entry from list. + dir_entry = ISC_LIST_HEAD(*dir_list); + while (dir_entry != NULL){ + + result = isc_dir_open(&dir, dir_entry->dirpath); + // if directory open failed, return error. + if (result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Unable to open %s directory to read entries.", basepath); + result = ISC_R_FAILURE; + goto complete_allnds; + } + + // process the directory + result = process_dir(dir, allnodes, cd, dir_list, basepathlen); + + // close the directory + isc_dir_close(&dir); + + if(result != ISC_R_SUCCESS) + goto complete_allnds; + + dir_entry = ISC_LIST_NEXT(dir_entry, link); + } // end while + +complete_allnds: + if(dir_list != NULL){ + // clean up entries from list. + dir_entry = ISC_LIST_HEAD(*dir_list); + while (dir_entry != NULL){ + next_de = ISC_LIST_NEXT(dir_entry, link); + isc_mem_put(ns_g_mctx, dir_entry, sizeof(dir_entry_t)); + dir_entry = next_de; + } // end while + isc_mem_put(ns_g_mctx, dir_list, sizeof(dlist_t)); + } + + if(basepath != NULL) + isc_mem_free(ns_g_mctx, basepath); + + return result; +} + +isc_result_t +fs_findzone(void *driverarg, void *dbdata, const char *name) +{ + + isc_result_t result; + char *path; + struct stat sb; + path = NULL; + + UNUSED(driverarg); + + if(create_path(name, NULL, NULL, (config_data_t *) dbdata, &path) != ISC_R_SUCCESS){ + return (ISC_R_NOTFOUND); + } + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Filesystem driver Findzone() Checking for path: '%s'\n", path); + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_FZ; + } + + if( (sb.st_mode & S_IFDIR) != 0){ + result = ISC_R_SUCCESS; + goto complete_FZ; + } + + result = ISC_R_NOTFOUND; + +complete_FZ: + + isc_mem_free(ns_g_mctx, path); + return result; +} + +isc_result_t +fs_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + char *path; + struct stat sb; + isc_dir_t dir; + path = NULL; + + UNUSED(driverarg); + UNUSED(lookup); + + if(strcmp(name, "*") == 0) // handle filesystem's special wildcard "-" + result = create_path(zone, "-", NULL, (config_data_t *) dbdata, &path); + else + result = create_path(zone, name, NULL, (config_data_t *) dbdata, &path); + + if( result != ISC_R_SUCCESS){ + return (ISC_R_NOTFOUND); + } + + // remove path separator at end of path so stat works properly + path[strlen(path)-1] = '\0'; + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Filesystem driver lookup() Checking for path: '%s'\n", path); + + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_lkup; + } + + if( (sb.st_mode & S_IFDIR) == 0){ + result = ISC_R_NOTFOUND; + goto complete_lkup; + } + + // initialize and open directory + isc_dir_init(&dir); + result = isc_dir_open(&dir, path); + + // if directory open failed, return error. + if (result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Unable to open %s directory to read entries.", path); + result = ISC_R_FAILURE; + goto complete_lkup; + } + + // process any records in the directory + result = process_dir(dir, lookup, (config_data_t *) dbdata, NULL, 0); + + // close the directory + isc_dir_close(&dir); + +complete_lkup: + + isc_mem_free(ns_g_mctx, path); + return result; +} + +isc_result_t +fs_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + config_data_t *cd; + char *endp; + int len; + char pathsep; + + UNUSED(driverarg); + UNUSED(dlzname); + + // we require 5 command line args. + if(argc != 6){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver requires 6 command line args."); + return (ISC_R_FAILURE); + } + + if(strlen(argv[5]) > 1){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver can only accept a single character for separator."); + return (ISC_R_FAILURE); + } + + // verify base dir ends with '/' or '\' + len = strlen(argv[1]); + if(argv[1][len-1] != '\\' && argv[1][len-1] != '/'){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Base dir parameter for filesystem driver should end with %s", + "either '/' or '\\' "); + return (ISC_R_FAILURE); + } + + // determine and save path separator for later + if(argv[1][len-1] == '\\') + pathsep = '\\'; + else + pathsep = '/'; + + // allocate memory for our config data + cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t)); + if(cd == NULL) + goto no_mem; + + // zero the memory + memset(cd, 0, sizeof(config_data_t)); + + cd->pathsep = pathsep; + + // get and store our base directory + cd->basedir = isc_mem_strdup(ns_g_mctx, argv[1]); + if(cd->basedir == NULL) + goto no_mem; + cd->basedirsize = strlen(cd->basedir); + + // get and store our data sub-dir + cd->datadir = isc_mem_strdup(ns_g_mctx, argv[2]); + if(cd->datadir == NULL) + goto no_mem; + cd->datadirsize = strlen(cd->datadir); + + // get and store our zone xfr sub-dir + cd->xfrdir = isc_mem_strdup(ns_g_mctx, argv[3]); + if(cd->xfrdir == NULL) + goto no_mem; + cd->xfrdirsize = strlen(cd->xfrdir); + + // get and store our directory split count + cd->splitcnt = strtol(argv[4], &endp, 10); + if (*endp != '\0' || cd->splitcnt < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Directory split count must be zero (0) or a postive number"); + } + + // get and store our separator character + cd->separator = *argv[5]; + + // attach config data to memory context + isc_mem_attach(ns_g_mctx, &cd->mctx); + + // pass back config data + *dbdata = cd; + + // return success + return(ISC_R_SUCCESS); + + // handle no memory error +no_mem: + + // if we allocated a config data object clean it up + if(cd != NULL) + fs_destroy(NULL, cd); + + // write error message + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver unable to allocate memory for config data."); + + // return error + return (ISC_R_NOMEMORY); +} + +void +fs_destroy(void *driverarg, void *dbdata) +{ + isc_mem_t *mctx; + config_data_t *cd; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + // free memory for each section of config data that was allocated + if(cd->basedir != NULL) + isc_mem_free(ns_g_mctx, cd->basedir); + + if(cd->datadir != NULL) + isc_mem_free(ns_g_mctx, cd->datadir); + + if(cd->xfrdir != NULL) + isc_mem_free(ns_g_mctx, cd->xfrdir); + + // hold memory context to use later + mctx = cd->mctx; + + // free config data memory + isc_mem_put(mctx, cd, sizeof(config_data_t)); + + // detach memory from context + isc_mem_detach(&mctx); +} + +static dns_sdlzmethods_t dlz_fs_methods = { + fs_create, + fs_destroy, + fs_findzone, + fs_lookup, + NULL, + fs_allnodes, + fs_allowzonexfr}; + +/* + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_fs_init(void) +{ + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ filesystem driver."); + + result = dns_sdlzregister("filesystem", &dlz_fs_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA, + ns_g_mctx, &dlz_fs); + if(result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_fs_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ filesystem driver."); + + if (dlz_fs != NULL) + dns_sdlzunregister(&dlz_fs); +} + +#endif + diff -Nuar bind-9.3.2-orig/bin/named/dlz_ldap_driver.c bind-9.3.2-mod/bin/named/dlz_ldap_driver.c --- bind-9.3.2-orig/bin/named/dlz_ldap_driver.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/dlz_ldap_driver.c 2006-02-20 19:41:46.000000000 +0100 @@ -0,0 +1,1303 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ_LDAP + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define SIMPLE "simple" +#define KRB41 "krb41" +#define KRB42 "krb42" +#define V2 "v2" +#define V3 "v3" + + +dns_sdlzimplementation_t *dlz_ldap = NULL; + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define LOOKUP 5 + +/*** + *** Structure to hold everthing needed by this "instance" of the LDAP driver + *** remember, the driver code is only loaded once, but may have many separate + *** instances + ***/ + +typedef struct { + +#ifdef ISC_PLATFORM_USETHREADS + + db_list_t *db; // handle to a list of DB + +#else + + dbinstance_t *db; // handle to db + +#endif + + int method; // security authentication method + char *user; // who is authenticating + char *cred; // password for simple authentication method + int protocol; // LDAP communication protocol version + char *hosts; // LDAP server hosts + +} ldap_instance_t; + +isc_result_t +dlz_ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc); + +void +ldap_destroy_dblist(db_list_t *dblist); + +dbinstance_t * +ldap_find_avail_conn(db_list_t *dblist); + +isc_result_t +ldap_get_results(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, void *ptr); + +isc_result_t +ldap_process_results(LDAP *dbc, LDAPMessage *msg, char ** attrs, + void *ptr, isc_boolean_t allnodes); + +isc_result_t +dlz_ldap_checkURL(char *URL, int attrCnt, const char *msg); + +/*** + *** DLZ method prototypes + ***/ + +isc_result_t +dlz_ldap_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client); + +isc_result_t +dlz_ldap_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +isc_result_t +dlz_ldap_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup); + +isc_result_t +dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name); + +isc_result_t +dlz_ldap_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +isc_result_t +dlz_ldap_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +void +dlz_ldap_destroy(void *driverarg, void *dbdata); + + +/*** + *** Private methods + ***/ + + /* checks that the LDAP URL parameters make sense */ + +isc_result_t +dlz_ldap_checkURL(char *URL, int attrCnt, const char *msg){ + + isc_result_t result = ISC_R_SUCCESS; + int ldap_result; + LDAPURLDesc *ldap_url = NULL; + + if(!ldap_is_ldap_url(URL)){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query is not a valid LDAP URL", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + ldap_result = ldap_url_parse(URL, &ldap_url); + if(ldap_result != LDAP_SUCCESS || ldap_url == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "parsing %s query failed", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if(ldap_count_values(ldap_url->lud_attrs) < attrCnt){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query must specify at least %d attributes to return", + msg, attrCnt); + result = ISC_R_FAILURE; + goto cleanup; + } + + if(ldap_url->lud_host != NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query must not specify a host", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if(ldap_url->lud_port != 389){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query must not specify a port", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if(ldap_url->lud_dn == NULL || strlen (ldap_url->lud_dn) < 1){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query must specify a search base", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if(ldap_url->lud_exts != NULL || ldap_url->lud_crit_exts != 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s uses extensions. The driver does not support LDAP extensions.", + msg); + result = ISC_R_FAILURE; + goto cleanup; + } + +cleanup: + + if(ldap_url != NULL) + ldap_free_urldesc(ldap_url); + + return result; +} + /* Connects / reconnects to LDAP server */ + +isc_result_t +dlz_ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc){ + + isc_result_t result; + int ldap_result; + + // if we have a connection, get ride of it. + if(dbc->dbconn != NULL){ + ldap_unbind_s((LDAP *) dbc->dbconn); + dbc->dbconn = NULL; + } + + // now connect / reconnect. + + // initialize. + dbc->dbconn = ldap_init(dbi->hosts, LDAP_PORT); + if(dbc->dbconn == NULL) + return ISC_R_NOMEMORY; + + // set protocol version. + ldap_result = ldap_set_option((LDAP *) dbc->dbconn, + LDAP_OPT_PROTOCOL_VERSION, &(dbi->protocol)); + if(ldap_result != LDAP_SUCCESS){ + result = ISC_R_NOPERM; + goto cleanup; + } + + // "bind" to server. i.e. send username / pass + ldap_result = ldap_bind_s((LDAP *) dbc->dbconn, dbi->user, + dbi->cred, dbi->method); + if(ldap_result != LDAP_SUCCESS){ + result = ISC_R_FAILURE; + goto cleanup; + } + + return ISC_R_SUCCESS; + +cleanup: + + // cleanup if failure. + if(dbc->dbconn != NULL){ + ldap_unbind_s((LDAP *) dbc->dbconn); + dbc->dbconn = NULL; + } + + return result; +} + +#ifdef ISC_PLATFORM_USETHREADS + + + /* Properly cleans up a list of database instances. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ +void +ldap_destroy_dblist(db_list_t *dblist) +{ + + dbinstance_t *ndbi = NULL; + dbinstance_t *dbi = NULL; + + // get the first DBI in the list + ndbi = ISC_LIST_HEAD(*dblist); + + // loop through the list + while(ndbi != NULL){ + dbi = ndbi; + // get the next DBI in the list + ndbi = ISC_LIST_NEXT(dbi, link); + // release DB connection + if(dbi->dbconn != NULL) + ldap_unbind_s((LDAP *) dbi->dbconn); + // release all memory that comprised a DBI + destroy_sqldbinstance(dbi); + } + // release memory for the list structure + isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t)); +} + + /* Loops through the list of DB instances, attempting to lock + * on the mutex. If successful, the DBI is reserved for use + * and the thread can perform queries against the database. + * If the lock fails, the next one in the list is tried. + * looping continues until a lock is obtained, or until + * the list has been searched dbc_search_limit times. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ + +dbinstance_t * +ldap_find_avail_conn(db_list_t *dblist) +{ + dbinstance_t *dbi = NULL; + dbinstance_t *head; + int count = 0; + + // get top of list + head = dbi = ISC_LIST_HEAD(*dblist); + + // loop through list + while(count < dbc_search_limit){ + // try to lock on the mutex + if(isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS) + return dbi; // success, return the DBI for use. + + // not successful, keep trying + dbi = ISC_LIST_NEXT(dbi, link); + + // check to see if we have gone to the top of the list. + if(dbi == NULL){ + count++; + dbi = head; + } + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "LDAP driver unable to find available connection after searching %d times", + count); + return NULL; +} + +#endif /* ISC_PLATFORM_USETHREADS */ + +isc_result_t +ldap_process_results(LDAP *dbc, LDAPMessage *msg, char ** attrs, + void *ptr, isc_boolean_t allnodes) +{ + isc_result_t result = ISC_R_SUCCESS; + int i = 0; + int j; + int len; + char *attribute = NULL; + LDAPMessage *entry; + char *endp = NULL; + char *host = NULL; + char *type = NULL; + char *data = NULL; + char **vals = NULL; + int ttl; + + // make sure there are at least some attributes to process. + REQUIRE(attrs != NULL || attrs[0] != NULL); + + // get the first entry to process + entry = ldap_first_entry(dbc, msg); + if(entry == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "LDAP no entries to process."); + return ISC_R_FAILURE; + } + + // loop through all entries returned + while(entry != NULL){ + + // reset for this loop + ttl = 0; + len = 0; + i = 0; + attribute = attrs[i]; + + // determine how much space we need for data string + for(j=0; attrs[j] != NULL; j++){ + // get the list of values for this attribute. + vals = ldap_get_values(dbc, entry, attrs[j]); + // skip empty attributes. + if(vals == NULL || ldap_count_values(vals) < 1) + continue; + // we only use the first value + // this driver does not support multi-valued attributes. + len = len + strlen(vals[0]) + 1; + // free vals for next loop + ldap_value_free(vals); + } // end for(j=0; attrs[j] != NULL, j++) loop + + // allocate memory for data string + data = isc_mem_allocate(ns_g_mctx, len + 1); + if(data == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver unable to allocate memory while processing results"); + result = ISC_R_FAILURE; + goto cleanup; + } + + // Make sure data is null termed at the beginning so we + // can check if any data was stored to it later. + data[0] = '\0'; + + // reset j to re-use below + j = 0; + + // loop through the attributes in the order specified. + while(attribute != NULL){ + + // get the list of values for this attribute. + vals = ldap_get_values(dbc, entry, attribute); + + // skip empty attributes. + if(vals == NULL || vals[0] == NULL){ + // increment attibute pointer + attribute = attrs[++i]; + // start loop over + continue; + } + + // j initially = 0. Increment j each time we set a field + // that way next loop will set next field. + switch(j){ + case 0: + j++; + // convert text to int, make sure it worked right + ttl = strtol(vals[0], &endp, 10); + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver ttl must be a postive number"); + goto cleanup; + } + break; + case 1: + j++; + type = isc_mem_strdup(ns_g_mctx, vals[0]); + break; + case 2: + j++; + if(allnodes == isc_boolean_true){ + host = isc_mem_strdup(ns_g_mctx, vals[0]); + } else { + strcpy(data, vals[0]); + } + break; + case 3: + j++; + if(allnodes == isc_boolean_true){ + strcpy(data, vals[0]); + } else { + strcat(data, " "); + strcat(data, vals[0]); + } + break; + default: + strcat(data, " "); + strcat(data, vals[0]); + break; + } // end switch(j) + + // free values + ldap_value_free(vals); + vals = NULL; + + // increment attibute pointer + attribute = attrs[++i]; + } // end while(attribute != NULL) + + if(type == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver unable to retrieve dns type"); + result = ISC_R_FAILURE; + goto cleanup; + } + if(strlen(data) < 1){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver unable to retrieve dns data"); + result = ISC_R_FAILURE; + goto cleanup; + } + if(allnodes == isc_boolean_true){ + if(strcasecmp(host, "~") == 0) + result = dns_sdlz_putnamedrr((dns_sdlzallnodes_t *) ptr, + "*", type, ttl, data); + else + result = dns_sdlz_putnamedrr((dns_sdlzallnodes_t *) ptr, + host, type, ttl, data); + } + else + result = dns_sdlz_putrr((dns_sdlzlookup_t *) ptr, type, ttl, data); + + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver failed while sending data to Bind."); + goto cleanup; + } + + // free memory for type, data and host for next loop + isc_mem_free(ns_g_mctx, type); + isc_mem_free(ns_g_mctx, data); + if(host != NULL) + isc_mem_free(ns_g_mctx, host); + + // get the next entry to process + entry = ldap_next_entry(dbc, entry); + } // end while(entry != NULL) + +cleanup: + + // de-allocate memory + if(vals != NULL) + ldap_value_free(vals); + if(host != NULL) + isc_mem_free(ns_g_mctx, host); + if(type != NULL) + isc_mem_free(ns_g_mctx, type); + if(data != NULL) + isc_mem_free(ns_g_mctx, data); + + return result; +} + + /* This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds either: + * 1) a list of database instances (in multithreaded mode) OR + * 2) a single database instance (in single threaded mode) + * The function will construct the query and obtain an available + * database instance (DBI). It will then run the query and hopefully + * obtain a result set. + */ +isc_result_t +ldap_get_results(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, void *ptr) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + LDAPURLDesc *ldap_url = NULL; + int ldap_result = 0; + LDAPMessage *ldap_msg = NULL; + int i; + int entries; + + /* Extended Patch for Zone Extended Tag */ + char *n_zone_domain; + char *n_zone_tld; + char n_buffer[strlen(zone)+1]; + + + // get db instance / connection +#ifdef ISC_PLATFORM_USETHREADS + + // find an available DBI from the list + dbi = ldap_find_avail_conn((db_list_t *) ((ldap_instance_t *)dbdata)->db); + +#else /* ISC_PLATFORM_USETHREADS */ + + // only 1 DBI - no need to lock instance lock either + // only 1 thread in the whole process, no possible contention. + dbi = (dbinstance_t *) ((ldap_instance_t *)dbdata)->db; + +#endif /* ISC_PLATFORM_USETHREADS */ + + // if DBI is null, can't do anything else + if(dbi == NULL) + return ISC_R_FAILURE; + + // set fields + if(zone != NULL){ + dbi->zone = isc_mem_strdup(ns_g_mctx, zone); + + // Extended Patch for Zone Extended Tag + snprintf(n_buffer, strlen(zone)+1, "%s", zone); + n_zone_domain = strtok(n_buffer, "."); + n_zone_tld = strtok(NULL, ""); + // + dbi->zone_domain = isc_mem_strdup(ns_g_mctx, n_zone_domain); + dbi->zone_tld = isc_mem_strdup(ns_g_mctx, n_zone_tld); + + if(dbi->zone == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + if(dbi->zone_domain == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + if(dbi->zone_tld == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + + + } else { + dbi->zone = NULL; + } + if(record != NULL){ + dbi->record = isc_mem_strdup(ns_g_mctx, record); + if(dbi->record == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { + dbi->record = NULL; + } + if(client != NULL){ + dbi->client = isc_mem_strdup(ns_g_mctx, client); + if(dbi->client == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { + dbi->client = NULL; + } + + // what type of query are we going to run? + switch(query){ + case ALLNODES: + // if the query was not passed in from the config file + // then we can't run it. return not_implemented, + // so it's like the code for that operation + // was never built into the driver.... AHHH flexibility!!! + if(dbi->allnodes_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); + } + break; + case ALLOWXFR: + // same as comments as ALLNODES + if(dbi->allowxfr_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); + } + break; + case AUTHORITY: + // same as comments as ALLNODES + if(dbi->authority_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, dbi->authority_q); + } + break; + case FINDZONE: + // this is required. It's the whole point of DLZ! + if(dbi->findzone_q == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for findzone. Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, dbi->findzone_q); + } + break; + case LOOKUP: + // this is required. It's also a major point of DLZ! + if(dbi->lookup_q == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for lookup. Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, dbi->lookup_q); + } + break; + default: + // this should never happen. If it does, the code is screwed up! + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to postgres_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + // if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! + if(querystring == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + + // output the full query string during debug so we can see + // what lame error the query has. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "\nQuery String: %s\n", querystring); + + // break URL down into it's component parts, if error cleanup + ldap_result = ldap_url_parse(querystring, &ldap_url); + if(ldap_result != LDAP_SUCCESS || ldap_url == NULL){ + result = ISC_R_FAILURE; + goto cleanup; + } + + for(i=0; i < 3; i++){ + + // dbi->dbconn may be null if trying to reconnect on a previous + // query failed. + if(dbi->dbconn == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "LDAP driver attempting to re-connect"); + + result = dlz_ldap_connect((ldap_instance_t *) dbdata, dbi); + if(result != ISC_R_SUCCESS){ + result = ISC_R_FAILURE; + continue; + } + } + + // perform ldap search syncronously + ldap_result = ldap_search_s((LDAP *) dbi->dbconn, ldap_url->lud_dn, + ldap_url->lud_scope, ldap_url->lud_filter, + ldap_url->lud_attrs, 0, &ldap_msg); + + // check return code. + //No such object is ok, just didn't find what we wanted + switch(ldap_result){ + case LDAP_NO_SUCH_OBJECT: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "No object found matching query requirements"); + result = ISC_R_NOTFOUND; + goto cleanup; + break; + case LDAP_SUCCESS: // on success do nothing + result = ISC_R_SUCCESS; + i = 3; + break; + case LDAP_SERVER_DOWN: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "LDAP driver attempting to re-connect"); + result = dlz_ldap_connect((ldap_instance_t *) dbdata, dbi); + if(result != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + break; + default: + // other errors not ok. Log error message and get out + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP error: %s", ldap_err2string(ldap_result)); + result = ISC_R_FAILURE; + goto cleanup; + break; + } // close switch(ldap_result) + } // end for(int i=0 i < 3; i++) + + if(result != ISC_R_SUCCESS) + goto cleanup; + + switch(query){ + case ALLNODES: + result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg, + ldap_url->lud_attrs, ptr, isc_boolean_true); + break; + case AUTHORITY: + case LOOKUP: + result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg, + ldap_url->lud_attrs, ptr, isc_boolean_false); + break; + case ALLOWXFR: + entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg); + if (entries == 0) + result = ISC_R_NOPERM; + else if (entries > 0) + result = ISC_R_SUCCESS; + else + result = ISC_R_FAILURE; + break; + case FINDZONE: + entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg); + if (entries == 0) + result = ISC_R_NOTFOUND; + else if (entries > 0) + result = ISC_R_SUCCESS; + else + result = ISC_R_FAILURE; + break; + default: + // this should never happen. If it does, the code is screwed up! + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to postgres_get_resultset"); + result = ISC_R_UNEXPECTED; + } + + +cleanup: // it's always good to cleanup after yourself + + // if we retrieved results, free them + if(ldap_msg != NULL) + ldap_msgfree(ldap_msg); + + if(ldap_url != NULL) + ldap_free_urldesc(ldap_url); + + // cleanup + if(dbi->zone != NULL) + isc_mem_free(ns_g_mctx, dbi->zone); + // free dbi->zone_domain string + if(dbi->zone_domain != NULL) + isc_mem_free(ns_g_mctx, dbi->zone_domain); + // free dbi->zone_tld string + if(dbi->zone_tld != NULL) + isc_mem_free(ns_g_mctx, dbi->zone_tld); + + if(dbi->record != NULL) + isc_mem_free(ns_g_mctx, dbi->record); + if(dbi->client != NULL) + isc_mem_free(ns_g_mctx, dbi->client); + +#ifdef ISC_PLATFORM_USETHREADS + + // release the lock so another thread can use this dbi + isc_mutex_unlock(&dbi->instance_lock); + +#endif /* ISC_PLATFORM_USETHREADS */ + + // release query string + if(querystring != NULL) + isc_mem_free(ns_g_mctx, querystring ); + + // return result + return result; +} + +/*** + *** DLZ methods + ***/ + +isc_result_t +dlz_ldap_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + + UNUSED(driverarg); + + // check to see if we are authoritative for the zone first + result = dlz_ldap_findzone(driverarg, dbdata, name); + if(result != ISC_R_SUCCESS){ + return result; + } + + // get all the zone data + return ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, NULL); +} + +isc_result_t +dlz_ldap_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + UNUSED(driverarg); + return ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, allnodes); +} + +isc_result_t +dlz_ldap_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + UNUSED(driverarg); + return ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, lookup); +} + +isc_result_t +dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name) +{ + UNUSED(driverarg); + return ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL); +} + +isc_result_t +dlz_ldap_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + UNUSED(driverarg); + if(strcmp(name, "*") == 0) + return ldap_get_results(zone, "~", NULL, LOOKUP, dbdata, lookup); + else + return ldap_get_results(zone, name, NULL, LOOKUP, dbdata, lookup); +} + + +isc_result_t +dlz_ldap_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + + isc_result_t result; + ldap_instance_t *ldap_inst = NULL; + dbinstance_t *dbi = NULL; + int protocol; + int method; + +#ifdef ISC_PLATFORM_USETHREADS + // if multi-threaded, we need a few extra variables. + int dbcount; + char *endp; +// db_list_t *dblist = NULL; + int i; + +#endif /* ISC_PLATFORM_USETHREADS */ + + UNUSED(dlzname); + UNUSED(driverarg); + +#ifdef ISC_PLATFORM_USETHREADS + // if debugging, let user know we are multithreaded. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "LDAP driver running multithreaded"); +#else /* ISC_PLATFORM_USETHREADS */ + // if debugging, let user know we are single threaded. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "LDAP driver running single threaded"); +#endif /* ISC_PLATFORM_USETHREADS */ + + if(argc < 9){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver requires at least 8 command line args."); + return (ISC_R_FAILURE); + } + + /* no more than 13 arg's should be passed to the driver */ + if(argc > 12){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver cannot accept more than 11 command line args."); + return (ISC_R_FAILURE); + } + + // determine protocol version. + if(strncasecmp(argv[2], V2, strlen(V2)) == 0){ + protocol = 2; + } else if(strncasecmp(argv[2], V3, strlen(V3)) == 0){ + protocol = 3; + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver protocol must be either %s or %s", V2, V3); + return (ISC_R_FAILURE); + } + + // determine connection method. + if(strncasecmp(argv[3], SIMPLE, strlen(SIMPLE)) == 0){ + method = LDAP_AUTH_SIMPLE; + } else if(strncasecmp(argv[3], KRB41, strlen(KRB41)) == 0){ + method = LDAP_AUTH_KRBV41; + } else if(strncasecmp(argv[3], KRB42, strlen(KRB42)) == 0){ + method = LDAP_AUTH_KRBV42; + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver authentication method must be one of %s, %s or %s", + SIMPLE, KRB41, KRB42); + return (ISC_R_FAILURE); + } + + // multithreaded build can have multiple DB connections +#ifdef ISC_PLATFORM_USETHREADS + + /* check how many db connections we should create */ + dbcount = strtol(argv[1], &endp, 10); + if (*endp != '\0' || dbcount < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver database connection count must be positive."); + return (ISC_R_FAILURE); + } +#endif + + // check that LDAP URL parameters make sense + switch(argc){ + case 12: + result = dlz_ldap_checkURL(argv[11], 0, "allow zone transfer"); + if(result != ISC_R_SUCCESS) + return result; + case 11: + result = dlz_ldap_checkURL(argv[10], 3, "all nodes"); + if(result != ISC_R_SUCCESS) + return result; + case 10: + if(strlen(argv[9]) > 0){ + result = dlz_ldap_checkURL(argv[9], 3, "authority"); + if(result != ISC_R_SUCCESS) + return result; + } + case 9: + result = dlz_ldap_checkURL(argv[8], 3, "lookup"); + if(result != ISC_R_SUCCESS) + return result; + result = dlz_ldap_checkURL(argv[7], 0, "find zone"); + if(result != ISC_R_SUCCESS) + return result; + break; + default: + result = ISC_R_FAILURE; // not really needed, should shut up compiler. + } + + /* allocate memory for LDAP instance */ + ldap_inst = isc_mem_get(ns_g_mctx, sizeof(ldap_instance_t)); + if(ldap_inst == NULL) + return (ISC_R_NOMEMORY); + memset(ldap_inst, 0, sizeof(ldap_instance_t)); + + // store info needed to automatically re-connect. + ldap_inst->protocol = protocol; + ldap_inst->method = method; + ldap_inst->hosts = isc_mem_strdup(ns_g_mctx, argv[6]); + if(ldap_inst->hosts == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + ldap_inst->user = isc_mem_strdup(ns_g_mctx, argv[4]); + if(ldap_inst->user == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + ldap_inst->cred = isc_mem_strdup(ns_g_mctx, argv[5]); + if(ldap_inst->cred == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + /* allocate memory for database connection list */ + ldap_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t)); + if (ldap_inst->db == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* initialize DB connection list */ + ISC_LIST_INIT(*(ldap_inst->db)); + + // create the appropriate number of database instances (DBI) + // append each new DBI to the end of the list + for(i=0; i < dbcount; i++){ + +#endif /* ISC_PLATFORM_USETHREADS */ + + // how many queries were passed in from config file? + switch(argc){ + case 9: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, NULL, + argv[7], argv[8], NULL, &dbi); + break; + case 10: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, argv[9], + argv[7], argv[8], NULL, &dbi); + break; + case 11: + result = build_sqldbinstance(ns_g_mctx, argv[10], NULL, argv[9], + argv[7], argv[8], NULL, &dbi); + break; + case 12: + result = build_sqldbinstance(ns_g_mctx, argv[10], argv[11], argv[9], + argv[7], argv[8], NULL, &dbi); + break; + default: + result = ISC_R_FAILURE; // not really needed, should shut up compiler. + } + + if(result == ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "LDAP driver created database instance object."); + } else { // unsuccessful?, log err msg and cleanup. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not create database instance object."); + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + // when multithreaded, build a list of DBI's + ISC_LINK_INIT(dbi, link); + ISC_LIST_APPEND(*(ldap_inst->db), dbi, link); +#else + // when single threaded, hold onto the one connection instance. + ldap_inst->db = dbi; + +#endif + // attempt to connect + result = dlz_ldap_connect(ldap_inst, dbi); + + // if db connection cannot be created, log err msg and cleanup. + switch(result){ + // success, do nothing + case ISC_R_SUCCESS: + break; + // no memory means ldap_init could not allocate memory + case ISC_R_NOMEMORY: +#ifdef ISC_PLATFORM_USETHREADS + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not allocate memory for connection number %u", + i+1); +#else + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not allocate memory for connection"); +#endif + goto cleanup; + break; + // no perm means ldap_set_option could not set protocol version + case ISC_R_NOPERM: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not set protocol version."); + result = ISC_R_FAILURE; + goto cleanup; + break; + // failure means couldn't connect to ldap server + case ISC_R_FAILURE: +#ifdef ISC_PLATFORM_USETHREADS + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not bind connection number %u to server.", + i+1); +#else + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not bind connection to server."); +#endif + goto cleanup; + break; + // default should never happen. If it does, major errors. + default: + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dlz_ldap_create() failed: %s", isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + goto cleanup; + break; + } // end switch(result) + + +#ifdef ISC_PLATFORM_USETHREADS + + // set DBI = null for next loop through. + dbi = NULL; + } // end for loop + +#endif /* ISC_PLATFORM_USETHREADS */ + + + // set dbdata to the ldap_instance we created. + *dbdata = ldap_inst; + + // hey, we got through all of that ok, return success. + return(ISC_R_SUCCESS); + +cleanup: + + dlz_ldap_destroy(NULL, ldap_inst); + + return(ISC_R_FAILURE); +} + +void +dlz_ldap_destroy(void *driverarg, void *dbdata) +{ + + UNUSED(driverarg); + + if(dbdata != NULL){ + +#ifdef ISC_PLATFORM_USETHREADS + + // cleanup the list of DBI's + ldap_destroy_dblist((db_list_t *) ((ldap_instance_t *)dbdata)->db); + +#else /* ISC_PLATFORM_USETHREADS */ + + // release connection + if(((ldap_instance_t *)dbdata)->db->dbconn != NULL) + ldap_unbind_s((LDAP *) ((ldap_instance_t *)dbdata)->db->dbconn); + + // destroy single DB instance + destroy_sqldbinstance(((ldap_instance_t *)dbdata)->db); + +#endif /* ISC_PLATFORM_USETHREADS */ + + if(((ldap_instance_t *)dbdata)->hosts != NULL) + isc_mem_free(ns_g_mctx, ((ldap_instance_t *)dbdata)->hosts); + + if(((ldap_instance_t *)dbdata)->user != NULL) + isc_mem_free(ns_g_mctx, ((ldap_instance_t *)dbdata)->user); + + if(((ldap_instance_t *)dbdata)->cred != NULL) + isc_mem_free(ns_g_mctx, ((ldap_instance_t *)dbdata)->cred); + + isc_mem_put(ns_g_mctx, dbdata, sizeof(ldap_instance_t)); + } +} + +static dns_sdlzmethods_t dlz_ldap_methods = { + dlz_ldap_create, + dlz_ldap_destroy, + dlz_ldap_findzone, + dlz_ldap_lookup, + dlz_ldap_authority, + dlz_ldap_allnodes, + dlz_ldap_allowzonexfr}; + +/* + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_ldap_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ ldap driver."); + + result = dns_sdlzregister("ldap", &dlz_ldap_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA, + ns_g_mctx, &dlz_ldap); + if(result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_ldap_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ ldap driver."); + + if (dlz_ldap != NULL) + dns_sdlzunregister(&dlz_ldap); +} + +#endif diff -Nuar bind-9.3.2-orig/bin/named/dlz_mysql_driver.c bind-9.3.2-mod/bin/named/dlz_mysql_driver.c --- bind-9.3.2-orig/bin/named/dlz_mysql_driver.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/dlz_mysql_driver.c 2006-02-20 19:41:46.000000000 +0100 @@ -0,0 +1,1022 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ_MYSQL + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +dns_sdlzimplementation_t *dlz_mysql = NULL; + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define COUNTZONE 5 +#define LOOKUP 6 + +#define safeGet(in) in == NULL ? "" : in + +/*** + *** method prototypes + *** Declared here to shut up compiler warnings + *** about "no previous prototype" + ***/ + +char * +mysqldrv_escape_string(MYSQL *mysql, const char *instr); + +isc_result_t +mysql_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, MYSQL_RES **rs); + +isc_result_t +mysql_process_rs(dns_sdlzlookup_t *lookup, MYSQL_RES *rs); + +isc_result_t +mysql_findzone(void *driverarg, void *dbdata, const char *name); + +isc_result_t +mysql_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client); + +isc_result_t +mysql_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +isc_result_t +mysql_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup); + +isc_result_t +mysql_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +isc_result_t +mysql_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +void +mysql_destroy(void *driverarg, void *dbdata); + +/*** + *** Private methods + ***/ + + /* Allocates memory for a new string, and then constructs the new + * string by "escaping" the input string. The new string is + * safe to be used in queries. This is necessary because we cannot + * be sure of what types of strings are passed to us, and we don't + * want special characters in the string causing problems. + */ + +char * +mysqldrv_escape_string(MYSQL *mysql, const char *instr){ + + char *outstr; + unsigned int len; + + if(instr == NULL) + return NULL; + + len = strlen(instr); + + outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1); + if(outstr == NULL) + return NULL; + + mysql_real_escape_string(mysql, outstr, instr, len); + + return outstr; +} + + /* This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds a single database instance. + * The function will construct and run the query, hopefully getting + * a result set. + */ + +isc_result_t +mysql_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, MYSQL_RES **rs) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + unsigned int i = 0; + unsigned int j = 0; + int qres = 0; + + /* Extended Patch for Zone Extended Tag */ + char *n_zone_domain; + char *n_zone_tld; + char n_buffer[strlen(zone)+1]; + + if(query != COUNTZONE) + REQUIRE(*rs == NULL); + else + REQUIRE(rs == NULL); + + // get db instance / connection + dbi = (dbinstance_t *) dbdata; + + // if DBI is null, can't do anything else + if(dbi == NULL){ + result = ISC_R_FAILURE; + goto cleanup; + } + + // what type of query are we going to run? + switch(query){ + case ALLNODES: + // if the query was not passed in from the config file + // then we can't run it. return not_implemented, + // so it's like the code for that operation + // was never built into the driver.... AHHH flexibility!!! + if(dbi->allnodes_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case ALLOWXFR: + // same as comments as ALLNODES + if(dbi->allowxfr_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case AUTHORITY: + // same as comments as ALLNODES + if(dbi->authority_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case FINDZONE: + // this is required. It's the whole point of DLZ! + if(dbi->findzone_q == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for findzone. Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + case COUNTZONE: + // same as comments as ALLNODES + if(dbi->countzone_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case LOOKUP: + // this is required. It's also a major point of DLZ! + if(dbi->lookup_q == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for lookup. Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + default: + // this should never happen. If it does, the code is screwed up! + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to mysql_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + + // was a zone string passed? If so, make it safe for use in queries. + if(zone != NULL){ + dbi->zone = mysqldrv_escape_string((MYSQL *) dbi->dbconn, zone); + + // Extended Patch for Zone Extended Tag + snprintf(n_buffer, strlen(zone)+1, "%s", zone); + n_zone_domain = strtok(n_buffer, "."); + n_zone_tld = strtok(NULL, ""); + // + dbi->zone_domain = mysqldrv_escape_string((MYSQL *) dbi->dbconn, n_zone_domain); + dbi->zone_tld = mysqldrv_escape_string((MYSQL *) dbi->dbconn, n_zone_tld); + + if(dbi->zone == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + if(dbi->zone_domain == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + if(dbi->zone_tld == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->zone = NULL; + } + + // was a record string passed? If so, make it safe for use in queries. + if(record != NULL){ + dbi->record = mysqldrv_escape_string((MYSQL *) dbi->dbconn, record); + if(dbi->record == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->record = NULL; + } + + // was a client string passed? If so, make it safe for use in queries. + if(client != NULL){ + dbi->client = mysqldrv_escape_string((MYSQL *) dbi->dbconn, client); + if(dbi->client == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->client = NULL; + } + + // what type of query are we going to run? + // this time we build the actual query to run. + switch(query){ + case ALLNODES: + querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); + break; + case ALLOWXFR: + querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); + break; + case AUTHORITY: + querystring = build_querystring(ns_g_mctx, dbi->authority_q); + break; + case FINDZONE: + querystring = build_querystring(ns_g_mctx, dbi->findzone_q); + break; + case COUNTZONE: + querystring = build_querystring(ns_g_mctx, dbi->countzone_q); + break; + case LOOKUP: + querystring = build_querystring(ns_g_mctx, dbi->lookup_q); + break; + default: + // this should never happen. If it does, the code is screwed up! + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to mysql_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + // if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! + if(querystring == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + + // output the full query string during debug so we can see + // what lame error the query has. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "\nQuery String: %s\n", querystring); + + // attempt query up to 3 times. + for(i=0; i < 3; i++){ + qres = mysql_query((MYSQL *) dbi->dbconn, querystring); + if(qres == 0) + break; + for(j=0; mysql_ping((MYSQL *) dbi->dbconn) != 0 && j < 4; j++); + } + + if(qres == 0){ + result = ISC_R_SUCCESS; + if(query != COUNTZONE){ + *rs = mysql_store_result((MYSQL *) dbi->dbconn); + if(*rs == NULL) + result = ISC_R_FAILURE; + } + } else { + result = ISC_R_FAILURE; + } + + +cleanup: // it's always good to cleanup after yourself + + // if we couldn't even get DBI, just return NULL + if(dbi == NULL) + return ISC_R_FAILURE; + + // free dbi->zone string + if(dbi->zone != NULL) + isc_mem_free(ns_g_mctx, dbi->zone); + + // free dbi->zone_domain string + if(dbi->zone_domain != NULL) + isc_mem_free(ns_g_mctx, dbi->zone_domain); + + // free dbi->zone_tld string + if(dbi->zone_tld != NULL) + isc_mem_free(ns_g_mctx, dbi->zone_tld); + + // free dbi->record string + if(dbi->record != NULL) + isc_mem_free(ns_g_mctx, dbi->record); + + // free dbi->client string + if(dbi->client != NULL) + isc_mem_free(ns_g_mctx, dbi->client); + + // release query string + if(querystring != NULL) + isc_mem_free(ns_g_mctx, querystring); + + // return result + return result; +} + + /* The processing of result sets for lookup and authority are + * exactly the same. So that functionality has been moved + * into this function to minimize code. + */ + +isc_result_t +mysql_process_rs(dns_sdlzlookup_t *lookup, MYSQL_RES *rs) +{ + isc_result_t result = ISC_R_NOTFOUND; + MYSQL_ROW row; + unsigned int fields; + unsigned int j; + unsigned int len; + char *tmpString; + char *endp; + int ttl; + + row = mysql_fetch_row(rs); // get a row from the result set + fields = mysql_num_fields(rs); // how many columns in result set + while(row != NULL){ + switch(fields){ + case 1: // one column in rs, it's the data field + // use default type of A record, and default TTL of 86400 + result = dns_sdlz_putrr(lookup, "a", 86400, safeGet(row[0])); + break; + case 2: // two columns, data field, and data type. + // use default TTL of 86400 + result = dns_sdlz_putrr(lookup, safeGet(row[0]), 86400, safeGet(row[1])); + break; + case 3: // three columns, all data no defaults + // convert text to int, make sure it worked right + ttl = strtol(safeGet(row[0]), &endp, 10); + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver ttl must be a postive number"); + } + result = dns_sdlz_putrr(lookup, safeGet(row[1]), ttl, safeGet(row[2])); + break; + default: // more than 3 fields, concatonate the last ones together. + // figure out how long to make string + for(j=2, len=0; j < fields; j++){ + len += strlen(safeGet(row[j])) + 1; + } + // allocate string memory, allow for NULL to term string + tmpString = isc_mem_allocate(ns_g_mctx, len + 1); + if(tmpString == NULL){ // major bummer, need more ram + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to allocate memory for temporary string"); + mysql_free_result(rs); // can't use it now, get rid of it. + return (ISC_R_FAILURE); // Yeah, I'd say! + } + // copy field to tmpString + strcpy(tmpString, safeGet(row[2])); + + + // concat the rest of fields together, space between each one. + for(j=3; j < fields; j++){ + strcat(tmpString, " "); + strcat(tmpString, safeGet(row[j])); + } + // convert text to int, make sure it worked right + ttl = strtol(safeGet(row[0]), &endp, 10); + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver ttl must be a postive number"); + } + // ok, now tell Bind about it. + result = dns_sdlz_putrr(lookup, safeGet(row[1]), ttl, tmpString); + // done, get rid of this thing. + isc_mem_free(ns_g_mctx, tmpString); + } + // I sure hope we were successful + if(result != ISC_R_SUCCESS){ + mysql_free_result(rs); // nope, get rid of the Result set, and log a msg + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putrr returned error. Error code was: %s", + isc_result_totext(result)); + return (ISC_R_FAILURE); + } + row = mysql_fetch_row(rs); // get next row + } + + // free result set memory + mysql_free_result(rs); + + // return result code + return result; +} + +/*** + *** SDLZ interface methods + ***/ + + /* determine if the zone is supported by (in) the database */ + +isc_result_t +mysql_findzone(void *driverarg, void *dbdata, const char *name) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + my_ulonglong rows; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = mysql_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &rs); + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS || rs == NULL){ + if(rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return result set for findzone query"); + return (ISC_R_FAILURE); + } + // count how many rows in result set + rows = mysql_num_rows(rs); + // get rid of result set, we are done with it. + mysql_free_result(rs); + + // if we returned any rows, zone is supported. + if(rows > 0){ + mysql_get_resultset(name, NULL, NULL, COUNTZONE, dbdata, NULL); + return (ISC_R_SUCCESS); + } + + // no rows returned, zone is not supported. + return (ISC_R_NOTFOUND); +} + + /* Determine if the client is allowed to perform a zone transfer */ +isc_result_t +mysql_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + my_ulonglong rows; + + UNUSED(driverarg); + + // first check if the zone is supported by the database. + result = mysql_findzone(driverarg, dbdata, name); + if(result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + // if we get to this point we know the zone is supported by the database + // the only questions now are is the zone transfer is allowed for this client + // and did the config file have an allow zone xfr query + + // Run our query, and get a result set from the database. + result = mysql_get_resultset(name, NULL, client, ALLOWXFR, dbdata, &rs); + // if we get "not implemented", send it along. + if(result == ISC_R_NOTIMPLEMENTED) + return result; + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS || rs == NULL){ + if(rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return result set for allow xfr query"); + return (ISC_R_FAILURE); + } + // count how many rows in result set + rows = mysql_num_rows(rs); + // get rid of result set, we are done with it. + mysql_free_result(rs); + + // if we returned any rows, zone xfr is allowed. + if(rows > 0) + return (ISC_R_SUCCESS); + + // no rows returned, zone xfr not allowed + return (ISC_R_NOPERM); +} + + /* If the client is allowed to perform a zone transfer, the next order of + * business is to get all the nodes in the zone, so bind can respond to the + * query. + */ +isc_result_t +mysql_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + MYSQL_ROW row; + unsigned int fields; + unsigned int j; + unsigned int len; + char *tmpString; + char *endp; + int ttl; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = mysql_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &rs); + // if we get "not implemented", send it along + if(result == ISC_R_NOTIMPLEMENTED) + return result; + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + if(rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return result set for all nodes query"); + return (ISC_R_FAILURE); + } + + result = ISC_R_NOTFOUND; + + row = mysql_fetch_row(rs); // get a row from the result set + fields = mysql_num_fields(rs); // how many columns in result set + while(row != NULL){ + if(fields < 4){ // gotta have at least 4 columns + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver too few fields returned by all nodes query"); + } + // convert text to int, make sure it worked right + ttl = strtol(safeGet(row[0]), &endp, 10); + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver ttl must be a postive number"); + } + if(fields == 4){ + // tell Bind about it. + result = dns_sdlz_putnamedrr(allnodes, safeGet(row[0]), + safeGet(row[1]), ttl, safeGet(row[3])); + } else { // more than 4 fields, concatonat the last ones together. + // figure out how long to make string + for(j=3, len=0; j < fields; j++){ + len += strlen(safeGet(row[j])) + 1; + } + // allocate memory, allow for NULL to term string + tmpString = isc_mem_allocate(ns_g_mctx, len + 1); + if(tmpString == NULL){ // we need more ram. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to allocate memory for temporary string"); + mysql_free_result(rs); + return (ISC_R_FAILURE); + } + // copy this field to tmpString + strcpy(tmpString, safeGet(row[3])); + // concatonate the rest, with spaces between + for(j=4; j < fields; j++){ + strcat(tmpString, " "); + strcat(tmpString, safeGet(row[j])); + } + // tell Bind about it. + result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]), safeGet(row[1]), ttl, tmpString); + isc_mem_free(ns_g_mctx, tmpString); + } + // if we weren't successful, log err msg + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putnamedrr returned error. Error code was: %s", + isc_result_totext(result)); + result = ISC_R_FAILURE; + break; + } + row = mysql_fetch_row(rs); // get next row from the result set + } + + // free result set memory + mysql_free_result(rs); + + return result; +} + + /* if the lookup function does not return SOA or NS records for the zone, + * use this function to get that information for Bind. + */ + +isc_result_t +mysql_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = mysql_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &rs); + // if we get "not implemented", send it along + if(result == ISC_R_NOTIMPLEMENTED) + return result; + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + if(rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return result set for authority query"); + return (ISC_R_FAILURE); + } + // lookup and authority result sets are processed in the same manner + // mysql_process_rs does the job for both functions. + return mysql_process_rs(lookup, rs); +} + + /* if zone is supported, lookup up a (or multiple) record(s) in it */ +isc_result_t +mysql_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = mysql_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs); + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + if(rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return result set for lookup query"); + return (ISC_R_FAILURE); + } + // lookup and authority result sets are processed in the same manner + // mysql_process_rs does the job for both functions. + return mysql_process_rs(lookup, rs); +} + + /* create an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement which is + * passed into all query functions. + */ +isc_result_t +mysql_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + char *tmp = NULL; + char *dbname = NULL; + char *host = NULL; + char *user = NULL; + char *pass = NULL; + char *socket = NULL; + int port; + MYSQL *dbc; + char *endp; + int j; + unsigned int flags = 0; + + UNUSED(driverarg); + UNUSED(dlzname); + + /* verify we have at least 4 arg's passed to the driver */ + if(argc < 4){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver requires at least 4 command line args."); + return (ISC_R_FAILURE); + } + + /* no more than 8 arg's should be passed to the driver */ + if(argc > 8){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver cannot accept more than 7 command line args."); + return (ISC_R_FAILURE); + } + + /* parse connection string and get paramters. */ + + // get db name - required + dbname = getParameterValue(argv[1], "dbname="); + if(dbname == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver requires a dbname parameter."); + result = ISC_R_FAILURE; + goto full_cleanup; + } + + // get db port. Not required, but must be > 0 if specified + tmp = getParameterValue(argv[1], "port="); + if(tmp == NULL){ + port = 0; + } else { + port = strtol(tmp, &endp, 10); + if (*endp != '\0' || port < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Mysql driver port must be a positive number."); + isc_mem_free(ns_g_mctx, tmp); + result = ISC_R_FAILURE; + goto full_cleanup; + } + isc_mem_free(ns_g_mctx, tmp); + } + + // how many queries were passed in from config file? + switch(argc){ + case 4: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, NULL, + argv[2], argv[3], NULL, &dbi); + break; + case 5: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, argv[4], + argv[2], argv[3], NULL, &dbi); + break; + case 6: + result = build_sqldbinstance(ns_g_mctx, argv[5], NULL, argv[4], + argv[2], argv[3], NULL, &dbi); + break; + case 7: + result = build_sqldbinstance(ns_g_mctx, argv[5], argv[6], argv[4], + argv[2], argv[3], NULL, &dbi); + break; + case 8: + result = build_sqldbinstance(ns_g_mctx, argv[5], argv[6], argv[4], + argv[2], argv[3], argv[7], &dbi); + break; + default: + result = ISC_R_FAILURE; // not really needed, should shut up compiler. + } + + // unsuccessful?, log err msg and cleanup. + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver could not create database instance object."); + result = ISC_R_FAILURE; + goto full_cleanup; + } + + // create and set db connection + dbi->dbconn = mysql_init(NULL); + + // if db connection cannot be created, log err msg and cleanup. + if(dbi->dbconn == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver could not allocate memory for database connection"); + result = ISC_R_FAILURE; + goto full_cleanup; + } + + tmp = getParameterValue(argv[1], "compress="); + if(tmp != NULL){ + if(strcasecmp(tmp, "true") == 0) + flags = CLIENT_COMPRESS; + isc_mem_free(ns_g_mctx, tmp); + } + + tmp = getParameterValue(argv[1], "ssl="); + if(tmp != NULL){ + if(strcasecmp(tmp, "true") == 0) + flags = flags | CLIENT_SSL; + isc_mem_free(ns_g_mctx, tmp); + } + + tmp = getParameterValue(argv[1], "space="); + if(tmp != NULL){ + if(strcasecmp(tmp, "ignore") == 0) + flags = flags | CLIENT_IGNORE_SPACE; + isc_mem_free(ns_g_mctx, tmp); + } + + dbc = NULL; + host = getParameterValue(argv[1], "host="); + user = getParameterValue(argv[1], "user="); + pass = getParameterValue(argv[1], "pass="); + socket = getParameterValue(argv[1], "socket="); + + for(j=0; dbc == NULL && j < 4; j++) + dbc = mysql_real_connect((MYSQL *) dbi->dbconn, host, user, pass, + dbname, port, socket, flags); + + // let user know if we couldn't connect. + if(dbc == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver failed to create database connection after 4 attempts"); + result = ISC_R_FAILURE; + goto full_cleanup; + } + + // return db connection via dbdata + *dbdata = dbi; + + result = ISC_R_SUCCESS; + goto cleanup; + +full_cleanup: + + destroy_sqldbinstance(dbi); + +cleanup: + + if(dbname != NULL) + isc_mem_free(ns_g_mctx, dbname); + if(host != NULL) + isc_mem_free(ns_g_mctx, host); + if(user != NULL) + isc_mem_free(ns_g_mctx, user); + if(pass != NULL) + isc_mem_free(ns_g_mctx, pass); + if(socket != NULL) + isc_mem_free(ns_g_mctx, socket); + + + return result; +} + + /* destroy the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement. + * so we really only need to clean it up since we are not using driverarg. + */ + +void +mysql_destroy(void *driverarg, void *dbdata) +{ + dbinstance_t *dbi; + + UNUSED(driverarg); + + dbi = (dbinstance_t *) dbdata; + + // release DB connection + if(dbi->dbconn != NULL) + mysql_close((MYSQL *) dbi->dbconn); + + // destroy DB instance + destroy_sqldbinstance(dbi); +} + + // pointers to all our runtime methods. + // this is used during driver registration + // i.e. in dlz_mysql_init below. +static dns_sdlzmethods_t dlz_mysql_methods = { + mysql_create, + mysql_destroy, + mysql_findzone, + mysql_lookup, + mysql_authority, + mysql_allnodes, + mysql_allowzonexfr}; + +/* + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_mysql_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ mysql driver."); + + /* Driver is always threadsafe. Because of the way MySQL handles + * threads the MySQL driver can only be used when bind is run single + * threaded. Using MySQL with Bind running multi-threaded is not + * allowed. When using the MySQL driver "-n1" should always be + * passed to Bind to guarantee single threaded operation. + */ + result = dns_sdlzregister("mysql", &dlz_mysql_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA + | DNS_SDLZFLAG_THREADSAFE, ns_g_mctx, &dlz_mysql); + // if we can't register the driver, there are big problems. + if(result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_mysql_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ mysql driver."); + + // unregister the driver. + if (dlz_mysql != NULL) + dns_sdlzunregister(&dlz_mysql); +} + +#endif + diff -Nuar bind-9.3.2-orig/bin/named/dlz_odbc_driver.c bind-9.3.2-mod/bin/named/dlz_odbc_driver.c --- bind-9.3.2-orig/bin/named/dlz_odbc_driver.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/dlz_odbc_driver.c 2006-02-20 19:41:46.000000000 +0100 @@ -0,0 +1,1539 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ_ODBC + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +dns_sdlzimplementation_t *dlz_odbc = NULL; + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define LOOKUP 5 + +#define sqlOK(a) ((a == SQL_SUCCESS || a == SQL_SUCCESS_WITH_INFO) ? -1 : 0) + +/*** + *** Private Structures + ***/ + +/*** + *** structure to hold ODBC connection & statement + ***/ + +typedef struct{ + SQLHDBC dbc; + SQLHSTMT stmnt; +} odbc_db_t; + +/*** + *** Structure to hold everthing needed by this "instance" of the odbc driver + *** remember, the driver code is only loaded once, but may have many separate + *** instances + ***/ + +typedef struct { + +#ifdef ISC_PLATFORM_USETHREADS + + db_list_t *db; // handle to a list of DB + +#else + + dbinstance_t *db; // handle to db + +#endif + + SQLHENV sql_env; // handle to SQL environment + SQLCHAR *dsn; + SQLCHAR *user; + SQLCHAR *pass; +} odbc_instance_t; + +/*** + *** method prototypes + *** Declared here to shut up compiler warnings + *** about "no previous prototype" + ***/ + +void +destroy_odbc_instance(odbc_instance_t *odbc_inst); + +dbinstance_t * +odbc_find_avail_conn(db_list_t *dblist); + +char * +odbc_escape_string(const char *instr); + +size_t +odbc_makesafe(char *to, const char *from, size_t length); + +isc_result_t +odbc_connect(odbc_instance_t *dbi, odbc_db_t **dbc); + +isc_result_t +odbc_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, dbinstance_t **r_dbi); + +isc_result_t +odbc_getField(SQLHSTMT *stmnt, SQLSMALLINT field, char **data); + +isc_result_t +odbc_getManyFields(SQLHSTMT *stmnt, SQLSMALLINT startField, + SQLSMALLINT endField, char **retData); + +isc_result_t +odbc_process_rs(dns_sdlzlookup_t *lookup, dbinstance_t *r_dbi); + +isc_result_t +odbc_findzone(void *driverarg, void *dbdata, const char *name); + +isc_result_t +odbc_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client); + +isc_result_t +odbc_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +isc_result_t +odbc_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup); + +isc_result_t +odbc_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +isc_result_t +odbc_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +void +odbc_destroy(void *driverarg, void *dbdata); + +SQLSMALLINT +safeLen(void *a); + +/*** + *** Private methods + ***/ + +SQLSMALLINT +safeLen(void *a){ + if(a == NULL) + return 0; + return strlen((char *) a); +} + + /* propertly cleans up an odbc_instance_t */ + +void +destroy_odbc_instance(odbc_instance_t *odbc_inst){ + +#ifdef ISC_PLATFORM_USETHREADS + + dbinstance_t *ndbi = NULL; + dbinstance_t *dbi = NULL; + + // get the first DBI in the list + ndbi = ISC_LIST_HEAD(*odbc_inst->db); + + // loop through the list + while(ndbi != NULL){ + dbi = ndbi; + // get the next DBI in the list + ndbi = ISC_LIST_NEXT(dbi, link); + + // if we have a connection / statement object in memory + if(dbi->dbconn != NULL){ + // free statement handle + if(((odbc_db_t *) (dbi->dbconn))->stmnt != NULL){ + SQLFreeHandle(SQL_HANDLE_STMT,((odbc_db_t *)(dbi->dbconn))->stmnt); + ((odbc_db_t *) (dbi->dbconn))->stmnt = NULL; + } + + // disconnect from database & free connection handle + if(((odbc_db_t *) (dbi->dbconn))->dbc != NULL){ + SQLDisconnect(((odbc_db_t *) dbi->dbconn)->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, ((odbc_db_t *) (dbi->dbconn))->dbc); + ((odbc_db_t *) (dbi->dbconn))->dbc = NULL; + } + + // free memory that held connection & statement. + isc_mem_free(ns_g_mctx, dbi->dbconn); + } + // release all memory that comprised a DBI + destroy_sqldbinstance(dbi); + } + // release memory for the list structure + isc_mem_put(ns_g_mctx, odbc_inst->db, sizeof(db_list_t)); + +#else /* ISC_PLATFORM_USETHREADS */ + + // free statement handle + if(((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt != NULL){ + SQLFreeHandle(SQL_HANDLE_STMT, ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt); + ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt = NULL; + } + + // disconnect from database, free connection handle + if(((odbc_db_t *) (odbc_inst->db->dbconn))->dbc != NULL){ + SQLDisconnect(((odbc_db_t *) (odbc_inst->db->dbconn))->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc); + ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc = NULL; + } + // free mem for the odbc_db_t structure held in db + if(((odbc_db_t *) odbc_inst->db->dbconn) != NULL){ + isc_mem_free(ns_g_mctx, odbc_inst->db->dbconn); + odbc_inst->db->dbconn = NULL; + } + + if(odbc_inst->db != NULL) + destroy_sqldbinstance(odbc_inst->db); + +#endif /* ISC_PLATFORM_USETHREADS */ + + + // free sql environment + if(odbc_inst->sql_env != NULL) + SQLFreeHandle(SQL_HANDLE_ENV, odbc_inst->sql_env); + + // free ODBC instance strings + if(odbc_inst->dsn != NULL) + isc_mem_free(ns_g_mctx, odbc_inst->dsn); + if(odbc_inst->pass != NULL) + isc_mem_free(ns_g_mctx, odbc_inst->pass); + if(odbc_inst->user != NULL) + isc_mem_free(ns_g_mctx, odbc_inst->user); + + // free memory for odbc_inst + if(odbc_inst != NULL) + isc_mem_put(ns_g_mctx, odbc_inst, sizeof(odbc_instance_t)); + +} + + /* Connects to database, and creates ODBC statements */ + +isc_result_t +odbc_connect(odbc_instance_t *dbi, odbc_db_t **dbc){ + + odbc_db_t *ndb = *dbc; + SQLRETURN sqlRes; + isc_result_t result = ISC_R_SUCCESS; + + if(ndb != NULL){ // if db != null, we have to do some cleanup + // if statement handle != null free it + if(ndb->stmnt != NULL){ + SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt); + ndb->stmnt = NULL; + } + + // if connection handle != null free it + if(ndb->dbc != NULL){ + SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc); + ndb->dbc = NULL; + } + } else { + ndb = isc_mem_allocate(ns_g_mctx, sizeof(odbc_db_t)); + if(ndb == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to allocate memory"); + return ISC_R_NOMEMORY; + } + memset(ndb, 0, sizeof(odbc_db_t)); + } + + sqlRes = SQLAllocHandle(SQL_HANDLE_DBC, dbi->sql_env, &(ndb->dbc)); + if(!sqlOK(sqlRes)){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to allocate memory"); + result = ISC_R_NOMEMORY; + goto cleanup; + } + + sqlRes = SQLConnect(ndb->dbc, dbi->dsn, safeLen(dbi->dsn), dbi->user, + safeLen(dbi->user), dbi->pass, safeLen(dbi->pass)); + if(!sqlOK(sqlRes)){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to connect"); + result = ISC_R_FAILURE; + goto cleanup; + } + + sqlRes = SQLAllocHandle(SQL_HANDLE_STMT, ndb->dbc, &(ndb->stmnt)); + if(!sqlOK(sqlRes)){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to allocate memory"); + result = ISC_R_NOMEMORY; + goto cleanup; + } + + *dbc = ndb; + + return ISC_R_SUCCESS; + +cleanup: + + if(ndb != NULL){ + + // if statement handle != null free it + if(ndb->stmnt != NULL){ + SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt); + ndb->stmnt = NULL; + } + + // if connection handle != null free it + if(ndb->dbc != NULL){ + SQLDisconnect(ndb->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc); + ndb->dbc = NULL; + } + // free memory holding ndb + isc_mem_free(ns_g_mctx, ndb); + } + + return result; +} + + /* Loops through the list of DB instances, attempting to lock + * on the mutex. If successful, the DBI is reserved for use + * and the thread can perform queries against the database. + * If the lock fails, the next one in the list is tried. + * looping continues until a lock is obtained, or until + * the list has been searched dbc_search_limit times. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ + +#ifdef ISC_PLATFORM_USETHREADS + +dbinstance_t * +odbc_find_avail_conn(db_list_t *dblist) +{ + dbinstance_t *dbi = NULL; + dbinstance_t *head; + int count = 0; + + // get top of list + head = dbi = ISC_LIST_HEAD(*dblist); + + // loop through list + while(count < dbc_search_limit){ + // try to lock on the mutex + if(isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS) + return dbi; // success, return the DBI for use. + + // not successful, keep trying + dbi = ISC_LIST_NEXT(dbi, link); + + // check to see if we have gone to the top of the list. + if(dbi == NULL){ + count++; + dbi = head; + } + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Odbc driver unable to find available connection after searching %d times", + count); + return NULL; +} + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* Allocates memory for a new string, and then constructs the new + * string by "escaping" the input string. The new string is + * safe to be used in queries. This is necessary because we cannot + * be sure of what types of strings are passed to us, and we don't + * want special characters in the string causing problems. + */ + +char * +odbc_escape_string(const char *instr){ + + char *outstr; + unsigned int len; + + if(instr == NULL) + return NULL; + + len = strlen(instr); + + outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1); + if(outstr == NULL) + return NULL; + + odbc_makesafe(outstr, instr, len); + + return outstr; +} + +/* --------------- + * Escaping arbitrary strings to get valid SQL strings/identifiers. + * + * Replaces "\\" with "\\\\" and "'" with "''". + * length is the length of the buffer pointed to by + * from. The buffer at to must be at least 2*length + 1 characters + * long. A terminating NUL character is written. + * + * NOTICE!!! + * This function was borrowed directly from PostgreSQL's libpq. + * + * The copyright statements from the original file containing this + * function are included below: + * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * --------------- + */ + +size_t +odbc_makesafe(char *to, const char *from, size_t length) +{ + const char *source = from; + char *target = to; + unsigned int remaining = length; + + while (remaining > 0) + { + switch (*source) + { + case '\\': + *target = '\\'; + target++; + *target = '\\'; + /* target and remaining are updated below. */ + break; + + case '\'': + *target = '\''; + target++; + *target = '\''; + /* target and remaining are updated below. */ + break; + + default: + *target = *source; + /* target and remaining are updated below. */ + } + source++; + target++; + remaining--; + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} + + /* This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds either: + * 1) a list of database instances (in multithreaded mode) OR + * 2) a single database instance (in single threaded mode) + * The function will construct the query and obtain an available + * database instance (DBI). It will then run the query and hopefully + * obtain a result set. The data base instance that is used is returned + * to the caller so they can get the data from the result set from it. + * If successfull, it will be the responsibility of the caller to close + * the cursor, and unlock the mutex of the DBI when they are done with it. + * If not successfull, this function will perform all the cleanup. + */ + + +isc_result_t +odbc_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, dbinstance_t **r_dbi) +{ + + isc_result_t result; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + unsigned int j = 0; + SQLRETURN sqlRes; + + /* Extended Patch for Zone Extended Tag */ + char *n_zone_domain; + char *n_zone_tld; + char n_buffer[strlen(zone)+1]; + + REQUIRE(*r_dbi == NULL); + + // get db instance / connection +#ifdef ISC_PLATFORM_USETHREADS + + // find an available DBI from the list + dbi = odbc_find_avail_conn(((odbc_instance_t *) dbdata)->db); + +#else /* ISC_PLATFORM_USETHREADS */ + + // only 1 DBI - no need to lock instance lock either + // only 1 thread in the whole process, no possible contention. + dbi = (dbinstance_t *) ((odbc_instance_t *) dbdata)->db; + +#endif /* ISC_PLATFORM_USETHREADS */ + + // if DBI is null, can't do anything else + if(dbi == NULL){ + result = ISC_R_FAILURE; + goto cleanup; + } + + // what type of query are we going to run? + switch(query){ + case ALLNODES: + // if the query was not passed in from the config file + // then we can't run it. return not_implemented, + // so it's like the code for that operation + // was never built into the driver.... AHHH flexibility!!! + if(dbi->allnodes_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case ALLOWXFR: + // same as comments as ALLNODES + if(dbi->allowxfr_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case AUTHORITY: + // same as comments as ALLNODES + if(dbi->authority_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case FINDZONE: + // this is required. It's the whole point of DLZ! + if(dbi->findzone_q == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for findzone. Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + case LOOKUP: + // this is required. It's also a major point of DLZ! + if(dbi->lookup_q == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for lookup. Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + default: + // this should never happen. If it does, the code is screwed up! + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to odbc_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + + // was a zone string passed? If so, make it safe for use in queries. + if(zone != NULL){ + dbi->zone = odbc_escape_string(zone); + + // Extended Patch for Zone Extended Tag + snprintf(n_buffer, strlen(zone)+1, "%s", zone); + n_zone_domain = strtok(n_buffer, "."); + n_zone_tld = strtok(NULL, ""); + // + dbi->zone_domain = odbc_escape_string(n_zone_domain); + dbi->zone_tld = odbc_escape_string(n_zone_tld); + + if(dbi->zone == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + if(dbi->zone_domain == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + if(dbi->zone_tld == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->zone = NULL; + } + + // was a record string passed? If so, make it safe for use in queries. + if(record != NULL){ + dbi->record = odbc_escape_string(record); + if(dbi->record == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->record = NULL; + } + + // was a client string passed? If so, make it safe for use in queries. + if(client != NULL){ + dbi->client = odbc_escape_string(client); + if(dbi->client == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->client = NULL; + } + + // what type of query are we going to run? + // this time we build the actual query to run. + switch(query){ + case ALLNODES: + querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); + break; + case ALLOWXFR: + querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); + break; + case AUTHORITY: + querystring = build_querystring(ns_g_mctx, dbi->authority_q); + break; + case FINDZONE: + querystring = build_querystring(ns_g_mctx, dbi->findzone_q); + break; + case LOOKUP: + querystring = build_querystring(ns_g_mctx, dbi->lookup_q); + break; + default: + // this should never happen. If it does, the code is screwed up! + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to odbc_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + // if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! + if(querystring == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + + // output the full query string during debug so we can see + // what lame error the query has. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "\nQuery String: %s\n", querystring); + + // attempt query up to 3 times. + for(j=0; j < 3; j++){ + // try to get result set + sqlRes = SQLExecDirect(((odbc_db_t *) dbi->dbconn)->stmnt, + (SQLCHAR *) querystring, (SQLINTEGER) strlen(querystring)); + + // if error, reset DB connection + if(!sqlOK(sqlRes)){ + // close cursor + SQLCloseCursor(((odbc_db_t *) dbi->dbconn)->stmnt); + // attempt to reconnect + result = odbc_connect((odbc_instance_t *) dbdata, (odbc_db_t **) &(dbi->dbconn)); + // check if we reconnected + if(result != ISC_R_SUCCESS) + break; + // incase this is the last time through the loop + result = ISC_R_FAILURE; + } else { + result = ISC_R_SUCCESS; + // return dbi + *r_dbi = dbi; + // result set ok, break loop + break; + } + } // end for loop + +cleanup: // it's always good to cleanup after yourself + + // if we couldn't even allocate DBI, just return NULL + if(dbi == NULL) + return ISC_R_FAILURE; + + // free dbi->zone string + if(dbi->zone != NULL) + isc_mem_free(ns_g_mctx, dbi->zone); + + // free dbi->zone_domain string + if(dbi->zone_domain != NULL) + isc_mem_free(ns_g_mctx, dbi->zone_domain); + + // free dbi->zone_tld string + if(dbi->zone_tld != NULL) + isc_mem_free(ns_g_mctx, dbi->zone_tld); + + // free dbi->record string + if(dbi->record != NULL) + isc_mem_free(ns_g_mctx, dbi->record); + + // free dbi->client string + if(dbi->client != NULL) + isc_mem_free(ns_g_mctx, dbi->client); + +#ifdef ISC_PLATFORM_USETHREADS + + // if we are done using this dbi, release the lock + if(result != ISC_R_SUCCESS) + isc_mutex_unlock(&dbi->instance_lock); + +#endif /* ISC_PLATFORM_USETHREADS */ + + // release query string + if(querystring != NULL) + isc_mem_free(ns_g_mctx, querystring ); + + // return result + return result; + +} + + /* Gets a single field from the ODBC statement. The memory for the + * returned data is dynamically allocated. If this method is successful + * it is the reponsibility of the caller to free the memory using + * isc_mem_free(ns_g_mctx, *ptr); + */ + +isc_result_t +odbc_getField(SQLHSTMT *stmnt, SQLSMALLINT field, char **data){ + + SQLINTEGER size; + + REQUIRE(data != NULL && *data == NULL); + + if(sqlOK(SQLColAttribute(stmnt, field, SQL_DESC_DISPLAY_SIZE, + NULL, 0, NULL, &size)) && size > 0){ + *data = isc_mem_allocate(ns_g_mctx, size + 1); + if(data != NULL){ + if(sqlOK(SQLGetData(stmnt, field, SQL_C_CHAR, *data, size + 1,&size))) + return ISC_R_SUCCESS; + isc_mem_free(ns_g_mctx, *data); + } + } + return ISC_R_FAILURE; +} + + /* Gets multiple fields from the ODBC statement. The memory for the + * returned data is dynamically allocated. If this method is successful + * it is the reponsibility of the caller to free the memory using + * isc_mem_free(ns_g_mctx, *ptr); + */ + +isc_result_t +odbc_getManyFields(SQLHSTMT *stmnt, SQLSMALLINT startField, + SQLSMALLINT endField, char **retData){ + + isc_result_t result; + SQLINTEGER size; + int totSize = 0; + SQLSMALLINT i; + int j = 0; + char *data; + + REQUIRE(retData != NULL && *retData == NULL); + REQUIRE(startField > 0 && startField <= endField); + + // determine how large the data is + for(i=startField; i <= endField; i++) + if(sqlOK(SQLColAttribute(stmnt, i, SQL_DESC_DISPLAY_SIZE, + NULL, 0, NULL, &size)) && size > 0){ + totSize += (size + 1); // always allow for a " " (space) character + // after the data item + } + + if(totSize < 1) + return ISC_R_FAILURE; + + // allow for a "\n" at the end of the string/ + data = isc_mem_allocate(ns_g_mctx, ++totSize); + if(data == NULL) + return ISC_R_NOMEMORY; + + result = ISC_R_FAILURE; + + // get the data and concat all fields into a large string + for(i=startField; i <= endField; i++){ + if(sqlOK(SQLGetData(stmnt, i, SQL_C_CHAR, &(data[j]), totSize - j, &size))){ + if(size > 0){ + j += size; + data[j++] = ' '; + data[j] = '\0'; + result = ISC_R_SUCCESS; + } + } else { + isc_mem_free(ns_g_mctx, data); + return ISC_R_FAILURE; + } + } + + if(result != ISC_R_SUCCESS){ + isc_mem_free(ns_g_mctx, data); + return result; + } + + *retData = data; + return ISC_R_SUCCESS; + +} + + /* The processing of result sets for lookup and authority are + * exactly the same. So that functionality has been moved + * into this function to minimize code. + */ + +isc_result_t +odbc_process_rs(dns_sdlzlookup_t *lookup, dbinstance_t *dbi) +{ + + + isc_result_t result; + SQLSMALLINT fields; + SQLHSTMT *stmnt; + char *ttl_s; + char *type; + char *data; + char *endp; + int ttl; + + REQUIRE(dbi != NULL); + + stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt; + + // get number of columns + if(!sqlOK(SQLNumResultCols(stmnt, &fields))){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to process result set"); + result = ISC_R_FAILURE; + goto process_rs_cleanup; + } + + // get things ready for processing + result = ISC_R_FAILURE; + + while(sqlOK(SQLFetch(stmnt))){ + + // set to null for next pass through + data = type = ttl_s = NULL; + + switch(fields){ + case 1: // one column in rs, it's the data field + // use default type of A record, and default TTL of 86400 + + // attempt to get data, & tell bind about it + if((result = odbc_getField(stmnt, 1, &data)) == ISC_R_SUCCESS){ + result = dns_sdlz_putrr(lookup, "a", 86400, data); + } + break; + case 2: // two columns, data field, and data type. + // use default TTL of 86400 + // attempt to get DNS type & data, then tell bind about it + if((result = odbc_getField(stmnt, 1, &type)) == ISC_R_SUCCESS && + (result = odbc_getField(stmnt, 2, &data)) == ISC_R_SUCCESS){ + result = dns_sdlz_putrr(lookup, type, 86400, data); + } + break; + default: // 3 fields or more, concatonate the last ones together. + // attempt to get DNS ttl, type, data then tell Bind about them + if((result = odbc_getField(stmnt, 1, &ttl_s)) == ISC_R_SUCCESS && + (result = odbc_getField(stmnt, 2, &type)) == ISC_R_SUCCESS && + (result = odbc_getManyFields(stmnt, 3, fields, &data)) + == ISC_R_SUCCESS){ + // try to convert ttl string to int + ttl = strtol(ttl_s, &endp, 10); + // failure converting ttl. + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver ttl must be a postive number"); + result = ISC_R_FAILURE; + } else { + // successful converting TTL, tell Bind everything + result = dns_sdlz_putrr(lookup, type, ttl, data); + } + } // closes bid if() + } // closes switch(fields) + + // clean up mem + if(ttl_s != NULL) + isc_mem_free(ns_g_mctx, ttl_s); + if(type != NULL) + isc_mem_free(ns_g_mctx, type); + if(data != NULL) + isc_mem_free(ns_g_mctx, data); + + // I sure hope we were successful + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putrr returned error. Error code was: %s", + isc_result_totext(result)); + result = ISC_R_FAILURE; + goto process_rs_cleanup; + } + } // closes while loop + +process_rs_cleanup: + + // close cursor + SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); + +#ifdef ISC_PLATFORM_USETHREADS + + // free lock on dbi so someone else can use it. + isc_mutex_unlock(&dbi->instance_lock); + +#endif + + return result; +} + +/*** + *** SDLZ interface methods + ***/ + + /* determine if the zone is supported by (in) the database */ + +isc_result_t +odbc_findzone(void *driverarg, void *dbdata, const char *name) +{ + + isc_result_t result; + dbinstance_t *dbi = NULL; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + // if result != ISC_R_SUCCESS cursor and mutex already cleaned up. + // so we don't have to do it here. + result = odbc_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &dbi); + + // Check that we got a result set with data + if(result == ISC_R_SUCCESS && + !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))){ + result = ISC_R_NOTFOUND; + } + + if(dbi != NULL){ + // get rid of result set, we are done with it. + SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); + +#ifdef ISC_PLATFORM_USETHREADS + + // free lock on dbi so someone else can use it. + isc_mutex_unlock(&dbi->instance_lock); +#endif + } + + return result; +} + + /* Determine if the client is allowed to perform a zone transfer */ +isc_result_t +odbc_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + + UNUSED(driverarg); + + // first check if the zone is supported by the database. + result = odbc_findzone(driverarg, dbdata, name); + if(result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + // if we get to this point we know the zone is supported by the database + // the only questions now are is the zone transfer is allowed for this client + // and did the config file have an allow zone xfr query + + // Run our query, and get a result set from the database. + // if result != ISC_R_SUCCESS cursor and mutex already cleaned up. + // so we don't have to do it here. + result = odbc_get_resultset(name, NULL, client, ALLOWXFR, dbdata, &dbi); + + // if we get "not implemented", send it along. + if(result == ISC_R_NOTIMPLEMENTED) + return result; + + // Check that we got a result set with data + if(result == ISC_R_SUCCESS && + !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))){ + result = ISC_R_NOPERM; + } + + if(dbi != NULL){ + // get rid of result set, we are done with it. + SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); + +#ifdef ISC_PLATFORM_USETHREADS + + // free lock on dbi so someone else can use it. + isc_mutex_unlock(&dbi->instance_lock); +#endif + + } + + return result; +} + + /* If the client is allowed to perform a zone transfer, the next order of + * business is to get all the nodes in the zone, so bind can respond to the + * query. + */ + +isc_result_t +odbc_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + + isc_result_t result; + dbinstance_t *dbi = NULL; + SQLHSTMT *stmnt; + SQLSMALLINT fields; + char *data; + char *type; + char *ttl_s; + int ttl; + char *host; + char *endp; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = odbc_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &dbi); + + // if we get "not implemented", send it along + if(result == ISC_R_NOTIMPLEMENTED) + return result; + + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to return result set for all nodes query"); + return (ISC_R_FAILURE); + } + + stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt; + + // get number of columns + if(!sqlOK(SQLNumResultCols(stmnt, &fields))){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to process result set"); + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + if(fields < 4){ // gotta have at least 4 columns + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver too few fields returned by all nodes query"); + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + // get things ready for processing + result = ISC_R_FAILURE; + + while(sqlOK(SQLFetch(stmnt))){ + + // set to null for next pass through + data = host = type = ttl_s = NULL; + + // attempt to get DNS ttl, type, host, data then tell Bind about them + if((result = odbc_getField(stmnt, 1, &ttl_s)) == ISC_R_SUCCESS && + (result = odbc_getField(stmnt, 2, &type)) == ISC_R_SUCCESS && + (result = odbc_getField(stmnt, 3, &host)) == ISC_R_SUCCESS && + (result = odbc_getManyFields(stmnt, 4, fields,&data))==ISC_R_SUCCESS){ + // convert ttl string to int + ttl = strtol(ttl_s, &endp, 10); + // failure converting ttl. + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver ttl must be a postive number"); + result = ISC_R_FAILURE; + } else { + // successful converting TTL, tell Bind + result = dns_sdlz_putnamedrr(allnodes, host, type, ttl, data); + } + } // closes big if() + + // clean up mem + if(ttl_s != NULL) + isc_mem_free(ns_g_mctx, ttl_s); + if(type != NULL) + isc_mem_free(ns_g_mctx, type); + if(host != NULL) + isc_mem_free(ns_g_mctx, host); + if(data != NULL) + isc_mem_free(ns_g_mctx, data); + + // if we weren't successful, log err msg + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putnamedrr returned error. Error code was: %s", + isc_result_totext(result)); + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + } // closes while loop + +allnodes_cleanup: + + // close cursor + SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); + +#ifdef ISC_PLATFORM_USETHREADS + + // free lock on dbi so someone else can use it. + isc_mutex_unlock(&dbi->instance_lock); + +#endif + + return result; +} + + /* if the lookup function does not return SOA or NS records for the zone, + * use this function to get that information for Bind. + */ + +isc_result_t +odbc_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = odbc_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &dbi); + // if we get "not implemented", send it along + if(result == ISC_R_NOTIMPLEMENTED) + return result; + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to return result set for authority query"); + return (ISC_R_FAILURE); + } + // lookup and authority result sets are processed in the same manner + // odbc_process_rs does the job for both functions. + return odbc_process_rs(lookup, dbi); +} + + /* if zone is supported, lookup up a (or multiple) record(s) in it */ + +isc_result_t +odbc_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = odbc_get_resultset(zone, name, NULL, LOOKUP, dbdata, &dbi); + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to return result set for lookup query"); + return (ISC_R_FAILURE); + } + // lookup and authority result sets are processed in the same manner + // odbc_process_rs does the job for both functions. + return odbc_process_rs(lookup, dbi); +} + + /* create an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement which is + * passed into all query functions. + */ +isc_result_t +odbc_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + odbc_instance_t *odbc_inst = NULL; + dbinstance_t *db = NULL; + SQLRETURN sqlRes; + +#ifdef ISC_PLATFORM_USETHREADS + // if multi-threaded, we need a few extra variables. + int dbcount; + int i; + char *endp; + +#endif /* ISC_PLATFORM_USETHREADS */ + + UNUSED(dlzname); + UNUSED(driverarg); + +#ifdef ISC_PLATFORM_USETHREADS + // if debugging, let user know we are multithreaded. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Odbc driver running multithreaded"); +#else /* ISC_PLATFORM_USETHREADS */ + // if debugging, let user know we are single threaded. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Odbc driver running single threaded"); +#endif /* ISC_PLATFORM_USETHREADS */ + + /* verify we have at least 5 arg's passed to the driver */ + if(argc < 5){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver requires at least 4 command line args."); + return (ISC_R_FAILURE); + } + + /* no more than 8 arg's should be passed to the driver */ + if(argc > 8){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver cannot accept more than 7 command line args."); + return (ISC_R_FAILURE); + } + + // multithreaded build can have multiple DB connections +#ifdef ISC_PLATFORM_USETHREADS + + /* check how many db connections we should create */ + dbcount = strtol(argv[1], &endp, 10); + if (*endp != '\0' || dbcount < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver database connection count must be positive."); + return (ISC_R_FAILURE); + } + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* allocate memory for odbc instance */ + odbc_inst = isc_mem_get(ns_g_mctx, sizeof(odbc_instance_t)); + if(odbc_inst == NULL) + return (ISC_R_NOMEMORY); + memset(odbc_inst, 0, sizeof(odbc_instance_t)); + + /* parse connection string and get paramters. */ + + // get odbc database dsn - required + odbc_inst->dsn = (SQLCHAR *) getParameterValue(argv[2], "dsn="); + if(odbc_inst->dsn == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "odbc driver requires a dns parameter."); + result = ISC_R_FAILURE; + goto cleanup; + } + // get odbc database username + // if no username was passed, set odbc_inst.user = NULL; + odbc_inst->user = (SQLCHAR *) getParameterValue(argv[2], "user="); + + // get odbc database password + // if no password was passed, set odbc_inst.pass = NULL; + odbc_inst->pass = (SQLCHAR *) getParameterValue(argv[2], "pass="); + + // create odbc environment & set environment to ODBC V3 + if(odbc_inst->sql_env == NULL){ + // create environment handle + sqlRes = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, + &(odbc_inst->sql_env)); + if(!sqlOK(sqlRes)){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Odbc driver unable to allocate memory"); + result = ISC_R_NOMEMORY; + goto cleanup; + } + //set ODBC version = 3 + sqlRes = SQLSetEnvAttr(odbc_inst->sql_env, SQL_ATTR_ODBC_VERSION, + (void *) SQL_OV_ODBC3, 0); + if(!sqlOK(sqlRes)){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Unable to configure ODBC environment"); + result = ISC_R_NOMEMORY; + goto cleanup; + } + } + +#ifdef ISC_PLATFORM_USETHREADS + + /* allocate memory for database connection list */ + odbc_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t)); + if (odbc_inst->db == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + + + /* initialize DB connection list */ + ISC_LIST_INIT(*odbc_inst->db); + + // create the appropriate number of database instances (DBI) + // append each new DBI to the end of the list + for(i=0; i < dbcount; i++){ + +#endif /* ISC_PLATFORM_USETHREADS */ + + // how many queries were passed in from config file? + switch(argc){ + case 5: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, NULL, + argv[3], argv[4], NULL, &db); + break; + case 6: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, argv[5], + argv[3], argv[4], NULL, &db); + break; + case 7: + result = build_sqldbinstance(ns_g_mctx, argv[6], NULL, argv[5], + argv[3], argv[4], NULL, &db); + break; + case 8: + result = build_sqldbinstance(ns_g_mctx, argv[6], argv[7], argv[5], + argv[3], argv[4], NULL, &db); + break; + default: + result = ISC_R_FAILURE; // not really needed, should shut up compiler. + } + + // unsuccessful?, log err msg and cleanup. + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver could not create database instance object."); + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + + // when multithreaded, build a list of DBI's + ISC_LINK_INIT(db, link); + ISC_LIST_APPEND(*odbc_inst->db, db, link); + +#endif + + result = odbc_connect(odbc_inst, (odbc_db_t **) &(db->dbconn)); + + if(result != ISC_R_SUCCESS){ + +#ifdef ISC_PLATFORM_USETHREADS + + // if multi threaded, let user know which connection failed. + // user could be attempting to create 10 db connections + // and for some reason the db backend only allows 9 + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver failed to create database connection number %u after 3 attempts", i+1); +#else + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver failed to create database connection after 3 attempts"); +#endif + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + + // set DB = null for next loop through. + db = NULL; + + } // end for loop + +#else + // tell odbc_inst about the db connection we just created. + odbc_inst->db = db; + +#endif + + // set dbdata to the odbc_instance we created. + *dbdata = odbc_inst; + + // hey, we got through all of that ok, return success. + return(ISC_R_SUCCESS); + +cleanup: + + destroy_odbc_instance(odbc_inst); + + return result; +} + + /* destroy an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement. + * so we really only need to clean it up since we are not using driverarg. + */ + +void +odbc_destroy(void *driverarg, void *dbdata) +{ + UNUSED(driverarg); + + destroy_odbc_instance((odbc_instance_t *) dbdata); +} + + + // pointers to all our runtime methods. + // this is used during driver registration + // i.e. in dlz_odbc_init below. +static dns_sdlzmethods_t dlz_odbc_methods = { + odbc_create, + odbc_destroy, + odbc_findzone, + odbc_lookup, + odbc_authority, + odbc_allnodes, + odbc_allowzonexfr}; + +/* + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_odbc_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ odbc driver."); + + /* Driver is always threadsafe. When multithreaded all functions use + * multithreaded code. When not multithreaded, all functions can only + * be entered once, but only 1 thread of operation is available in Bind. + * So everything is still threadsafe. + */ + result = dns_sdlzregister("odbc", &dlz_odbc_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA + | DNS_SDLZFLAG_THREADSAFE, ns_g_mctx, &dlz_odbc); + // if we can't register the driver, there are big problems. + if(result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_odbc_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ odbc driver."); + + // unregister the driver. + if (dlz_odbc != NULL) + dns_sdlzunregister(&dlz_odbc); +} + +#endif + diff -Nuar bind-9.3.2-orig/bin/named/dlz_postgres_driver.c bind-9.3.2-mod/bin/named/dlz_postgres_driver.c --- bind-9.3.2-orig/bin/named/dlz_postgres_driver.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/dlz_postgres_driver.c 2006-02-20 19:41:46.000000000 +0100 @@ -0,0 +1,1269 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ_POSTGRES + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//** temporarily include time. +#include + + +#include + +dns_sdlzimplementation_t *dlz_postgres = NULL; + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define LOOKUP 5 + +/*** + *** method prototypes + *** Declared here to shut up compiler warnings + *** about "no previous prototype" + ***/ + +size_t +postgres_makesafe(char *to, const char *from, size_t length); + +void +postgres_destroy_dblist(db_list_t *dblist); + +dbinstance_t * +postgres_find_avail_conn(db_list_t *dblist); + +char * +postgres_escape_string(const char *instr); + +isc_result_t +postgres_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, PGresult **rs); + +isc_result_t +postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs); + +isc_result_t +postgres_findzone(void *driverarg, void *dbdata, const char *name); + +isc_result_t +postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client); + +isc_result_t +postgres_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +isc_result_t +postgres_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup); + +isc_result_t +postgres_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +isc_result_t +postgres_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +void +postgres_destroy(void *driverarg, void *dbdata); + +/*** + *** Private methods + ***/ + +/* --------------- + * Escaping arbitrary strings to get valid SQL strings/identifiers. + * + * Replaces "\\" with "\\\\" and "'" with "''". + * length is the length of the buffer pointed to by + * from. The buffer at to must be at least 2*length + 1 characters + * long. A terminating NUL character is written. + * + * NOTICE!!! + * This function was borrowed directly from PostgreSQL's libpq. + * The function was originally called PQescapeString and renamed + * to postgres_makesafe to avoid a naming collision. + * PQescapeString is a new function made available in Postgres 7.2. + * For some reason the function is not properly exported on Win32 + * builds making the function unavailable on Windows. Also, since + * this function is new it would require building this driver with + * the libpq 7.2. By borrowing this function the Windows problem + * is solved, and the dependence on libpq 7.2 is removed. Libpq is + * still required of course, but an older version should work now too. + * + * The copyright statements from the original file containing this + * function are included below: + * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * --------------- + */ + +size_t +postgres_makesafe(char *to, const char *from, size_t length) +{ + const char *source = from; + char *target = to; + unsigned int remaining = length; + + while (remaining > 0) + { + switch (*source) + { + case '\\': + *target = '\\'; + target++; + *target = '\\'; + /* target and remaining are updated below. */ + break; + + case '\'': + *target = '\''; + target++; + *target = '\''; + /* target and remaining are updated below. */ + break; + + default: + *target = *source; + /* target and remaining are updated below. */ + } + source++; + target++; + remaining--; + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} + +#ifdef ISC_PLATFORM_USETHREADS + + /* Properly cleans up a list of database instances. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ +void +postgres_destroy_dblist(db_list_t *dblist) +{ + + dbinstance_t *ndbi = NULL; + dbinstance_t *dbi = NULL; + + // get the first DBI in the list + ndbi = ISC_LIST_HEAD(*dblist); + + // loop through the list + while(ndbi != NULL){ + dbi = ndbi; + // get the next DBI in the list + ndbi = ISC_LIST_NEXT(dbi, link); + // release DB connection + if(dbi->dbconn != NULL) + PQfinish((PGconn *) dbi->dbconn); + // release all memory that comprised a DBI + destroy_sqldbinstance(dbi); + } + // release memory for the list structure + isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t)); +} + + /* Loops through the list of DB instances, attempting to lock + * on the mutex. If successful, the DBI is reserved for use + * and the thread can perform queries against the database. + * If the lock fails, the next one in the list is tried. + * looping continues until a lock is obtained, or until + * the list has been searched dbc_search_limit times. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ + +dbinstance_t * +postgres_find_avail_conn(db_list_t *dblist) +{ + dbinstance_t *dbi = NULL; + dbinstance_t *head; + int count = 0; + + // get top of list + head = dbi = ISC_LIST_HEAD(*dblist); + + // loop through list + while(count < dbc_search_limit){ + // try to lock on the mutex + if(isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS) + return dbi; // success, return the DBI for use. + + // not successful, keep trying + dbi = ISC_LIST_NEXT(dbi, link); + + // check to see if we have gone to the top of the list. + if(dbi == NULL){ + count++; + dbi = head; + } + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Postgres driver unable to find available connection after searching %d times", + count); + return NULL; +} + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* Allocates memory for a new string, and then constructs the new + * string by "escaping" the input string. The new string is + * safe to be used in queries. This is necessary because we cannot + * be sure of what types of strings are passed to us, and we don't + * want special characters in the string causing problems. + */ + +char * +postgres_escape_string(const char *instr){ + + char *outstr; + unsigned int len; + + if(instr == NULL) + return NULL; + + len = strlen(instr); + + outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1); + if(outstr == NULL) + return NULL; + + postgres_makesafe(outstr, instr, len); +// PQescapeString(outstr, instr, len); + + return outstr; +} + + /* This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds either: + * 1) a list of database instances (in multithreaded mode) OR + * 2) a single database instance (in single threaded mode) + * The function will construct the query and obtain an available + * database instance (DBI). It will then run the query and hopefully + * obtain a result set. Postgres is nice, in that once the result + * set is returned, we can make the db connection available for another + * thread to use, while this thread continues on. So, the DBI is made + * available ASAP by unlocking the instance_lock after we have cleaned + * it up properly. + */ +isc_result_t +postgres_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, PGresult **rs) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + unsigned int i = 0; + unsigned int j = 0; + + /* Extended Patch for Zone Extended Tag */ + char *n_zone_domain; + char *n_zone_tld; + char n_buffer[strlen(zone)+1]; + + +//** temporarily get a unique thread # +unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0)); + + REQUIRE(*rs == NULL); + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d Getting DBI", dlz_thread_num); + + // get db instance / connection +#ifdef ISC_PLATFORM_USETHREADS + + // find an available DBI from the list + dbi = postgres_find_avail_conn((db_list_t *) dbdata); + +#else /* ISC_PLATFORM_USETHREADS */ + + // only 1 DBI - no need to lock instance lock either + // only 1 thread in the whole process, no possible contention. + dbi = (dbinstance_t *) dbdata; + +#endif /* ISC_PLATFORM_USETHREADS */ + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d Got DBI - checking query", dlz_thread_num); + + // if DBI is null, can't do anything else + if(dbi == NULL){ + result = ISC_R_FAILURE; + goto cleanup; + } + + // what type of query are we going to run? + switch(query){ + case ALLNODES: + // if the query was not passed in from the config file + // then we can't run it. return not_implemented, + // so it's like the code for that operation + // was never built into the driver.... AHHH flexibility!!! + if(dbi->allnodes_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case ALLOWXFR: + // same as comments as ALLNODES + if(dbi->allowxfr_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case AUTHORITY: + // same as comments as ALLNODES + if(dbi->authority_q == NULL){ + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case FINDZONE: + // this is required. It's the whole point of DLZ! + if(dbi->findzone_q == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for findzone. Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + case LOOKUP: + // this is required. It's also a major point of DLZ! + if(dbi->lookup_q == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for lookup. Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + default: + // this should never happen. If it does, the code is screwed up! + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to postgres_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d checked query", dlz_thread_num); + + // was a zone string passed? If so, make it safe for use in queries. + if(zone != NULL){ + dbi->zone = postgres_escape_string(zone); + + // Extended Patch for Zone Extended Tag + snprintf(n_buffer, strlen(zone)+1, "%s", zone); + n_zone_domain = strtok(n_buffer, "."); + n_zone_tld = strtok(NULL, ""); + // + dbi->zone_domain = postgres_escape_string(n_zone_domain); + dbi->zone_tld = postgres_escape_string(n_zone_tld); + + if(dbi->zone == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + if(dbi->zone_domain == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + if(dbi->zone_tld == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->zone = NULL; + } + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d did zone", dlz_thread_num); + + // was a record string passed? If so, make it safe for use in queries. + if(record != NULL){ + dbi->record = postgres_escape_string(record); + if(dbi->record == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->record = NULL; + } + + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d did record", dlz_thread_num); + + // was a client string passed? If so, make it safe for use in queries. + if(client != NULL){ + dbi->client = postgres_escape_string(client); + if(dbi->client == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { // no string passed, set the string pointer to NULL + dbi->client = NULL; + } + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d did client", dlz_thread_num); + + // what type of query are we going to run? + // this time we build the actual query to run. + switch(query){ + case ALLNODES: + querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); + break; + case ALLOWXFR: + querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); + break; + case AUTHORITY: + querystring = build_querystring(ns_g_mctx, dbi->authority_q); + break; + case FINDZONE: + querystring = build_querystring(ns_g_mctx, dbi->findzone_q); + break; + case LOOKUP: + querystring = build_querystring(ns_g_mctx, dbi->lookup_q); + break; + default: + // this should never happen. If it does, the code is screwed up! + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to postgres_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d built query", dlz_thread_num); + + // if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! + if(querystring == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d query is '%s'", dlz_thread_num, querystring); + + // output the full query string during debug so we can see + // what lame error the query has. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "\nQuery String: %s\n", querystring); + + // attempt query up to 3 times. + for(j=0; j < 3; j++){ +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d executing query for %d time", dlz_thread_num, j); + // try to get result set + *rs = PQexec((PGconn *)dbi->dbconn, querystring ); + result = ISC_R_SUCCESS; + // if result set is null, reset DB connection, max 3 attempts. + for(i=0; *rs == NULL && i < 3; i++){ +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d resetting connection", dlz_thread_num); + result = ISC_R_FAILURE; + PQreset((PGconn *) dbi->dbconn); + // connection ok, break inner loop + if(PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK) + break; + } + // result set ok, break outter loop + if(PQresultStatus(*rs) == PGRES_TUPLES_OK){ +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d rs ok", dlz_thread_num); + break; + }else{ // we got a result set object, but it's not right. +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d clearing rs", dlz_thread_num); + PQclear(*rs); // get rid of it + result = ISC_R_FAILURE; // incase this was the last attempt + } + } + +cleanup: // it's always good to cleanup after yourself + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d cleaning up", dlz_thread_num); + + // if we couldn't even allocate DBI, just return NULL + if(dbi == NULL) + return ISC_R_FAILURE; + + // free dbi->zone string + if(dbi->zone != NULL) + isc_mem_free(ns_g_mctx, dbi->zone); + + // free dbi->zone_domain string + if(dbi->zone_domain != NULL) + isc_mem_free(ns_g_mctx, dbi->zone_domain); + + // free dbi->zone_tld string + if(dbi->zone_tld != NULL) + isc_mem_free(ns_g_mctx, dbi->zone_tld); + + // free dbi->record string + if(dbi->record != NULL) + isc_mem_free(ns_g_mctx, dbi->record); + + // free dbi->client string + if(dbi->client != NULL) + isc_mem_free(ns_g_mctx, dbi->client); + +#ifdef ISC_PLATFORM_USETHREADS + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d unlocking mutex", dlz_thread_num); + + // release the lock so another thread can use this dbi + isc_mutex_unlock(&dbi->instance_lock); + +#endif /* ISC_PLATFORM_USETHREADS */ + + // release query string + if(querystring != NULL) + isc_mem_free(ns_g_mctx, querystring ); + +//** temporary logging message +isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, +"%d returning", dlz_thread_num); + + // return result + return result; +} + + /* The processing of result sets for lookup and authority are + * exactly the same. So that functionality has been moved + * into this function to minimize code. + */ + +isc_result_t +postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs) +{ + isc_result_t result; + unsigned int i; + unsigned int rows; + unsigned int fields; + unsigned int j; + unsigned int len; + char *tmpString; + char *endp; + int ttl; + + rows = PQntuples(rs); // how many rows in result set + fields = PQnfields(rs); // how many columns in result set + for(i=0; i < rows; i++){ + switch(fields){ + case 1: // one column in rs, it's the data field + // use default type of A record, and default TTL of 86400 + result = dns_sdlz_putrr(lookup, "a", 86400, PQgetvalue(rs, i, 0)); + break; + case 2: // two columns, data field, and data type. + // use default TTL of 86400 + result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0), + 86400, PQgetvalue(rs, i, 1)); + break; + case 3: // three columns, all data no defaults + // convert text to int, make sure it worked right + ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver ttl must be a postive number"); + } + result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1), + ttl, PQgetvalue(rs, i, 2)); + break; + default: // more than 3 fields, concatonate the last ones together. + // figure out how long to make string + for(j=2, len=0; j < fields; j++){ + len += strlen(PQgetvalue(rs, i, j)) + 1; + } + // allocate string memory, allow for NULL to term string + tmpString = isc_mem_allocate(ns_g_mctx, len + 1); + if(tmpString == NULL){ // major bummer, need more ram + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to allocate memory for temporary string"); + PQclear(rs); // can't use it now, get rid of it. + return (ISC_R_FAILURE); // Yeah, I'd say! + } + // copy field to tmpString + strcpy(tmpString, PQgetvalue(rs, i, 2)); + // concat the rest of fields together, space between each one. + for(j=3; j < fields; j++){ + strcat(tmpString, " "); + strcat(tmpString, PQgetvalue(rs, i, j)); + } + // convert text to int, make sure it worked right + ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver ttl must be a postive number"); + } + // ok, now tell Bind about it. + result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1), + ttl, tmpString); + // done, get rid of this thing. + isc_mem_free(ns_g_mctx, tmpString); + } + // I sure hope we were successful + if(result != ISC_R_SUCCESS){ + PQclear(rs); // nope, get rid of the Result set, and log a msg + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putrr returned error. Error code was: %s", + isc_result_totext(result)); + return (ISC_R_FAILURE); + } + } + + // free result set memory + PQclear(rs); + + // if we did return results, we are successful + if(rows > 0) + return (ISC_R_SUCCESS); + + // empty result set, no data found + return (ISC_R_NOTFOUND); +} + +/*** + *** SDLZ interface methods + ***/ + + /* determine if the zone is supported by (in) the database */ + +isc_result_t +postgres_findzone(void *driverarg, void *dbdata, const char *name) +{ + isc_result_t result; + PGresult *rs = NULL; + unsigned int rows; + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = postgres_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &rs); + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + if(rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return result set for findzone query"); + return (ISC_R_FAILURE); + } + // count how many rows in result set + rows = PQntuples(rs); + // get rid of result set, we are done with it. + PQclear(rs); + + // if we returned any rows, zone is supported. + if(rows > 0) + return (ISC_R_SUCCESS); + + // no rows returned, zone is not supported. + return (ISC_R_NOTFOUND); +} + + /* Determine if the client is allowed to perform a zone transfer */ +isc_result_t +postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + PGresult *rs = NULL; + unsigned int rows; + UNUSED(driverarg); + + // first check if the zone is supported by the database. + result = postgres_findzone(driverarg, dbdata, name); + if(result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + // if we get to this point we know the zone is supported by the database + // the only questions now are is the zone transfer is allowed for this client + // and did the config file have an allow zone xfr query + + // Run our query, and get a result set from the database. + result = postgres_get_resultset(name, NULL, client, ALLOWXFR, dbdata, &rs); + // if we get "not implemented", send it along. + if(result == ISC_R_NOTIMPLEMENTED) + return result; + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + if(rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return result set for allow xfr query"); + return (ISC_R_FAILURE); + } + // count how many rows in result set + rows = PQntuples(rs); + // get rid of result set, we are done with it. + PQclear(rs); + + // if we returned any rows, zone xfr is allowed. + if(rows > 0) + return (ISC_R_SUCCESS); + + // no rows returned, zone xfr not allowed + return (ISC_R_NOPERM); +} + + /* If the client is allowed to perform a zone transfer, the next order of + * business is to get all the nodes in the zone, so bind can respond to the + * query. + */ +isc_result_t +postgres_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + isc_result_t result; + PGresult *rs = NULL; + unsigned int i; + unsigned int rows; + unsigned int fields; + unsigned int j; + unsigned int len; + char *tmpString; + char *endp; + int ttl; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = postgres_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &rs); + // if we get "not implemented", send it along + if(result == ISC_R_NOTIMPLEMENTED) + return result; + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + if(rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return result set for all nodes query"); + return (ISC_R_FAILURE); + } + + rows = PQntuples(rs); // how many rows in result set + fields = PQnfields(rs); // how many columns in result set + for(i=0; i < rows; i++){ + if(fields < 4){ // gotta have at least 4 columns + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver too few fields returned by all nodes query"); + } + // convert text to int, make sure it worked right + ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); + if (*endp != '\0' || ttl < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver ttl must be a postive number"); + } + if(fields == 4){ + // tell Bind about it. + result = dns_sdlz_putnamedrr(allnodes, PQgetvalue(rs, i, 2), + PQgetvalue(rs, i, 1), ttl, PQgetvalue(rs, i, 3)); + } else { // more than 4 fields, concatonat the last ones together. + // figure out how long to make string + for(j=3, len=0; j < fields; j++){ + len += strlen(PQgetvalue(rs, i, j)) + 1; + } + // allocate memory, allow for NULL to term string + tmpString = isc_mem_allocate(ns_g_mctx, len + 1); + if(tmpString == NULL){ // we need more ram. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to allocate memory for temporary string"); + PQclear(rs); + return (ISC_R_FAILURE); + } + // copy this field to tmpString + strcpy(tmpString, PQgetvalue(rs, i, 3)); + // concatonate the rest, with spaces between + for(j=4; j < fields; j++){ + strcat(tmpString, " "); + strcat(tmpString, PQgetvalue(rs, i, j)); + } + // tell Bind about it. + result = dns_sdlz_putnamedrr(allnodes, PQgetvalue(rs, i, 2), + PQgetvalue(rs, i, 1), ttl, tmpString); + isc_mem_free(ns_g_mctx, tmpString); + } + // if we weren't successful, log err msg + if(result != ISC_R_SUCCESS){ + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putnamedrr returned error. Error code was: %s", + isc_result_totext(result)); + return (ISC_R_FAILURE); + } + } + + // free result set memory + PQclear(rs); + + // if we did return results, we are successful + if(rows > 0) + return (ISC_R_SUCCESS); + + // empty result set, no data found + return (ISC_R_NOTFOUND); +} + + /* if the lookup function does not return SOA or NS records for the zone, + * use this function to get that information for Bind. + */ + +isc_result_t +postgres_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + PGresult *rs = NULL; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = postgres_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &rs); + // if we get "not implemented", send it along + if(result == ISC_R_NOTIMPLEMENTED) + return result; + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + if(rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return result set for authority query"); + return (ISC_R_FAILURE); + } + // lookup and authority result sets are processed in the same manner + // postgres_process_rs does the job for both functions. + return postgres_process_rs(lookup, rs); +} + + /* if zone is supported, lookup up a (or multiple) record(s) in it */ +isc_result_t +postgres_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + PGresult *rs = NULL; + + UNUSED(driverarg); + + // run the query and get the result set from the database. + result = postgres_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs); + // if we didn't get a result set, log an err msg. + if(result != ISC_R_SUCCESS){ + if(rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return result set for lookup query"); + return (ISC_R_FAILURE); + } + // lookup and authority result sets are processed in the same manner + // postgres_process_rs does the job for both functions. + return postgres_process_rs(lookup, rs); +} + + /* create an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement which is + * passed into all query functions. + */ +isc_result_t +postgres_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + unsigned int j; + +#ifdef ISC_PLATFORM_USETHREADS + // if multi-threaded, we need a few extra variables. + int dbcount; + db_list_t *dblist = NULL; + int i; + char *endp; + +#endif /* ISC_PLATFORM_USETHREADS */ + + UNUSED(driverarg); + UNUSED(dlzname); + +//** seed random # generator +srand( (unsigned)time( NULL ) ); + + +#ifdef ISC_PLATFORM_USETHREADS + // if debugging, let user know we are multithreaded. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Postgres driver running multithreaded"); +#else /* ISC_PLATFORM_USETHREADS */ + // if debugging, let user know we are single threaded. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Postgres driver running single threaded"); +#endif /* ISC_PLATFORM_USETHREADS */ + + /* verify we have at least 5 arg's passed to the driver */ + if(argc < 5){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver requires at least 4 command line args."); + return (ISC_R_FAILURE); + } + + /* no more than 8 arg's should be passed to the driver */ + if(argc > 8){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver cannot accept more than 7 command line args."); + return (ISC_R_FAILURE); + } + + // multithreaded build can have multiple DB connections +#ifdef ISC_PLATFORM_USETHREADS + + /* check how many db connections we should create */ + dbcount = strtol(argv[1], &endp, 10); + if (*endp != '\0' || dbcount < 0){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver database connection count must be positive."); + return (ISC_R_FAILURE); + } + + /* allocate memory for database connection list */ + dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t)); + if (dblist == NULL) + return (ISC_R_NOMEMORY); + + /* initialize DB connection list */ + ISC_LIST_INIT(*dblist); + + // create the appropriate number of database instances (DBI) + // append each new DBI to the end of the list + for(i=0; i < dbcount; i++){ + +#endif /* ISC_PLATFORM_USETHREADS */ + + // how many queries were passed in from config file? + switch(argc){ + case 5: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, NULL, + argv[3], argv[4], NULL, &dbi); + break; + case 6: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, argv[5], + argv[3], argv[4], NULL, &dbi); + break; + case 7: + result = build_sqldbinstance(ns_g_mctx, argv[6], NULL, argv[5], + argv[3], argv[4], NULL, &dbi); + break; + case 8: + result = build_sqldbinstance(ns_g_mctx, argv[6], argv[7], argv[5], + argv[3], argv[4], NULL, &dbi); + break; + default: + result = ISC_R_FAILURE; // not really needed, should shut up compiler. + } + + + if(result == ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Postgres driver created database instance object."); + } else { // unsuccessful?, log err msg and cleanup. + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver could not create database instance object."); + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + + // when multithreaded, build a list of DBI's + ISC_LINK_INIT(dbi, link); + ISC_LIST_APPEND(*dblist, dbi, link); + +#endif + + // create and set db connection + dbi->dbconn = PQconnectdb(argv[2]); + // if db connection cannot be created, log err msg and cleanup. + if(dbi->dbconn == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver could not allocate memory for database connection"); + goto cleanup; + } + + // if we cannot connect the first time, try 3 more times. + for(j=0; PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK && j < 3; j++) + PQreset((PGconn *) dbi->dbconn); + + +#ifdef ISC_PLATFORM_USETHREADS + + // if multi threaded, let user know which connection failed. + // user could be attempting to create 10 db connections + // and for some reason the db backend only allows 9 + if(PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver failed to create database connection number %u after 4 attempts", i+1); + goto cleanup; + } + + // set DBI = null for next loop through. + dbi = NULL; + } // end for loop + + // set dbdata to the list we created. + *dbdata = dblist; + +#else /* ISC_PLATFORM_USETHREADS */ + // if single threaded, just let user know we couldn't connect. + if(PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver failed to create database connection after 4 attempts"); + goto cleanup; + } + + // single threaded build can only use 1 db connection, return it via dbdata + *dbdata = dbi; + +#endif /* ISC_PLATFORM_USETHREADS */ + + // hey, we got through all of that ok, return success. + return(ISC_R_SUCCESS); + +cleanup: + +#ifdef ISC_PLATFORM_USETHREADS + // if multithreaded, we could fail because only 1 connection + // couldn't be made. We should cleanup the other successful + // connections properly. + postgres_destroy_dblist(dblist); + +#else /* ISC_PLATFORM_USETHREADS */ + if(dbi != NULL) + destroy_sqldbinstance(dbi); + +#endif /* ISC_PLATFORM_USETHREADS */ + return(ISC_R_FAILURE); +} + + + /* destroy an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement. + * so we really only need to clean it up since we are not using driverarg. + */ +void +postgres_destroy(void *driverarg, void *dbdata) +{ + +#ifdef ISC_PLATFORM_USETHREADS + + UNUSED(driverarg); + // cleanup the list of DBI's + postgres_destroy_dblist((db_list_t *) dbdata); + +#else /* ISC_PLATFORM_USETHREADS */ + + dbinstance_t *dbi; + + UNUSED(driverarg); + + dbi = (dbinstance_t *) dbdata; + + // release DB connection + if(dbi->dbconn != NULL) + PQfinish((PGconn *) dbi->dbconn); + + // destroy single DB instance + destroy_sqldbinstance(dbi); + +#endif /* ISC_PLATFORM_USETHREADS */ +} + + // pointers to all our runtime methods. + // this is used during driver registration + // i.e. in dlz_postgres_init below. +static dns_sdlzmethods_t dlz_postgres_methods = { + postgres_create, + postgres_destroy, + postgres_findzone, + postgres_lookup, + postgres_authority, + postgres_allnodes, + postgres_allowzonexfr}; + +/* + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_postgres_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ postgres driver."); + + /* Driver is always threadsafe. When multithreaded all functions use + * multithreaded code. When not multithreaded, all functions can only + * be entered once, but only 1 thread of operation is available in Bind. + * So everything is still threadsafe. + */ + result = dns_sdlzregister("postgres", &dlz_postgres_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA + | DNS_SDLZFLAG_THREADSAFE, ns_g_mctx, &dlz_postgres); + // if we can't register the driver, there are big problems. + if(result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_postgres_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ postgres driver."); + + // unregister the driver. + if (dlz_postgres != NULL) + dns_sdlzunregister(&dlz_postgres); +} + +#endif + diff -Nuar bind-9.3.2-orig/bin/named/dlz_stub_driver.c bind-9.3.2-mod/bin/named/dlz_stub_driver.c --- bind-9.3.2-orig/bin/named/dlz_stub_driver.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/dlz_stub_driver.c 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,356 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ_STUB + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +dns_sdlzimplementation_t *dlz_stub = NULL; + +typedef struct config_data { + char *myzone; + char *myname; + char *myip; + isc_mem_t *mctx; +}config_data_t; + +/*** + *** method prototypes + *** Declared here to shut up compiler warnings + *** about "no previous prototype" + ***/ + +isc_result_t +stub_dlz_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +isc_result_t +stub_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client); + +isc_result_t +stub_dlz_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup); + +isc_result_t +stub_dlz_findzonedb(void *driverarg, void *dbdata, const char *name); + +isc_result_t +stub_dlz_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +isc_result_t +stub_dlz_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +void +stub_dlz_destroy(void *driverarg, void *dbdata); + +/*** + *** SDLZ methods + ***/ +isc_result_t +stub_dlz_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + config_data_t *cd; + isc_result_t result; + + UNUSED(zone); + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + result = dns_sdlz_putnamedrr(allnodes, cd->myname, "soa", 86400, "web root.localhost. 0 28800 7200 604800 86400"); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + result = dns_sdlz_putnamedrr(allnodes, "ns", "ns", 86400, cd->myname); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + result = dns_sdlz_putnamedrr(allnodes, cd->myname, "a", 1, cd->myip); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + return (ISC_R_SUCCESS); +} + +isc_result_t +stub_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + UNUSED(driverarg); + UNUSED(dbdata); + UNUSED(name); + UNUSED(client); + return ISC_R_SUCCESS; +} + +isc_result_t +stub_dlz_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + config_data_t *cd; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + if (strcmp(zone, cd->myzone) == 0) { + result = dns_sdlz_putsoa(lookup, cd->myname, "root.localhost.", 0); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + + result = dns_sdlz_putrr(lookup, "ns", 86400, cd->myname); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + + return (ISC_R_SUCCESS); + } + return (ISC_R_NOTFOUND); +} + +isc_result_t +stub_dlz_findzonedb(void *driverarg, void *dbdata, const char *name) +{ + + config_data_t *cd; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + // Write info message to log + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "dlz_stub findzone looking for '%s'", name); + + if(strcmp(cd->myzone, name) == 0) + return (ISC_R_SUCCESS); + else + return (ISC_R_SUCCESS); +} + + +isc_result_t +stub_dlz_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + config_data_t *cd; + + UNUSED(zone); + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + if (strcmp(name, cd->myname) == 0) { + result = dns_sdlz_putrr(lookup, "a", 1, cd->myip); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + + return (ISC_R_SUCCESS); + } + return (ISC_R_FAILURE); + +} + + +isc_result_t +stub_dlz_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + + config_data_t *cd; + + UNUSED(driverarg); + + if(argc < 4) + return (ISC_R_FAILURE); + /* + * Write info message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Loading '%s' using DLZ_stub driver. Zone: %s, Name: %s IP: %s", + dlzname, argv[1], argv[2], argv[3]); + + cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t)); + if((cd) == NULL){ + return (ISC_R_NOMEMORY); + } + + memset(cd, 0, sizeof(config_data_t)); + + cd->myzone = isc_mem_strdup(ns_g_mctx, argv[1]); + if(cd->myzone == NULL){ + isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t)); + return (ISC_R_NOMEMORY); + } + + cd->myname = isc_mem_strdup(ns_g_mctx, argv[2]); + if(cd->myname == NULL){ + isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t)); + isc_mem_free(ns_g_mctx, cd->myzone); + return (ISC_R_NOMEMORY); + } + + cd->myip = isc_mem_strdup(ns_g_mctx, argv[3]); + if(cd->myip == NULL){ + isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t)); + isc_mem_free(ns_g_mctx, cd->myname); + isc_mem_free(ns_g_mctx, cd->myzone); + return (ISC_R_NOMEMORY); + } + + isc_mem_attach(ns_g_mctx, &cd->mctx); + + *dbdata = cd; + + return(ISC_R_SUCCESS); +} + +void +stub_dlz_destroy(void *driverarg, void *dbdata) +{ + config_data_t *cd; + isc_mem_t *mctx; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unloading DLZ_stub driver."); + + isc_mem_free(ns_g_mctx, cd->myzone); + isc_mem_free(ns_g_mctx, cd->myname); + isc_mem_free(ns_g_mctx, cd->myip); + mctx = cd->mctx; + isc_mem_put(mctx, cd, sizeof(config_data_t)); + isc_mem_detach(&mctx); +} + +static dns_sdlzmethods_t dlz_stub_methods = { + stub_dlz_create, + stub_dlz_destroy, + stub_dlz_findzonedb, + stub_dlz_lookup, + stub_dlz_authority, + stub_dlz_allnodes, + stub_dlz_allowzonexfr}; + +/* + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_stub_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ_stub driver."); + + result = dns_sdlzregister("dlz_stub", &dlz_stub_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA, + ns_g_mctx, &dlz_stub); + if(result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_stub_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ_stub driver."); + + if (dlz_stub != NULL) + dns_sdlzunregister(&dlz_stub); +} + +#endif + diff -Nuar bind-9.3.2-orig/bin/named/include/named/dlz_bdb_driver.h bind-9.3.2-mod/bin/named/include/named/dlz_bdb_driver.h --- bind-9.3.2-orig/bin/named/include/named/dlz_bdb_driver.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/dlz_bdb_driver.h 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,41 @@ +/* + * 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. + */ + +isc_result_t +dlz_bdb_init(void); + +void +dlz_bdb_clear(void); + diff -Nuar bind-9.3.2-orig/bin/named/include/named/dlz_bdbhpt_driver.h bind-9.3.2-mod/bin/named/include/named/dlz_bdbhpt_driver.h --- bind-9.3.2-orig/bin/named/include/named/dlz_bdbhpt_driver.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/dlz_bdbhpt_driver.h 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,41 @@ +/* + * 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. + */ + +isc_result_t +dlz_bdbhpt_init(void); + +void +dlz_bdbhpt_clear(void); + diff -Nuar bind-9.3.2-orig/bin/named/include/named/dlz_filesystem_driver.h bind-9.3.2-mod/bin/named/include/named/dlz_filesystem_driver.h --- bind-9.3.2-orig/bin/named/include/named/dlz_filesystem_driver.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/dlz_filesystem_driver.h 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,41 @@ +/* + * 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. + */ + +isc_result_t +dlz_fs_init(void); + +void +dlz_fs_clear(void); + diff -Nuar bind-9.3.2-orig/bin/named/include/named/dlz_ldap_driver.h bind-9.3.2-mod/bin/named/include/named/dlz_ldap_driver.h --- bind-9.3.2-orig/bin/named/include/named/dlz_ldap_driver.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/dlz_ldap_driver.h 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,41 @@ +/* + * 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. + */ + +isc_result_t +dlz_ldap_init(void); + +void +dlz_ldap_clear(void); + diff -Nuar bind-9.3.2-orig/bin/named/include/named/dlz_mysql_driver.h bind-9.3.2-mod/bin/named/include/named/dlz_mysql_driver.h --- bind-9.3.2-orig/bin/named/include/named/dlz_mysql_driver.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/dlz_mysql_driver.h 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,41 @@ +/* + * 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. + */ + +isc_result_t +dlz_mysql_init(void); + +void +dlz_mysql_clear(void); + diff -Nuar bind-9.3.2-orig/bin/named/include/named/dlz_odbc_driver.h bind-9.3.2-mod/bin/named/include/named/dlz_odbc_driver.h --- bind-9.3.2-orig/bin/named/include/named/dlz_odbc_driver.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/dlz_odbc_driver.h 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,41 @@ +/* + * 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. + */ + +isc_result_t +dlz_odbc_init(void); + +void +dlz_odbc_clear(void); + diff -Nuar bind-9.3.2-orig/bin/named/include/named/dlz_postgres_driver.h bind-9.3.2-mod/bin/named/include/named/dlz_postgres_driver.h --- bind-9.3.2-orig/bin/named/include/named/dlz_postgres_driver.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/dlz_postgres_driver.h 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,41 @@ +/* + * 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. + */ + +isc_result_t +dlz_postgres_init(void); + +void +dlz_postgres_clear(void); + diff -Nuar bind-9.3.2-orig/bin/named/include/named/dlz_stub_driver.h bind-9.3.2-mod/bin/named/include/named/dlz_stub_driver.h --- bind-9.3.2-orig/bin/named/include/named/dlz_stub_driver.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/dlz_stub_driver.h 2006-02-20 19:13:47.000000000 +0100 @@ -0,0 +1,40 @@ +/* + * 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. + */ + +isc_result_t +dlz_stub_init(void); + +void +dlz_stub_clear(void); diff -Nuar bind-9.3.2-orig/bin/named/include/named/sdlz_helper.h bind-9.3.2-mod/bin/named/include/named/sdlz_helper.h --- bind-9.3.2-orig/bin/named/include/named/sdlz_helper.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/include/named/sdlz_helper.h 2006-02-20 19:41:46.000000000 +0100 @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#ifndef SDLZHELPER_H +#define SDLZHELPER_H 1 + +/*** + *** Types + ***/ +#define SDLZH_REQUIRE_CLIENT 0x01 +#define SDLZH_REQUIRE_QUERY 0x02 +#define SDLZH_REQUIRE_RECORD 0x04 +#define SDLZH_REQUIRE_ZONE 0x08 + +typedef struct query_segment query_segment_t; +typedef ISC_LIST(query_segment_t) query_list_t; +typedef struct dbinstance dbinstance_t; +typedef ISC_LIST(dbinstance_t) db_list_t; +typedef struct driverinstance driverinstance_t; + + /* a query segment is all the text between our special tokens + * special tokens are %zone%, %record%, %client% + */ +struct query_segment { + void *sql; + unsigned int strlen; + isc_boolean_t direct; + ISC_LINK(query_segment_t) link; +}; + + /* a database instance contains everything we need for running + * a query against the database. Using it each separate thread + * can dynamically construct a query and execute it against the + * database. The "instance_lock" and locking code in the driver's + * make sure no two threads try to use the same DBI at a time. + */ +struct dbinstance { + void *dbconn; + query_list_t *allnodes_q; + query_list_t *allowxfr_q; + query_list_t *authority_q; + query_list_t *findzone_q; + query_list_t *lookup_q; + query_list_t *countzone_q; + char *query_buf; + char *zone; + char *zone_domain; + char *zone_tld; + char *record; + char *client; + isc_mem_t *mctx; + isc_mutex_t instance_lock; + ISC_LINK(dbinstance_t) link; + +}; + +/*** + *** Method declarations + ***/ + + /* see the code in sdlz_helper.c for more information on these methods */ + +char * +build_querystring(isc_mem_t *mctx, query_list_t *querylist); + +isc_result_t +build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str, + const char *allowxfr_str, const char *authority_str, + const char *findzone_str, const char *lookup_str, + const char *countzone_str, dbinstance_t **dbi); + +void +destroy_sqldbinstance(dbinstance_t *dbi); + +char * +getParameterValue(const char *input, const char* key); + +#endif /* SDLZHELPER_H */ + diff -Nuar bind-9.3.2-orig/bin/named/main.c bind-9.3.2-mod/bin/named/main.c --- bind-9.3.2-orig/bin/named/main.c 2005-04-29 03:04:47.000000000 +0200 +++ bind-9.3.2-mod/bin/named/main.c 2006-02-20 19:13:47.000000000 +0100 @@ -71,6 +71,35 @@ */ /* #include "xxdb.h" */ +#ifdef DLZ_STUB +#include +#endif + +#ifdef DLZ_POSTGRES +#include +#endif + +#ifdef DLZ_MYSQL +#include +#endif + +#ifdef DLZ_FILESYSTEM +#include +#endif + +#ifdef DLZ_BDB +#include +#include +#endif + +#ifdef DLZ_LDAP +#include +#endif + +#ifdef DLZ_ODBC +#include +#endif + static isc_boolean_t want_stats = ISC_FALSE; static char program_name[ISC_DIR_NAMEMAX] = "named"; static char absolute_conffile[ISC_DIR_PATHMAX]; @@ -671,6 +700,35 @@ */ /* xxdb_init(); */ +#ifdef DLZ_STUB + dlz_stub_init(); +#endif + +#ifdef DLZ_POSTGRES + dlz_postgres_init(); +#endif + +#ifdef DLZ_MYSQL + dlz_mysql_init(); +#endif + +#ifdef DLZ_FILESYSTEM + dlz_fs_init(); +#endif + +#ifdef DLZ_BDB + dlz_bdb_init(); + dlz_bdbhpt_init(); +#endif + +#ifdef DLZ_LDAP + dlz_ldap_init(); +#endif + +#ifdef DLZ_ODBC + dlz_odbc_init(); +#endif + ns_server_create(ns_g_mctx, &ns_g_server); } @@ -687,6 +745,35 @@ */ /* xxdb_clear(); */ +#ifdef DLZ_STUB + dlz_stub_clear(); +#endif + +#ifdef DLZ_POSTGRES + dlz_postgres_clear(); +#endif + +#ifdef DLZ_MYSQL + dlz_mysql_clear(); +#endif + +#ifdef DLZ_FILESYSTEM + dlz_fs_clear(); +#endif + +#ifdef DLZ_BDB + dlz_bdb_clear(); + dlz_bdbhpt_clear(); +#endif + +#ifdef DLZ_LDAP + dlz_ldap_clear(); +#endif + +#ifdef DLZ_ODBC + dlz_odbc_clear(); +#endif + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN, ISC_LOG_NOTICE, "exiting"); ns_log_shutdown(); @@ -775,7 +862,7 @@ #endif /* HAVE_LIBSCF */ int -main(int argc, char *argv[]) { +main(int argc, char **argv) { isc_result_t result; #ifdef HAVE_LIBSCF char *instance = NULL; diff -Nuar bind-9.3.2-orig/bin/named/main.c.orig bind-9.3.2-mod/bin/named/main.c.orig --- bind-9.3.2-orig/bin/named/main.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/main.c.orig 2005-04-29 03:04:47.000000000 +0200 @@ -0,0 +1,895 @@ +/* + * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2003 Internet Software Consortium. + * + * 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: main.c,v 1.119.2.3.2.22 2005/04/29 01:04:47 marka Exp $ */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +/* + * Defining NS_MAIN provides storage declarations (rather than extern) + * for variables in named/globals.h. + */ +#define NS_MAIN 1 + +#include +#include +#include /* Explicit, though named/log.h includes it. */ +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBSCF +#include +#endif + +/* + * Include header files for database drivers here. + */ +/* #include "xxdb.h" */ + +static isc_boolean_t want_stats = ISC_FALSE; +static char program_name[ISC_DIR_NAMEMAX] = "named"; +static char absolute_conffile[ISC_DIR_PATHMAX]; +static char saved_command_line[512]; +static char version[512]; + +void +ns_main_earlywarning(const char *format, ...) { + va_list args; + + va_start(args, format); + if (ns_g_lctx != NULL) { + isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_WARNING, + format, args); + } else { + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); +} + +void +ns_main_earlyfatal(const char *format, ...) { + va_list args; + + va_start(args, format); + if (ns_g_lctx != NULL) { + isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + format, args); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to early fatal error)"); + } else { + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); + + exit(1); +} + +static void +assertion_failed(const char *file, int line, isc_assertiontype_t type, + const char *cond) +{ + /* + * Handle assertion failures. + */ + + if (ns_g_lctx != NULL) { + /* + * Reset the assetion callback in case it is the log + * routines causing the assertion. + */ + isc_assertion_setcallback(NULL); + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "%s:%d: %s(%s) failed", file, line, + isc_assertion_typetotext(type), cond); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to assertion failure)"); + } else { + fprintf(stderr, "%s:%d: %s(%s) failed\n", + file, line, isc_assertion_typetotext(type), cond); + fflush(stderr); + } + + if (ns_g_coreok) + abort(); + exit(1); +} + +static void +library_fatal_error(const char *file, int line, const char *format, + va_list args) ISC_FORMAT_PRINTF(3, 0); + +static void +library_fatal_error(const char *file, int line, const char *format, + va_list args) +{ + /* + * Handle isc_error_fatal() calls from our libraries. + */ + + if (ns_g_lctx != NULL) { + /* + * Reset the error callback in case it is the log + * routines causing the assertion. + */ + isc_error_setfatal(NULL); + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "%s:%d: fatal error:", file, line); + isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + format, args); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to fatal error in library)"); + } else { + fprintf(stderr, "%s:%d: fatal error: ", file, line); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + + if (ns_g_coreok) + abort(); + exit(1); +} + +static void +library_unexpected_error(const char *file, int line, const char *format, + va_list args) ISC_FORMAT_PRINTF(3, 0); + +static void +library_unexpected_error(const char *file, int line, const char *format, + va_list args) +{ + /* + * Handle isc_error_unexpected() calls from our libraries. + */ + + if (ns_g_lctx != NULL) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_ERROR, + "%s:%d: unexpected error:", file, line); + isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_ERROR, + format, args); + } else { + fprintf(stderr, "%s:%d: fatal error: ", file, line); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } +} + +static void +lwresd_usage(void) { + fprintf(stderr, + "usage: lwresd [-4|-6] [-c conffile | -C resolvconffile] " + "[-d debuglevel]\n" + " [-f|-g] [-n number_of_cpus] [-p port] " + "[-P listen-port] [-s]\n" + " [-t chrootdir] [-u username] [-i pidfile]\n" + " [-m {usage|trace|record}]\n"); +} + +static void +usage(void) { + if (ns_g_lwresdonly) { + lwresd_usage(); + return; + } + fprintf(stderr, + "usage: named [-4|-6] [-c conffile] [-d debuglevel] " + "[-f|-g] [-n number_of_cpus]\n" + " [-p port] [-s] [-t chrootdir] [-u username]\n" + " [-m {usage|trace|record}]\n"); +} + +static void +save_command_line(int argc, char *argv[]) { + int i; + char *src; + char *dst; + char *eob; + const char truncated[] = "..."; + isc_boolean_t quoted = ISC_FALSE; + + dst = saved_command_line; + eob = saved_command_line + sizeof(saved_command_line); + + for (i = 1; i < argc && dst < eob; i++) { + *dst++ = ' '; + + src = argv[i]; + while (*src != '\0' && dst < eob) { + /* + * This won't perfectly produce a shell-independent + * pastable command line in all circumstances, but + * comes close, and for practical purposes will + * nearly always be fine. + */ + if (quoted || isalnum(*src & 0xff) || + *src == '-' || *src == '_' || + *src == '.' || *src == '/') { + *dst++ = *src++; + quoted = ISC_FALSE; + } else { + *dst++ = '\\'; + quoted = ISC_TRUE; + } + } + } + + INSIST(sizeof(saved_command_line) >= sizeof(truncated)); + + if (dst == eob) + strcpy(eob - sizeof(truncated), truncated); + else + *dst = '\0'; +} + +static int +parse_int(char *arg, const char *desc) { + char *endp; + int tmp; + long int ltmp; + + ltmp = strtol(arg, &endp, 10); + tmp = (int) ltmp; + if (*endp != '\0') + ns_main_earlyfatal("%s '%s' must be numeric", desc, arg); + if (tmp < 0 || tmp != ltmp) + ns_main_earlyfatal("%s '%s' out of range", desc, arg); + return (tmp); +} + +static struct flag_def { + const char *name; + unsigned int value; +} mem_debug_flags[] = { + { "trace", ISC_MEM_DEBUGTRACE }, + { "record", ISC_MEM_DEBUGRECORD }, + { "usage", ISC_MEM_DEBUGUSAGE }, + { NULL, 0 } +}; + +static void +set_flags(const char *arg, struct flag_def *defs, unsigned int *ret) { + for (;;) { + const struct flag_def *def; + const char *end = strchr(arg, ','); + int arglen; + if (end == NULL) + end = arg + strlen(arg); + arglen = end - arg; + for (def = defs; def->name != NULL; def++) { + if (arglen == (int)strlen(def->name) && + memcmp(arg, def->name, arglen) == 0) { + *ret |= def->value; + goto found; + } + } + ns_main_earlyfatal("unrecognized flag '%.*s'", arglen, arg); + found: + if (*end == '\0') + break; + arg = end + 1; + } +} + +static void +parse_command_line(int argc, char *argv[]) { + int ch; + int port; + isc_boolean_t disable6 = ISC_FALSE; + isc_boolean_t disable4 = ISC_FALSE; + + save_command_line(argc, argv); + + isc_commandline_errprint = ISC_FALSE; + while ((ch = isc_commandline_parse(argc, argv, + "46c:C:d:fgi:lm:n:N:p:P:st:u:vx:")) != -1) { + switch (ch) { + case '4': + if (disable4) + ns_main_earlyfatal("cannot specify -4 and -6"); + if (isc_net_probeipv4() != ISC_R_SUCCESS) + ns_main_earlyfatal("IPv4 not supported by OS"); + isc_net_disableipv6(); + disable6 = ISC_TRUE; + break; + case '6': + if (disable6) + ns_main_earlyfatal("cannot specify -4 and -6"); + if (isc_net_probeipv6() != ISC_R_SUCCESS) + ns_main_earlyfatal("IPv6 not supported by OS"); + isc_net_disableipv4(); + disable4 = ISC_TRUE; + break; + case 'c': + ns_g_conffile = isc_commandline_argument; + lwresd_g_conffile = isc_commandline_argument; + if (lwresd_g_useresolvconf) + ns_main_earlyfatal("cannot specify -c and -C"); + ns_g_conffileset = ISC_TRUE; + break; + case 'C': + lwresd_g_resolvconffile = isc_commandline_argument; + if (ns_g_conffileset) + ns_main_earlyfatal("cannot specify -c and -C"); + lwresd_g_useresolvconf = ISC_TRUE; + break; + case 'd': + ns_g_debuglevel = parse_int(isc_commandline_argument, + "debug level"); + break; + case 'f': + ns_g_foreground = ISC_TRUE; + break; + case 'g': + ns_g_foreground = ISC_TRUE; + ns_g_logstderr = ISC_TRUE; + break; + /* XXXBEW -i should be removed */ + case 'i': + lwresd_g_defaultpidfile = isc_commandline_argument; + break; + case 'l': + ns_g_lwresdonly = ISC_TRUE; + break; + case 'm': + set_flags(isc_commandline_argument, mem_debug_flags, + &isc_mem_debugging); + break; + case 'N': /* Deprecated. */ + case 'n': + ns_g_cpus = parse_int(isc_commandline_argument, + "number of cpus"); + if (ns_g_cpus == 0) + ns_g_cpus = 1; + break; + case 'p': + port = parse_int(isc_commandline_argument, "port"); + if (port < 1 || port > 65535) + ns_main_earlyfatal("port '%s' out of range", + isc_commandline_argument); + ns_g_port = port; + break; + /* XXXBEW Should -P be removed? */ + case 'P': + port = parse_int(isc_commandline_argument, "port"); + if (port < 1 || port > 65535) + ns_main_earlyfatal("port '%s' out of range", + isc_commandline_argument); + lwresd_g_listenport = port; + break; + case 's': + /* XXXRTH temporary syntax */ + want_stats = ISC_TRUE; + break; + case 't': + /* XXXJAB should we make a copy? */ + ns_g_chrootdir = isc_commandline_argument; + break; + case 'u': + ns_g_username = isc_commandline_argument; + break; + case 'v': + printf("BIND %s\n", ns_g_version); + exit(0); + case '?': + usage(); + ns_main_earlyfatal("unknown option '-%c'", + isc_commandline_option); + default: + ns_main_earlyfatal("parsing options returned %d", ch); + } + } + + argc -= isc_commandline_index; + argv += isc_commandline_index; + + if (argc > 0) { + usage(); + ns_main_earlyfatal("extra command line arguments"); + } +} + +static isc_result_t +create_managers(void) { + isc_result_t result; +#ifdef ISC_PLATFORM_USETHREADS + unsigned int cpus_detected; +#endif + +#ifdef ISC_PLATFORM_USETHREADS + cpus_detected = isc_os_ncpus(); + if (ns_g_cpus == 0) + ns_g_cpus = cpus_detected; + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_INFO, "found %u CPU%s, using %u worker thread%s", + cpus_detected, cpus_detected == 1 ? "" : "s", + ns_g_cpus, ns_g_cpus == 1 ? "" : "s"); +#else + ns_g_cpus = 1; +#endif + result = isc_taskmgr_create(ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "ns_taskmgr_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + result = isc_timermgr_create(ns_g_mctx, &ns_g_timermgr); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "ns_timermgr_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + result = isc_socketmgr_create(ns_g_mctx, &ns_g_socketmgr); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_socketmgr_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + result = isc_entropy_create(ns_g_mctx, &ns_g_entropy); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_entropy_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + result = isc_hash_create(ns_g_mctx, ns_g_entropy, DNS_NAME_MAXWIRE); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_hash_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +static void +destroy_managers(void) { + ns_lwresd_shutdown(); + + isc_entropy_detach(&ns_g_entropy); + if (ns_g_fallbackentropy != NULL) + isc_entropy_detach(&ns_g_fallbackentropy); + + /* + * isc_taskmgr_destroy() will block until all tasks have exited, + */ + isc_taskmgr_destroy(&ns_g_taskmgr); + isc_timermgr_destroy(&ns_g_timermgr); + isc_socketmgr_destroy(&ns_g_socketmgr); + + /* + * isc_hash_destroy() cannot be called as long as a resolver may be + * running. Calling this after isc_taskmgr_destroy() ensures the + * call is safe. + */ + isc_hash_destroy(); +} + +static void +setup(void) { + isc_result_t result; +#ifdef HAVE_LIBSCF + char *instance = NULL; +#endif + + /* + * Get the user and group information before changing the root + * directory, so the administrator does not need to keep a copy + * of the user and group databases in the chroot'ed environment. + */ + ns_os_inituserinfo(ns_g_username); + + /* + * Initialize time conversion information + */ + ns_os_tzset(); + + ns_os_opendevnull(); + +#ifdef HAVE_LIBSCF + /* Check if named is under smf control, before chroot. */ + result = ns_smf_get_instance(&instance, 0, ns_g_mctx); + /* We don't care about instance, just check if we got one. */ + if (result == ISC_R_SUCCESS) + ns_smf_got_instance = 1; + else + ns_smf_got_instance = 0; + if (instance != NULL) + isc_mem_free(ns_g_mctx, instance); +#endif /* HAVE_LIBSCF */ + +#ifdef PATH_RANDOMDEV + /* + * Initialize system's random device as fallback entropy source + * if running chroot'ed. + */ + if (ns_g_chrootdir != NULL) { + result = isc_entropy_create(ns_g_mctx, &ns_g_fallbackentropy); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("isc_entropy_create() failed: %s", + isc_result_totext(result)); + + result = isc_entropy_createfilesource(ns_g_fallbackentropy, + PATH_RANDOMDEV); + if (result != ISC_R_SUCCESS) { + ns_main_earlywarning("could not open pre-chroot " + "entropy source %s: %s", + PATH_RANDOMDEV, + isc_result_totext(result)); + isc_entropy_detach(&ns_g_fallbackentropy); + } + } +#endif + + ns_os_chroot(ns_g_chrootdir); + + /* + * For operating systems which have a capability mechanism, now + * is the time to switch to minimal privs and change our user id. + * On traditional UNIX systems, this call will be a no-op, and we + * will change the user ID after reading the config file the first + * time. (We need to read the config file to know which possibly + * privileged ports to bind() to.) + */ + ns_os_minprivs(); + + result = ns_log_init(ISC_TF(ns_g_username != NULL)); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("ns_log_init() failed: %s", + isc_result_totext(result)); + + /* + * Now is the time to daemonize (if we're not running in the + * foreground). We waited until now because we wanted to get + * a valid logging context setup. We cannot daemonize any later, + * because calling create_managers() will create threads, which + * would be lost after fork(). + */ + if (!ns_g_foreground) + ns_os_daemonize(); + + /* + * We call isc_app_start() here as some versions of FreeBSD's fork() + * destroys all the signal handling it sets up. + */ + result = isc_app_start(); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("isc_app_start() failed: %s", + isc_result_totext(result)); + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN, + ISC_LOG_NOTICE, "starting BIND %s%s", ns_g_version, + saved_command_line); + + /* + * Get the initial resource limits. + */ + (void)isc_resource_getlimit(isc_resource_stacksize, + &ns_g_initstacksize); + (void)isc_resource_getlimit(isc_resource_datasize, + &ns_g_initdatasize); + (void)isc_resource_getlimit(isc_resource_coresize, + &ns_g_initcoresize); + (void)isc_resource_getlimit(isc_resource_openfiles, + &ns_g_initopenfiles); + + /* + * If the named configuration filename is relative, prepend the current + * directory's name before possibly changing to another directory. + */ + if (! isc_file_isabsolute(ns_g_conffile)) { + result = isc_file_absolutepath(ns_g_conffile, + absolute_conffile, + sizeof(absolute_conffile)); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("could not construct absolute path of " + "configuration file: %s", + isc_result_totext(result)); + ns_g_conffile = absolute_conffile; + } + + result = create_managers(); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("create_managers() failed: %s", + isc_result_totext(result)); + + ns_builtin_init(); + + /* + * Add calls to register sdb drivers here. + */ + /* xxdb_init(); */ + + ns_server_create(ns_g_mctx, &ns_g_server); +} + +static void +cleanup(void) { + destroy_managers(); + + ns_server_destroy(&ns_g_server); + + ns_builtin_deinit(); + + /* + * Add calls to unregister sdb drivers here. + */ + /* xxdb_clear(); */ + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN, + ISC_LOG_NOTICE, "exiting"); + ns_log_shutdown(); +} + +static char *memstats = NULL; + +void +ns_main_setmemstats(const char *filename) { + /* + * Caller has to ensure locking. + */ + + if (memstats != NULL) { + free(memstats); + memstats = NULL; + } + if (filename == NULL) + return; + memstats = malloc(strlen(filename) + 1); + if (memstats) + strcpy(memstats, filename); +} + +#ifdef HAVE_LIBSCF +/* + * Get FMRI for the named process. + */ +isc_result_t +ns_smf_get_instance(char **ins_name, int debug, isc_mem_t *mctx) { + scf_handle_t *h = NULL; + int namelen; + char *instance; + + REQUIRE(ins_name != NULL && *ins_name == NULL); + + if ((h = scf_handle_create(SCF_VERSION)) == NULL) { + if (debug) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "scf_handle_create() failed: %s", + scf_strerror(scf_error())); + return (ISC_R_FAILURE); + } + + if (scf_handle_bind(h) == -1) { + if (debug) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "scf_handle_bind() failed: %s", + scf_strerror(scf_error())); + scf_handle_destroy(h); + return (ISC_R_FAILURE); + } + + if ((namelen = scf_myname(h, NULL, 0)) == -1) { + if (debug) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "scf_myname() failed: %s", + scf_strerror(scf_error())); + scf_handle_destroy(h); + return (ISC_R_FAILURE); + } + + if ((instance = isc_mem_allocate(mctx, namelen + 1)) == NULL) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "ns_smf_get_instance memory " + "allocation failed: %s", + isc_result_totext(ISC_R_NOMEMORY)); + scf_handle_destroy(h); + return (ISC_R_FAILURE); + } + + if (scf_myname(h, instance, namelen + 1) == -1) { + if (debug) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "scf_myname() failed: %s", + scf_strerror(scf_error())); + scf_handle_destroy(h); + isc_mem_free(mctx, instance); + return (ISC_R_FAILURE); + } + + scf_handle_destroy(h); + *ins_name = instance; + return (ISC_R_SUCCESS); +} +#endif /* HAVE_LIBSCF */ + +int +main(int argc, char *argv[]) { + isc_result_t result; +#ifdef HAVE_LIBSCF + char *instance = NULL; +#endif + + /* + * Record version in core image. + * strings named.core | grep "named version:" + */ + strlcat(version, +#ifdef __DATE__ + "named version: BIND " VERSION " (" __DATE__ ")", +#else + "named version: BIND " VERSION, +#endif + sizeof(version)); + result = isc_file_progname(*argv, program_name, sizeof(program_name)); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("program name too long"); + + if (strcmp(program_name, "lwresd") == 0) + ns_g_lwresdonly = ISC_TRUE; + + isc_assertion_setcallback(assertion_failed); + isc_error_setfatal(library_fatal_error); + isc_error_setunexpected(library_unexpected_error); + + ns_os_init(program_name); + + dns_result_register(); + dst_result_register(); + isccc_result_register(); + + parse_command_line(argc, argv); + + /* + * Warn about common configuration error. + */ + if (ns_g_chrootdir != NULL) { + int len = strlen(ns_g_chrootdir); + if (strncmp(ns_g_chrootdir, ns_g_conffile, len) == 0 && + (ns_g_conffile[len] == '/' || ns_g_conffile[len] == '\\')) + ns_main_earlywarning("config filename (-c %s) contains " + "chroot path (-t %s)", + ns_g_conffile, ns_g_chrootdir); + } + + result = isc_mem_create(0, 0, &ns_g_mctx); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("isc_mem_create() failed: %s", + isc_result_totext(result)); + + setup(); + + /* + * Start things running and then wait for a shutdown request + * or reload. + */ + do { + result = isc_app_run(); + + if (result == ISC_R_RELOAD) { + ns_server_reloadwanted(ns_g_server); + } else if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_run(): %s", + isc_result_totext(result)); + /* + * Force exit. + */ + result = ISC_R_SUCCESS; + } + } while (result != ISC_R_SUCCESS); + +#ifdef HAVE_LIBSCF + if (ns_smf_want_disable == 1) { + result = ns_smf_get_instance(&instance, 1, ns_g_mctx); + if (result == ISC_R_SUCCESS && instance != NULL) { + if (smf_disable_instance(instance, 0) != 0) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "smf_disable_instance() ", + "failed for %s : %s", + instance, + scf_strerror(scf_error())); + } + if (instance != NULL) + isc_mem_free(ns_g_mctx, instance); + } +#endif /* HAVE_LIBSCF */ + + cleanup(); + + if (want_stats) { + isc_mem_stats(ns_g_mctx, stdout); + isc_mutex_stats(stdout); + } + if (memstats != NULL) { + FILE *fp = NULL; + result = isc_stdio_open(memstats, "w", &fp); + if (result == ISC_R_SUCCESS) { + isc_mem_stats(ns_g_mctx, fp); + isc_mutex_stats(fp); + isc_stdio_close(fp); + } + } + isc_mem_destroy(&ns_g_mctx); + + ns_main_setmemstats(NULL); + + isc_app_finish(); + + ns_os_closedevnull(); + + ns_os_shutdown(); + + return (0); +} diff -Nuar bind-9.3.2-orig/bin/named/query.c bind-9.3.2-mod/bin/named/query.c --- bind-9.3.2-orig/bin/named/query.c 2005-08-11 07:25:20.000000000 +0200 +++ bind-9.3.2-mod/bin/named/query.c 2006-02-20 19:13:47.000000000 +0100 @@ -27,6 +27,9 @@ #include #include #include +#ifdef DLZ +#include +#endif #include #include #include @@ -817,9 +820,65 @@ { isc_result_t result; +#ifdef DLZ + + isc_result_t tresult; + unsigned int namelabels; + unsigned int zonelabels; + dns_zone_t *zone = NULL; + dns_db_t *tdbp; + + REQUIRE(zonep != NULL && *zonep == NULL); + + tdbp = NULL; + + /* Calculate how many labels are in name. */ + namelabels = dns_name_countlabels(name); + zonelabels = 0; + + /* Try to find name in bind's standard database. */ + result = query_getzonedb(client, name, qtype, options, &zone, dbp, versionp); + + /* See how many labels are in the zone's name. */ + if (result == ISC_R_SUCCESS && zone != NULL) + zonelabels = dns_name_countlabels(dns_zone_getorigin(zone)); + /* + * If # zone labels < # name labels, try to find an even better match + * Only try if a DLZ driver is loaded for this view + */ + if(zonelabels < namelabels && client->view->dlzdatabase != NULL){ + tresult = dns_dlzfindzone(client->view, name, zonelabels, &tdbp); + /* If we successful, we found a better match. */ + if(tresult == ISC_R_SUCCESS){ + /* If the previous search returned a zone, detach it. */ + if(zone != NULL) + dns_zone_detach(&zone); + /* If the previous search returned a database, detach it. */ + if(*dbp != NULL) + dns_db_detach(dbp); + /* incase the previous search returned a version, clear it */ + *versionp = NULL; + /* get our database version */ + dns_db_currentversion(tdbp, versionp); + /* be sure to return our database */ + *dbp = tdbp; + /* We return a null zone, No stats for DLZ zones */ + zone = NULL; + result = tresult; + } + } + +#else result = query_getzonedb(client, name, qtype, options, zonep, dbp, versionp); - if (result == ISC_R_SUCCESS) { + +#endif + /* If successfull, Transfer ownership of zone. */ + if(result == ISC_R_SUCCESS){ +#ifdef DLZ + *zonep = zone; +#endif + /* If neither attempt above succeeded, return the cache instead */ *is_zonep = ISC_TRUE; } else if (result == ISC_R_NOTFOUND) { result = query_getcachedb(client, name, qtype, dbp, options); @@ -2521,7 +2580,14 @@ if (event == NULL && client->query.restarts == 0) { if (is_zone) { - dns_zone_attach(zone, &client->query.authzone); +#ifdef DLZ + if(zone != NULL){ // if is_zone = true, zone = NULL then this is + // a DLZ zone. Don't attempt to attach zone +#endif + dns_zone_attach(zone, &client->query.authzone); +#ifdef DLZ + } +#endif dns_db_attach(db, &client->query.authdb); } client->query.authdbset = ISC_TRUE; diff -Nuar bind-9.3.2-orig/bin/named/query.c.orig bind-9.3.2-mod/bin/named/query.c.orig --- bind-9.3.2-orig/bin/named/query.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/query.c.orig 2005-08-11 07:25:20.000000000 +0200 @@ -0,0 +1,3553 @@ +/* + * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2003 Internet Software Consortium. + * + * 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: query.c,v 1.198.2.13.4.36 2005/08/11 05:25:20 marka Exp $ */ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define PARTIALANSWER(c) (((c)->query.attributes & \ + NS_QUERYATTR_PARTIALANSWER) != 0) +#define USECACHE(c) (((c)->query.attributes & \ + NS_QUERYATTR_CACHEOK) != 0) +#define RECURSIONOK(c) (((c)->query.attributes & \ + NS_QUERYATTR_RECURSIONOK) != 0) +#define RECURSING(c) (((c)->query.attributes & \ + NS_QUERYATTR_RECURSING) != 0) +#define CACHEGLUEOK(c) (((c)->query.attributes & \ + NS_QUERYATTR_CACHEGLUEOK) != 0) +#define WANTRECURSION(c) (((c)->query.attributes & \ + NS_QUERYATTR_WANTRECURSION) != 0) +#define WANTDNSSEC(c) (((c)->attributes & \ + NS_CLIENTATTR_WANTDNSSEC) != 0) +#define NOAUTHORITY(c) (((c)->query.attributes & \ + NS_QUERYATTR_NOAUTHORITY) != 0) +#define NOADDITIONAL(c) (((c)->query.attributes & \ + NS_QUERYATTR_NOADDITIONAL) != 0) +#define SECURE(c) (((c)->query.attributes & \ + NS_QUERYATTR_SECURE) != 0) + +#if 0 +#define CTRACE(m) isc_log_write(ns_g_lctx, \ + NS_LOGCATEGORY_CLIENT, \ + NS_LOGMODULE_QUERY, \ + ISC_LOG_DEBUG(3), \ + "client %p: %s", client, (m)) +#define QTRACE(m) isc_log_write(ns_g_lctx, \ + NS_LOGCATEGORY_GENERAL, \ + NS_LOGMODULE_QUERY, \ + ISC_LOG_DEBUG(3), \ + "query %p: %s", query, (m)) +#else +#define CTRACE(m) ((void)m) +#define QTRACE(m) ((void)m) +#endif + +#define DNS_GETDB_NOEXACT 0x01U +#define DNS_GETDB_NOLOG 0x02U +#define DNS_GETDB_PARTIAL 0x04U + +static void +query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype); + +/* + * Increment query statistics counters. + */ +static inline void +inc_stats(ns_client_t *client, dns_statscounter_t counter) { + dns_zone_t *zone = client->query.authzone; + + REQUIRE(counter < DNS_STATS_NCOUNTERS); + + ns_g_server->querystats[counter]++; + + if (zone != NULL) { + isc_uint64_t *zonestats = dns_zone_getstatscounters(zone); + if (zonestats != NULL) + zonestats[counter]++; + } +} + +static void +query_send(ns_client_t *client) { + dns_statscounter_t counter; + if (client->message->rcode == dns_rcode_noerror) { + if (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER])) { + if (client->query.isreferral) { + counter = dns_statscounter_referral; + } else { + counter = dns_statscounter_nxrrset; + } + } else { + counter = dns_statscounter_success; + } + } else if (client->message->rcode == dns_rcode_nxdomain) { + counter = dns_statscounter_nxdomain; + } else { + /* We end up here in case of YXDOMAIN, and maybe others */ + counter = dns_statscounter_failure; + } + inc_stats(client, counter); + ns_client_send(client); +} + +static void +query_error(ns_client_t *client, isc_result_t result) { + inc_stats(client, dns_statscounter_failure); + ns_client_error(client, result); +} + +static void +query_next(ns_client_t *client, isc_result_t result) { + inc_stats(client, dns_statscounter_failure); + ns_client_next(client, result); +} + +static inline void +query_maybeputqname(ns_client_t *client) { + if (client->query.restarts > 0) { + /* + * client->query.qname was dynamically allocated. + */ + dns_message_puttempname(client->message, + &client->query.qname); + client->query.qname = NULL; + } +} + +static inline void +query_freefreeversions(ns_client_t *client, isc_boolean_t everything) { + ns_dbversion_t *dbversion, *dbversion_next; + unsigned int i; + + for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0; + dbversion != NULL; + dbversion = dbversion_next, i++) + { + dbversion_next = ISC_LIST_NEXT(dbversion, link); + /* + * If we're not freeing everything, we keep the first three + * dbversions structures around. + */ + if (i > 3 || everything) { + ISC_LIST_UNLINK(client->query.freeversions, dbversion, + link); + isc_mem_put(client->mctx, dbversion, + sizeof(*dbversion)); + } + } +} + +void +ns_query_cancel(ns_client_t *client) { + LOCK(&client->query.fetchlock); + if (client->query.fetch != NULL) { + dns_resolver_cancelfetch(client->query.fetch); + + client->query.fetch = NULL; + } + UNLOCK(&client->query.fetchlock); +} + +static inline void +query_reset(ns_client_t *client, isc_boolean_t everything) { + isc_buffer_t *dbuf, *dbuf_next; + ns_dbversion_t *dbversion, *dbversion_next; + + /* + * Reset the query state of a client to its default state. + */ + + /* + * Cancel the fetch if it's running. + */ + ns_query_cancel(client); + + /* + * Cleanup any active versions. + */ + for (dbversion = ISC_LIST_HEAD(client->query.activeversions); + dbversion != NULL; + dbversion = dbversion_next) { + dbversion_next = ISC_LIST_NEXT(dbversion, link); + dns_db_closeversion(dbversion->db, &dbversion->version, + ISC_FALSE); + dns_db_detach(&dbversion->db); + ISC_LIST_INITANDAPPEND(client->query.freeversions, + dbversion, link); + } + ISC_LIST_INIT(client->query.activeversions); + + if (client->query.authdb != NULL) + dns_db_detach(&client->query.authdb); + if (client->query.authzone != NULL) + dns_zone_detach(&client->query.authzone); + + query_freefreeversions(client, everything); + + for (dbuf = ISC_LIST_HEAD(client->query.namebufs); + dbuf != NULL; + dbuf = dbuf_next) { + dbuf_next = ISC_LIST_NEXT(dbuf, link); + if (dbuf_next != NULL || everything) { + ISC_LIST_UNLINK(client->query.namebufs, dbuf, link); + isc_buffer_free(&dbuf); + } + } + + query_maybeputqname(client); + + client->query.attributes = (NS_QUERYATTR_RECURSIONOK | + NS_QUERYATTR_CACHEOK | + NS_QUERYATTR_SECURE); + client->query.restarts = 0; + client->query.timerset = ISC_FALSE; + client->query.origqname = NULL; + client->query.qname = NULL; + client->query.dboptions = 0; + client->query.fetchoptions = 0; + client->query.gluedb = NULL; + client->query.authdbset = ISC_FALSE; + client->query.isreferral = ISC_FALSE; +} + +static void +query_next_callback(ns_client_t *client) { + query_reset(client, ISC_FALSE); +} + +void +ns_query_free(ns_client_t *client) { + query_reset(client, ISC_TRUE); +} + +static inline isc_result_t +query_newnamebuf(ns_client_t *client) { + isc_buffer_t *dbuf; + isc_result_t result; + + CTRACE("query_newnamebuf"); + /* + * Allocate a name buffer. + */ + + dbuf = NULL; + result = isc_buffer_allocate(client->mctx, &dbuf, 1024); + if (result != ISC_R_SUCCESS) { + CTRACE("query_newnamebuf: isc_buffer_allocate failed: done"); + return (result); + } + ISC_LIST_APPEND(client->query.namebufs, dbuf, link); + + CTRACE("query_newnamebuf: done"); + return (ISC_R_SUCCESS); +} + +static inline isc_buffer_t * +query_getnamebuf(ns_client_t *client) { + isc_buffer_t *dbuf; + isc_result_t result; + isc_region_t r; + + CTRACE("query_getnamebuf"); + /* + * Return a name buffer with space for a maximal name, allocating + * a new one if necessary. + */ + + if (ISC_LIST_EMPTY(client->query.namebufs)) { + result = query_newnamebuf(client); + if (result != ISC_R_SUCCESS) { + CTRACE("query_getnamebuf: query_newnamebuf failed: done"); + return (NULL); + } + } + + dbuf = ISC_LIST_TAIL(client->query.namebufs); + INSIST(dbuf != NULL); + isc_buffer_availableregion(dbuf, &r); + if (r.length < 255) { + result = query_newnamebuf(client); + if (result != ISC_R_SUCCESS) { + CTRACE("query_getnamebuf: query_newnamebuf failed: done"); + return (NULL); + + } + dbuf = ISC_LIST_TAIL(client->query.namebufs); + isc_buffer_availableregion(dbuf, &r); + INSIST(r.length >= 255); + } + CTRACE("query_getnamebuf: done"); + return (dbuf); +} + +static inline void +query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) { + isc_region_t r; + + CTRACE("query_keepname"); + /* + * 'name' is using space in 'dbuf', but 'dbuf' has not yet been + * adjusted to take account of that. We do the adjustment. + */ + + REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0); + + dns_name_toregion(name, &r); + isc_buffer_add(dbuf, r.length); + dns_name_setbuffer(name, NULL); + client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED; +} + +static inline void +query_releasename(ns_client_t *client, dns_name_t **namep) { + dns_name_t *name = *namep; + + /* + * 'name' is no longer needed. Return it to our pool of temporary + * names. If it is using a name buffer, relinquish its exclusive + * rights on the buffer. + */ + + CTRACE("query_releasename"); + if (dns_name_hasbuffer(name)) { + INSIST((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) + != 0); + client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED; + } + dns_message_puttempname(client->message, namep); + CTRACE("query_releasename: done"); +} + +static inline dns_name_t * +query_newname(ns_client_t *client, isc_buffer_t *dbuf, + isc_buffer_t *nbuf) +{ + dns_name_t *name; + isc_region_t r; + isc_result_t result; + + REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0); + + CTRACE("query_newname"); + name = NULL; + result = dns_message_gettempname(client->message, &name); + if (result != ISC_R_SUCCESS) { + CTRACE("query_newname: dns_message_gettempname failed: done"); + return (NULL); + } + isc_buffer_availableregion(dbuf, &r); + isc_buffer_init(nbuf, r.base, r.length); + dns_name_init(name, NULL); + dns_name_setbuffer(name, nbuf); + client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED; + + CTRACE("query_newname: done"); + return (name); +} + +static inline dns_rdataset_t * +query_newrdataset(ns_client_t *client) { + dns_rdataset_t *rdataset; + isc_result_t result; + + CTRACE("query_newrdataset"); + rdataset = NULL; + result = dns_message_gettemprdataset(client->message, &rdataset); + if (result != ISC_R_SUCCESS) { + CTRACE("query_newrdataset: " + "dns_message_gettemprdataset failed: done"); + return (NULL); + } + dns_rdataset_init(rdataset); + + CTRACE("query_newrdataset: done"); + return (rdataset); +} + +static inline void +query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) { + dns_rdataset_t *rdataset = *rdatasetp; + + CTRACE("query_putrdataset"); + if (rdataset != NULL) { + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + dns_message_puttemprdataset(client->message, rdatasetp); + } + CTRACE("query_putrdataset: done"); +} + + +static inline isc_result_t +query_newdbversion(ns_client_t *client, unsigned int n) { + unsigned int i; + ns_dbversion_t *dbversion; + + for (i = 0; i < n; i++) { + dbversion = isc_mem_get(client->mctx, sizeof(*dbversion)); + if (dbversion != NULL) { + dbversion->db = NULL; + dbversion->version = NULL; + ISC_LIST_INITANDAPPEND(client->query.freeversions, + dbversion, link); + } else { + /* + * We only return ISC_R_NOMEMORY if we couldn't + * allocate anything. + */ + if (i == 0) + return (ISC_R_NOMEMORY); + else + return (ISC_R_SUCCESS); + } + } + + return (ISC_R_SUCCESS); +} + +static inline ns_dbversion_t * +query_getdbversion(ns_client_t *client) { + isc_result_t result; + ns_dbversion_t *dbversion; + + if (ISC_LIST_EMPTY(client->query.freeversions)) { + result = query_newdbversion(client, 1); + if (result != ISC_R_SUCCESS) + return (NULL); + } + dbversion = ISC_LIST_HEAD(client->query.freeversions); + INSIST(dbversion != NULL); + ISC_LIST_UNLINK(client->query.freeversions, dbversion, link); + + return (dbversion); +} + +isc_result_t +ns_query_init(ns_client_t *client) { + isc_result_t result; + + ISC_LIST_INIT(client->query.namebufs); + ISC_LIST_INIT(client->query.activeversions); + ISC_LIST_INIT(client->query.freeversions); + client->query.restarts = 0; + client->query.timerset = ISC_FALSE; + client->query.qname = NULL; + result = isc_mutex_init(&client->query.fetchlock); + if (result != ISC_R_SUCCESS) + return (result); + client->query.fetch = NULL; + client->query.authdb = NULL; + client->query.authzone = NULL; + client->query.authdbset = ISC_FALSE; + client->query.isreferral = ISC_FALSE; + query_reset(client, ISC_FALSE); + result = query_newdbversion(client, 3); + if (result != ISC_R_SUCCESS) { + DESTROYLOCK(&client->query.fetchlock); + return (result); + } + result = query_newnamebuf(client); + if (result != ISC_R_SUCCESS) + query_freefreeversions(client, ISC_TRUE); + + return (result); +} + +static inline ns_dbversion_t * +query_findversion(ns_client_t *client, dns_db_t *db, + isc_boolean_t *newzonep) +{ + ns_dbversion_t *dbversion; + + /* + * We may already have done a query related to this + * database. If so, we must be sure to make subsequent + * queries from the same version. + */ + for (dbversion = ISC_LIST_HEAD(client->query.activeversions); + dbversion != NULL; + dbversion = ISC_LIST_NEXT(dbversion, link)) { + if (dbversion->db == db) + break; + } + + if (dbversion == NULL) { + /* + * This is a new zone for this query. Add it to + * the active list. + */ + dbversion = query_getdbversion(client); + if (dbversion == NULL) + return (NULL); + dns_db_attach(db, &dbversion->db); + dns_db_currentversion(db, &dbversion->version); + dbversion->queryok = ISC_FALSE; + ISC_LIST_APPEND(client->query.activeversions, + dbversion, link); + *newzonep = ISC_TRUE; + } else + *newzonep = ISC_FALSE; + + return (dbversion); +} + +static inline isc_result_t +query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, + unsigned int options, dns_zone_t **zonep, dns_db_t **dbp, + dns_dbversion_t **versionp) +{ + isc_result_t result; + isc_boolean_t check_acl, new_zone; + dns_acl_t *queryacl; + ns_dbversion_t *dbversion; + unsigned int ztoptions; + dns_zone_t *zone = NULL; + dns_db_t *db = NULL; + isc_boolean_t partial = ISC_FALSE; + + REQUIRE(zonep != NULL && *zonep == NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + /* + * Find a zone database to answer the query. + */ + ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ? + DNS_ZTFIND_NOEXACT : 0; + + result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL, + &zone); + if (result == DNS_R_PARTIALMATCH) + partial = ISC_TRUE; + if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) + result = dns_zone_getdb(zone, &db); + + if (result != ISC_R_SUCCESS) + goto fail; + + /* + * This limits our searching to the zone where the first name + * (the query target) was looked for. This prevents following + * CNAMES or DNAMES into other zones and prevents returning + * additional data from other zones. + */ + if (!client->view->additionalfromauth && + client->query.authdbset && + db != client->query.authdb) + goto refuse; + + /* + * If the zone has an ACL, we'll check it, otherwise + * we use the view's "allow-query" ACL. Each ACL is only checked + * once per query. + * + * Also, get the database version to use. + */ + + check_acl = ISC_TRUE; /* Keep compiler happy. */ + queryacl = NULL; + + /* + * Get the current version of this database. + */ + dbversion = query_findversion(client, db, &new_zone); + if (dbversion == NULL) { + result = DNS_R_SERVFAIL; + goto fail; + } + if (new_zone) { + check_acl = ISC_TRUE; + } else if (!dbversion->queryok) { + goto refuse; + } else { + check_acl = ISC_FALSE; + } + + queryacl = dns_zone_getqueryacl(zone); + if (queryacl == NULL) { + queryacl = client->view->queryacl; + if ((client->query.attributes & + NS_QUERYATTR_QUERYOKVALID) != 0) { + /* + * We've evaluated the view's queryacl already. If + * NS_QUERYATTR_QUERYOK is set, then the client is + * allowed to make queries, otherwise the query should + * be refused. + */ + check_acl = ISC_FALSE; + if ((client->query.attributes & + NS_QUERYATTR_QUERYOK) == 0) + goto refuse; + } else { + /* + * We haven't evaluated the view's queryacl yet. + */ + check_acl = ISC_TRUE; + } + } + + if (check_acl) { + isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0); + + result = ns_client_checkaclsilent(client, queryacl, ISC_TRUE); + if (log) { + char msg[NS_CLIENT_ACLMSGSIZE("query")]; + if (result == ISC_R_SUCCESS) { + if (isc_log_wouldlog(ns_g_lctx, + ISC_LOG_DEBUG(3))) + { + ns_client_aclmsg("query", name, qtype, + client->view->rdclass, + msg, sizeof(msg)); + ns_client_log(client, + DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_QUERY, + ISC_LOG_DEBUG(3), + "%s approved", msg); + } + } else { + ns_client_aclmsg("query", name, qtype, + client->view->rdclass, + msg, sizeof(msg)); + ns_client_log(client, DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_QUERY, ISC_LOG_INFO, + "%s denied", msg); + } + } + + if (queryacl == client->view->queryacl) { + if (result == ISC_R_SUCCESS) { + /* + * We were allowed by the default + * "allow-query" ACL. Remember this so we + * don't have to check again. + */ + client->query.attributes |= + NS_QUERYATTR_QUERYOK; + } + /* + * We've now evaluated the view's query ACL, and + * the NS_QUERYATTR_QUERYOK attribute is now valid. + */ + client->query.attributes |= NS_QUERYATTR_QUERYOKVALID; + } + + if (result != ISC_R_SUCCESS) + goto refuse; + } + + /* Approved. */ + + /* + * Remember the result of the ACL check so we + * don't have to check again. + */ + dbversion->queryok = ISC_TRUE; + + /* Transfer ownership. */ + *zonep = zone; + *dbp = db; + *versionp = dbversion->version; + + if (partial && (options & DNS_GETDB_PARTIAL) != 0) + return (DNS_R_PARTIALMATCH); + return (ISC_R_SUCCESS); + + refuse: + result = DNS_R_REFUSED; + fail: + if (zone != NULL) + dns_zone_detach(&zone); + if (db != NULL) + dns_db_detach(&db); + + return (result); +} + +static inline isc_result_t +query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, + dns_db_t **dbp, unsigned int options) +{ + isc_result_t result; + isc_boolean_t check_acl; + dns_db_t *db = NULL; + + REQUIRE(dbp != NULL && *dbp == NULL); + + /* + * Find a cache database to answer the query. + * This may fail with DNS_R_REFUSED if the client + * is not allowed to use the cache. + */ + + if (!USECACHE(client)) + return (DNS_R_REFUSED); + dns_db_attach(client->view->cachedb, &db); + + if ((client->query.attributes & + NS_QUERYATTR_QUERYOKVALID) != 0) { + /* + * We've evaluated the view's queryacl already. If + * NS_QUERYATTR_QUERYOK is set, then the client is + * allowed to make queries, otherwise the query should + * be refused. + */ + check_acl = ISC_FALSE; + if ((client->query.attributes & + NS_QUERYATTR_QUERYOK) == 0) + goto refuse; + } else { + /* + * We haven't evaluated the view's queryacl yet. + */ + check_acl = ISC_TRUE; + } + + if (check_acl) { + isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0); + char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")]; + + result = ns_client_checkaclsilent(client, + client->view->queryacl, + ISC_TRUE); + if (result == ISC_R_SUCCESS) { + /* + * We were allowed by the default + * "allow-query" ACL. Remember this so we + * don't have to check again. + */ + client->query.attributes |= + NS_QUERYATTR_QUERYOK; + if (log && isc_log_wouldlog(ns_g_lctx, + ISC_LOG_DEBUG(3))) + { + ns_client_aclmsg("query (cache)", name, qtype, + client->view->rdclass, + msg, sizeof(msg)); + ns_client_log(client, + DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_QUERY, + ISC_LOG_DEBUG(3), + "%s approved", msg); + } + } else if (log) { + ns_client_aclmsg("query (cache)", name, qtype, + client->view->rdclass, msg, + sizeof(msg)); + ns_client_log(client, DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_QUERY, ISC_LOG_INFO, + "%s denied", msg); + } + /* + * We've now evaluated the view's query ACL, and + * the NS_QUERYATTR_QUERYOK attribute is now valid. + */ + client->query.attributes |= NS_QUERYATTR_QUERYOKVALID; + + if (result != ISC_R_SUCCESS) + goto refuse; + } + + /* Approved. */ + + /* Transfer ownership. */ + *dbp = db; + + return (ISC_R_SUCCESS); + + refuse: + result = DNS_R_REFUSED; + + if (db != NULL) + dns_db_detach(&db); + + return (result); +} + + +static inline isc_result_t +query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, + unsigned int options, dns_zone_t **zonep, dns_db_t **dbp, + dns_dbversion_t **versionp, isc_boolean_t *is_zonep) +{ + isc_result_t result; + + result = query_getzonedb(client, name, qtype, options, + zonep, dbp, versionp); + if (result == ISC_R_SUCCESS) { + *is_zonep = ISC_TRUE; + } else if (result == ISC_R_NOTFOUND) { + result = query_getcachedb(client, name, qtype, dbp, options); + *is_zonep = ISC_FALSE; + } + return (result); +} + +static inline isc_boolean_t +query_isduplicate(ns_client_t *client, dns_name_t *name, + dns_rdatatype_t type, dns_name_t **mnamep) +{ + dns_section_t section; + dns_name_t *mname = NULL; + isc_result_t result; + + CTRACE("query_isduplicate"); + + for (section = DNS_SECTION_ANSWER; + section <= DNS_SECTION_ADDITIONAL; + section++) { + result = dns_message_findname(client->message, section, + name, type, 0, &mname, NULL); + if (result == ISC_R_SUCCESS) { + /* + * We've already got this RRset in the response. + */ + CTRACE("query_isduplicate: true: done"); + return (ISC_TRUE); + } else if (result == DNS_R_NXRRSET) { + /* + * The name exists, but the rdataset does not. + */ + if (section == DNS_SECTION_ADDITIONAL) + break; + } else + RUNTIME_CHECK(result == DNS_R_NXDOMAIN); + mname = NULL; + } + + /* + * If the dns_name_t we're looking up is already in the message, + * we don't want to trigger the caller's name replacement logic. + */ + if (name == mname) + mname = NULL; + + *mnamep = mname; + + CTRACE("query_isduplicate: false: done"); + return (ISC_FALSE); +} + +static isc_result_t +query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { + ns_client_t *client = arg; + isc_result_t result, eresult; + dns_dbnode_t *node; + dns_db_t *db; + dns_name_t *fname, *mname; + dns_rdataset_t *rdataset, *sigrdataset, *trdataset; + isc_buffer_t *dbuf; + isc_buffer_t b; + dns_dbversion_t *version; + isc_boolean_t added_something, need_addname; + dns_zone_t *zone; + dns_rdatatype_t type; + + REQUIRE(NS_CLIENT_VALID(client)); + REQUIRE(qtype != dns_rdatatype_any); + + if (!WANTDNSSEC(client) && dns_rdatatype_isdnssec(qtype)) + return (ISC_R_SUCCESS); + + CTRACE("query_addadditional"); + + /* + * Initialization. + */ + eresult = ISC_R_SUCCESS; + fname = NULL; + rdataset = NULL; + sigrdataset = NULL; + trdataset = NULL; + db = NULL; + version = NULL; + node = NULL; + added_something = ISC_FALSE; + need_addname = ISC_FALSE; + zone = NULL; + + /* + * We treat type A additional section processing as if it + * were "any address type" additional section processing. + * To avoid multiple lookups, we do an 'any' database + * lookup and iterate over the node. + */ + if (qtype == dns_rdatatype_a) + type = dns_rdatatype_any; + else + type = qtype; + + /* + * Get some resources. + */ + dbuf = query_getnamebuf(client); + if (dbuf == NULL) + goto cleanup; + fname = query_newname(client, dbuf, &b); + rdataset = query_newrdataset(client); + if (fname == NULL || rdataset == NULL) + goto cleanup; + if (WANTDNSSEC(client)) { + sigrdataset = query_newrdataset(client); + if (sigrdataset == NULL) + goto cleanup; + } + + /* + * Look for a zone database that might contain authoritative + * additional data. + */ + result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG, + &zone, &db, &version); + if (result != ISC_R_SUCCESS) + goto try_cache; + + CTRACE("query_addadditional: db_find"); + + /* + * Since we are looking for authoritative data, we do not set + * the GLUEOK flag. Glue will be looked for later, but not + * necessarily in the same database. + */ + node = NULL; + result = dns_db_find(db, name, version, type, client->query.dboptions, + client->now, &node, fname, rdataset, + sigrdataset); + if (result == ISC_R_SUCCESS) + goto found; + + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) + dns_rdataset_disassociate(sigrdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + version = NULL; + dns_db_detach(&db); + + /* + * No authoritative data was found. The cache is our next best bet. + */ + + try_cache: + result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG); + if (result != ISC_R_SUCCESS) + /* + * Most likely the client isn't allowed to query the cache. + */ + goto try_glue; + + result = dns_db_find(db, name, version, type, client->query.dboptions, + client->now, &node, fname, rdataset, + sigrdataset); + if (result == ISC_R_SUCCESS) + goto found; + + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) + dns_rdataset_disassociate(sigrdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + dns_db_detach(&db); + + try_glue: + /* + * No cached data was found. Glue is our last chance. + * RFC1035 sayeth: + * + * NS records cause both the usual additional section + * processing to locate a type A record, and, when used + * in a referral, a special search of the zone in which + * they reside for glue information. + * + * This is the "special search". Note that we must search + * the zone where the NS record resides, not the zone it + * points to, and that we only do the search in the delegation + * case (identified by client->query.gluedb being set). + */ + + if (client->query.gluedb == NULL) + goto cleanup; + + /* + * Don't poision caches using the bailiwick protection model. + */ + if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb))) + goto cleanup; + + dns_db_attach(client->query.gluedb, &db); + result = dns_db_find(db, name, version, type, + client->query.dboptions | DNS_DBFIND_GLUEOK, + client->now, &node, fname, rdataset, + sigrdataset); + if (!(result == ISC_R_SUCCESS || + result == DNS_R_ZONECUT || + result == DNS_R_GLUE)) + goto cleanup; + + found: + /* + * We have found a potential additional data rdataset, or + * at least a node to iterate over. + */ + query_keepname(client, fname, dbuf); + + /* + * If we have an rdataset, add it to the additional data + * section. + */ + mname = NULL; + if (dns_rdataset_isassociated(rdataset) && + !query_isduplicate(client, fname, type, &mname)) { + if (mname != NULL) { + query_releasename(client, &fname); + fname = mname; + } else + need_addname = ISC_TRUE; + ISC_LIST_APPEND(fname->list, rdataset, link); + trdataset = rdataset; + rdataset = NULL; + added_something = ISC_TRUE; + /* + * Note: we only add SIGs if we've added the type they cover, + * so we do not need to check if the SIG rdataset is already + * in the response. + */ + if (sigrdataset != NULL && + dns_rdataset_isassociated(sigrdataset)) + { + ISC_LIST_APPEND(fname->list, sigrdataset, link); + sigrdataset = NULL; + } + } + + if (qtype == dns_rdatatype_a) { + /* + * We now go looking for A and AAAA records, along with + * their signatures. + * + * XXXRTH This code could be more efficient. + */ + if (rdataset != NULL) { + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + } else { + rdataset = query_newrdataset(client); + if (rdataset == NULL) + goto addname; + } + if (sigrdataset != NULL) { + if (dns_rdataset_isassociated(sigrdataset)) + dns_rdataset_disassociate(sigrdataset); + } else if (WANTDNSSEC(client)) { + sigrdataset = query_newrdataset(client); + if (sigrdataset == NULL) + goto addname; + } + result = dns_db_findrdataset(db, node, version, + dns_rdatatype_a, 0, + client->now, rdataset, + sigrdataset); + if (result == DNS_R_NCACHENXDOMAIN) + goto addname; + if (result == DNS_R_NCACHENXRRSET) { + dns_rdataset_disassociate(rdataset); + /* + * Negative cache entries don't have sigrdatasets. + */ + INSIST(sigrdataset == NULL || + ! dns_rdataset_isassociated(sigrdataset)); + } + if (result == ISC_R_SUCCESS) { + mname = NULL; + if (!query_isduplicate(client, fname, + dns_rdatatype_a, &mname)) { + if (mname != NULL) { + query_releasename(client, &fname); + fname = mname; + } else + need_addname = ISC_TRUE; + ISC_LIST_APPEND(fname->list, rdataset, link); + added_something = ISC_TRUE; + if (sigrdataset != NULL && + dns_rdataset_isassociated(sigrdataset)) + { + ISC_LIST_APPEND(fname->list, + sigrdataset, link); + sigrdataset = + query_newrdataset(client); + } + rdataset = query_newrdataset(client); + if (rdataset == NULL) + goto addname; + if (WANTDNSSEC(client) && sigrdataset == NULL) + goto addname; + } else { + dns_rdataset_disassociate(rdataset); + if (sigrdataset != NULL && + dns_rdataset_isassociated(sigrdataset)) + dns_rdataset_disassociate(sigrdataset); + } + } + result = dns_db_findrdataset(db, node, version, + dns_rdatatype_aaaa, 0, + client->now, rdataset, + sigrdataset); + if (result == DNS_R_NCACHENXDOMAIN) + goto addname; + if (result == DNS_R_NCACHENXRRSET) { + dns_rdataset_disassociate(rdataset); + INSIST(sigrdataset == NULL || + ! dns_rdataset_isassociated(sigrdataset)); + } + if (result == ISC_R_SUCCESS) { + mname = NULL; + if (!query_isduplicate(client, fname, + dns_rdatatype_aaaa, &mname)) { + if (mname != NULL) { + query_releasename(client, &fname); + fname = mname; + } else + need_addname = ISC_TRUE; + ISC_LIST_APPEND(fname->list, rdataset, link); + added_something = ISC_TRUE; + if (sigrdataset != NULL && + dns_rdataset_isassociated(sigrdataset)) + { + ISC_LIST_APPEND(fname->list, + sigrdataset, link); + sigrdataset = NULL; + } + rdataset = NULL; + } + } + } + + addname: + CTRACE("query_addadditional: addname"); + /* + * If we haven't added anything, then we're done. + */ + if (!added_something) + goto cleanup; + + /* + * We may have added our rdatasets to an existing name, if so, then + * need_addname will be ISC_FALSE. Whether we used an existing name + * or a new one, we must set fname to NULL to prevent cleanup. + */ + if (need_addname) + dns_message_addname(client->message, fname, + DNS_SECTION_ADDITIONAL); + fname = NULL; + + /* + * In a few cases, we want to add additional data for additional + * data. It's simpler to just deal with special cases here than + * to try to create a general purpose mechanism and allow the + * rdata implementations to do it themselves. + * + * This involves recursion, but the depth is limited. The + * most complex case is adding a SRV rdataset, which involves + * recursing to add address records, which in turn can cause + * recursion to add KEYs. + */ + if (type == dns_rdatatype_srv && trdataset != NULL) { + /* + * If we're adding SRV records to the additional data + * section, it's helpful if we add the SRV additional data + * as well. + */ + eresult = dns_rdataset_additionaldata(trdataset, + query_addadditional, + client); + } + + cleanup: + CTRACE("query_addadditional: cleanup"); + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + if (fname != NULL) + query_releasename(client, &fname); + if (node != NULL) + dns_db_detachnode(db, &node); + if (db != NULL) + dns_db_detach(&db); + if (zone != NULL) + dns_zone_detach(&zone); + + CTRACE("query_addadditional: done"); + return (eresult); +} + +static inline void +query_addrdataset(ns_client_t *client, dns_name_t *fname, + dns_rdataset_t *rdataset) +{ + /* + * Add 'rdataset' and any pertinent additional data to + * 'fname', a name in the response message for 'client'. + */ + + CTRACE("query_addrdataset"); + + ISC_LIST_APPEND(fname->list, rdataset, link); + + if (client->view->order != NULL) + rdataset->attributes |= dns_order_find(client->view->order, + fname, rdataset->type, + rdataset->rdclass); + if (NOADDITIONAL(client)) + return; + + /* + * Add additional data. + * + * We don't care if dns_rdataset_additionaldata() fails. + */ + (void)dns_rdataset_additionaldata(rdataset, + query_addadditional, client); + CTRACE("query_addrdataset: done"); +} + +static void +query_addrrset(ns_client_t *client, dns_name_t **namep, + dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp, + isc_buffer_t *dbuf, dns_section_t section) +{ + dns_name_t *name, *mname; + dns_rdataset_t *rdataset, *mrdataset, *sigrdataset; + isc_result_t result; + + /* + * To the current response for 'client', add the answer RRset + * '*rdatasetp' and an optional signature set '*sigrdatasetp', with + * owner name '*namep', to section 'section', unless they are + * already there. Also add any pertinent additional data. + * + * If 'dbuf' is not NULL, then '*namep' is the name whose data is + * stored in 'dbuf'. In this case, query_addrrset() guarantees that + * when it returns the name will either have been kept or released. + */ + CTRACE("query_addrrset"); + name = *namep; + rdataset = *rdatasetp; + if (sigrdatasetp != NULL) + sigrdataset = *sigrdatasetp; + else + sigrdataset = NULL; + mname = NULL; + mrdataset = NULL; + result = dns_message_findname(client->message, section, + name, rdataset->type, rdataset->covers, + &mname, &mrdataset); + if (result == ISC_R_SUCCESS) { + /* + * We've already got an RRset of the given name and type. + * There's nothing else to do; + */ + CTRACE("query_addrrset: dns_message_findname succeeded: done"); + if (dbuf != NULL) + query_releasename(client, namep); + return; + } else if (result == DNS_R_NXDOMAIN) { + /* + * The name doesn't exist. + */ + if (dbuf != NULL) + query_keepname(client, name, dbuf); + dns_message_addname(client->message, name, section); + *namep = NULL; + mname = name; + } else { + RUNTIME_CHECK(result == DNS_R_NXRRSET); + if (dbuf != NULL) + query_releasename(client, namep); + } + + if (rdataset->trust != dns_trust_secure && + (section == DNS_SECTION_ANSWER || + section == DNS_SECTION_AUTHORITY)) + client->query.attributes &= ~NS_QUERYATTR_SECURE; + /* + * Note: we only add SIGs if we've added the type they cover, so + * we do not need to check if the SIG rdataset is already in the + * response. + */ + query_addrdataset(client, mname, rdataset); + *rdatasetp = NULL; + if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { + /* + * We have a signature. Add it to the response. + */ + ISC_LIST_APPEND(mname->list, sigrdataset, link); + *sigrdatasetp = NULL; + } + CTRACE("query_addrrset: done"); +} + +static inline isc_result_t +query_addsoa(ns_client_t *client, dns_db_t *db, isc_boolean_t zero_ttl) { + dns_name_t *name, *fname; + dns_dbnode_t *node; + isc_result_t result, eresult; + dns_fixedname_t foundname; + dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; + dns_rdataset_t **sigrdatasetp = NULL; + + CTRACE("query_addsoa"); + /* + * Initialization. + */ + eresult = ISC_R_SUCCESS; + name = NULL; + rdataset = NULL; + node = NULL; + dns_fixedname_init(&foundname); + fname = dns_fixedname_name(&foundname); + + /* + * Get resources and make 'name' be the database origin. + */ + result = dns_message_gettempname(client->message, &name); + if (result != ISC_R_SUCCESS) + return (result); + dns_name_init(name, NULL); + dns_name_clone(dns_db_origin(db), name); + rdataset = query_newrdataset(client); + if (rdataset == NULL) { + eresult = DNS_R_SERVFAIL; + goto cleanup; + } + if (WANTDNSSEC(client)) { + sigrdataset = query_newrdataset(client); + if (sigrdataset == NULL) { + eresult = DNS_R_SERVFAIL; + goto cleanup; + } + } + + /* + * Find the SOA. + */ + result = dns_db_find(db, name, NULL, dns_rdatatype_soa, + client->query.dboptions, 0, &node, + fname, rdataset, sigrdataset); + if (result != ISC_R_SUCCESS) { + /* + * This is bad. We tried to get the SOA RR at the zone top + * and it didn't work! + */ + eresult = DNS_R_SERVFAIL; + } else { + /* + * Extract the SOA MINIMUM. + */ + dns_rdata_soa_t soa; + dns_rdata_t rdata = DNS_RDATA_INIT; + result = dns_rdataset_first(rdataset); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &soa, NULL); + if (result != ISC_R_SUCCESS) + goto cleanup; + + if (zero_ttl) { + rdataset->ttl = 0; + if (sigrdataset != NULL) + sigrdataset->ttl = 0; + } + + /* + * Add the SOA and its SIG to the response, with the + * TTLs adjusted per RFC2308 section 3. + */ + if (rdataset->ttl > soa.minimum) + rdataset->ttl = soa.minimum; + if (sigrdataset != NULL && sigrdataset->ttl > soa.minimum) + sigrdataset->ttl = soa.minimum; + + if (sigrdataset != NULL) + sigrdatasetp = &sigrdataset; + else + sigrdatasetp = NULL; + query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL, + DNS_SECTION_AUTHORITY); + } + + cleanup: + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + if (name != NULL) + query_releasename(client, &name); + if (node != NULL) + dns_db_detachnode(db, &node); + + return (eresult); +} + +static inline isc_result_t +query_addns(ns_client_t *client, dns_db_t *db) { + dns_name_t *name, *fname; + dns_dbnode_t *node; + isc_result_t result, eresult; + dns_fixedname_t foundname; + dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; + dns_rdataset_t **sigrdatasetp = NULL; + + CTRACE("query_addns"); + /* + * Initialization. + */ + eresult = ISC_R_SUCCESS; + name = NULL; + rdataset = NULL; + node = NULL; + dns_fixedname_init(&foundname); + fname = dns_fixedname_name(&foundname); + + /* + * Get resources and make 'name' be the database origin. + */ + result = dns_message_gettempname(client->message, &name); + if (result != ISC_R_SUCCESS) { + CTRACE("query_addns: dns_message_gettempname failed: done"); + return (result); + } + dns_name_init(name, NULL); + dns_name_clone(dns_db_origin(db), name); + rdataset = query_newrdataset(client); + if (rdataset == NULL) { + CTRACE("query_addns: query_newrdataset failed"); + eresult = DNS_R_SERVFAIL; + goto cleanup; + } + if (WANTDNSSEC(client)) { + sigrdataset = query_newrdataset(client); + if (sigrdataset == NULL) { + CTRACE("query_addns: query_newrdataset failed"); + eresult = DNS_R_SERVFAIL; + goto cleanup; + } + } + + /* + * Find the NS rdataset. + */ + CTRACE("query_addns: calling dns_db_find"); + result = dns_db_find(db, name, NULL, dns_rdatatype_ns, + client->query.dboptions, 0, &node, + fname, rdataset, sigrdataset); + CTRACE("query_addns: dns_db_find complete"); + if (result != ISC_R_SUCCESS) { + CTRACE("query_addns: dns_db_find failed"); + /* + * This is bad. We tried to get the NS rdataset at the zone + * top and it didn't work! + */ + eresult = DNS_R_SERVFAIL; + } else { + if (sigrdataset != NULL) + sigrdatasetp = &sigrdataset; + else + sigrdatasetp = NULL; + query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL, + DNS_SECTION_AUTHORITY); + } + + cleanup: + CTRACE("query_addns: cleanup"); + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + if (name != NULL) + query_releasename(client, &name); + if (node != NULL) + dns_db_detachnode(db, &node); + + CTRACE("query_addns: done"); + return (eresult); +} + +static inline isc_result_t +query_addcnamelike(ns_client_t *client, dns_name_t *qname, dns_name_t *tname, + dns_trust_t trust, dns_name_t **anamep, dns_rdatatype_t type) +{ + dns_rdataset_t *rdataset; + dns_rdatalist_t *rdatalist; + dns_rdata_t *rdata; + isc_result_t result; + isc_region_t r; + + /* + * We assume the name data referred to by tname won't go away. + */ + + REQUIRE(anamep != NULL); + + rdatalist = NULL; + result = dns_message_gettemprdatalist(client->message, &rdatalist); + if (result != ISC_R_SUCCESS) + return (result); + rdata = NULL; + result = dns_message_gettemprdata(client->message, &rdata); + if (result != ISC_R_SUCCESS) + return (result); + rdataset = NULL; + result = dns_message_gettemprdataset(client->message, &rdataset); + if (result != ISC_R_SUCCESS) + return (result); + dns_rdataset_init(rdataset); + result = dns_name_dup(qname, client->mctx, *anamep); + if (result != ISC_R_SUCCESS) { + dns_message_puttemprdataset(client->message, &rdataset); + return (result); + } + + rdatalist->type = type; + rdatalist->covers = 0; + rdatalist->rdclass = client->message->rdclass; + rdatalist->ttl = 0; + + dns_name_toregion(tname, &r); + rdata->data = r.base; + rdata->length = r.length; + rdata->rdclass = client->message->rdclass; + rdata->type = type; + + ISC_LIST_INIT(rdatalist->rdata); + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) + == ISC_R_SUCCESS); + rdataset->trust = trust; + + query_addrrset(client, anamep, &rdataset, NULL, NULL, + DNS_SECTION_ANSWER); + + if (rdataset != NULL) { + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + dns_message_puttemprdataset(client->message, &rdataset); + } + + return (ISC_R_SUCCESS); +} + +static void +query_addbestns(ns_client_t *client) { + dns_db_t *db, *zdb; + dns_dbnode_t *node; + dns_name_t *fname, *zfname; + dns_rdataset_t *rdataset, *sigrdataset, *zrdataset, *zsigrdataset; + isc_boolean_t is_zone, use_zone; + isc_buffer_t *dbuf; + isc_result_t result; + dns_dbversion_t *version; + dns_zone_t *zone; + isc_buffer_t b; + + CTRACE("query_addbestns"); + fname = NULL; + zfname = NULL; + rdataset = NULL; + zrdataset = NULL; + sigrdataset = NULL; + zsigrdataset = NULL; + node = NULL; + db = NULL; + zdb = NULL; + version = NULL; + zone = NULL; + is_zone = ISC_FALSE; + use_zone = ISC_FALSE; + + /* + * Find the right database. + */ + result = query_getdb(client, client->query.qname, dns_rdatatype_ns, 0, + &zone, &db, &version, &is_zone); + if (result != ISC_R_SUCCESS) + goto cleanup; + + db_find: + /* + * We'll need some resources... + */ + dbuf = query_getnamebuf(client); + if (dbuf == NULL) + goto cleanup; + fname = query_newname(client, dbuf, &b); + rdataset = query_newrdataset(client); + if (fname == NULL || rdataset == NULL) + goto cleanup; + if (WANTDNSSEC(client)) { + sigrdataset = query_newrdataset(client); + if (sigrdataset == NULL) + goto cleanup; + } + + /* + * Now look for the zonecut. + */ + if (is_zone) { + result = dns_db_find(db, client->query.qname, version, + dns_rdatatype_ns, client->query.dboptions, + client->now, &node, fname, + rdataset, sigrdataset); + if (result != DNS_R_DELEGATION) + goto cleanup; + if (USECACHE(client)) { + query_keepname(client, fname, dbuf); + zdb = db; + zfname = fname; + fname = NULL; + zrdataset = rdataset; + rdataset = NULL; + zsigrdataset = sigrdataset; + sigrdataset = NULL; + dns_db_detachnode(db, &node); + version = NULL; + db = NULL; + dns_db_attach(client->view->cachedb, &db); + is_zone = ISC_FALSE; + goto db_find; + } + } else { + result = dns_db_findzonecut(db, client->query.qname, + client->query.dboptions, + client->now, &node, fname, + rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + if (zfname != NULL && + !dns_name_issubdomain(fname, zfname)) { + /* + * We found a zonecut in the cache, but our + * zone delegation is better. + */ + use_zone = ISC_TRUE; + } + } else if (result == ISC_R_NOTFOUND && zfname != NULL) { + /* + * We didn't find anything in the cache, but we + * have a zone delegation, so use it. + */ + use_zone = ISC_TRUE; + } else + goto cleanup; + } + + if (use_zone) { + query_releasename(client, &fname); + fname = zfname; + zfname = NULL; + /* + * We've already done query_keepname() on + * zfname, so we must set dbuf to NULL to + * prevent query_addrrset() from trying to + * call query_keepname() again. + */ + dbuf = NULL; + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + rdataset = zrdataset; + zrdataset = NULL; + sigrdataset = zsigrdataset; + zsigrdataset = NULL; + } + + if ((client->query.dboptions & DNS_DBFIND_PENDINGOK) == 0 && + (rdataset->trust == dns_trust_pending || + (sigrdataset != NULL && sigrdataset->trust == dns_trust_pending))) + goto cleanup; + + if (WANTDNSSEC(client) && SECURE(client) && + (rdataset->trust == dns_trust_glue || + (sigrdataset != NULL && sigrdataset->trust == dns_trust_glue))) + goto cleanup; + + query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, + DNS_SECTION_AUTHORITY); + + cleanup: + if (rdataset != NULL) + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + if (fname != NULL) + query_releasename(client, &fname); + if (node != NULL) + dns_db_detachnode(db, &node); + if (db != NULL) + dns_db_detach(&db); + if (zone != NULL) + dns_zone_detach(&zone); + if (zdb != NULL) { + query_putrdataset(client, &zrdataset); + if (zsigrdataset != NULL) + query_putrdataset(client, &zsigrdataset); + if (zfname != NULL) + query_releasename(client, &zfname); + dns_db_detach(&zdb); + } +} + +static void +query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node) { + dns_name_t *rname; + dns_rdataset_t *rdataset, *sigrdataset; + isc_result_t result; + + CTRACE("query_addds"); + rname = NULL; + rdataset = NULL; + sigrdataset = NULL; + + /* + * We'll need some resources... + */ + rdataset = query_newrdataset(client); + sigrdataset = query_newrdataset(client); + if (rdataset == NULL || sigrdataset == NULL) + goto cleanup; + + /* + * Look for the DS record, which may or may not be present. + */ + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ds, 0, + client->now, rdataset, sigrdataset); + /* + * If we didn't find it, look for an NSEC. */ + if (result == ISC_R_NOTFOUND) + result = dns_db_findrdataset(db, node, NULL, + dns_rdatatype_nsec, 0, client->now, + rdataset, sigrdataset); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto cleanup; + if (!dns_rdataset_isassociated(rdataset) || + !dns_rdataset_isassociated(sigrdataset)) + goto cleanup; + + /* + * We've already added the NS record, so if the name's not there, + * we have other problems. Use this name rather than calling + * query_addrrset(). + */ + result = dns_message_firstname(client->message, DNS_SECTION_AUTHORITY); + if (result != ISC_R_SUCCESS) + goto cleanup; + + rname = NULL; + dns_message_currentname(client->message, DNS_SECTION_AUTHORITY, + &rname); + result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL); + if (result != ISC_R_SUCCESS) + goto cleanup; + + ISC_LIST_APPEND(rname->list, rdataset, link); + ISC_LIST_APPEND(rname->list, sigrdataset, link); + rdataset = NULL; + sigrdataset = NULL; + + cleanup: + if (rdataset != NULL) + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); +} + +static void +query_addwildcardproof(ns_client_t *client, dns_db_t *db, + dns_name_t *name, isc_boolean_t ispositive) +{ + isc_buffer_t *dbuf, b; + dns_name_t *fname; + dns_rdataset_t *rdataset, *sigrdataset; + dns_fixedname_t wfixed; + dns_name_t *wname; + dns_dbnode_t *node; + unsigned int options; + unsigned int olabels, nlabels; + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_nsec_t nsec; + isc_boolean_t have_wname; + int order; + + CTRACE("query_addwildcardproof"); + fname = NULL; + rdataset = NULL; + sigrdataset = NULL; + node = NULL; + + /* + * Get the NOQNAME proof then if !ispositve + * get the NOWILDCARD proof. + * + * DNS_DBFIND_NOWILD finds the NSEC records that covers the + * name ignoring any wildcard. From the owner and next names + * of this record you can compute which wildcard (if it exists) + * will match by finding the longest common suffix of the + * owner name and next names with the qname and prefixing that + * with the wildcard label. + * + * e.g. + * Given: + * example SOA + * example NSEC b.example + * b.example A + * b.example NSEC a.d.example + * a.d.example A + * a.d.example NSEC g.f.example + * g.f.example A + * g.f.example NSEC z.i.example + * z.i.example A + * z.i.example NSEC example + * + * QNAME: + * a.example -> example NSEC b.example + * owner common example + * next common example + * wild *.example + * d.b.example -> b.example NSEC a.d.example + * owner common b.example + * next common example + * wild *.b.example + * a.f.example -> a.d.example NSEC g.f.example + * owner common example + * next common f.example + * wild *.f.example + * j.example -> z.i.example NSEC example + * owner common example + * next common example + * wild *.f.example + */ + options = client->query.dboptions | DNS_DBFIND_NOWILD; + dns_fixedname_init(&wfixed); + wname = dns_fixedname_name(&wfixed); + again: + have_wname = ISC_FALSE; + /* + * We'll need some resources... + */ + dbuf = query_getnamebuf(client); + if (dbuf == NULL) + goto cleanup; + fname = query_newname(client, dbuf, &b); + rdataset = query_newrdataset(client); + sigrdataset = query_newrdataset(client); + if (fname == NULL || rdataset == NULL || sigrdataset == NULL) + goto cleanup; + + result = dns_db_find(db, name, NULL, dns_rdatatype_nsec, options, + 0, &node, fname, rdataset, sigrdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + if (result == DNS_R_NXDOMAIN) { + if (!ispositive) + result = dns_rdataset_first(rdataset); + if (result == ISC_R_SUCCESS) { + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec, NULL); + } + if (result == ISC_R_SUCCESS) { + (void)dns_name_fullcompare(name, fname, &order, + &olabels); + (void)dns_name_fullcompare(name, &nsec.next, &order, + &nlabels); + if (olabels > nlabels) + dns_name_split(name, olabels, NULL, wname); + else + dns_name_split(name, nlabels, NULL, wname); + result = dns_name_concatenate(dns_wildcardname, + wname, wname, NULL); + if (result == ISC_R_SUCCESS) + have_wname = ISC_TRUE; + dns_rdata_freestruct(&nsec); + } + query_addrrset(client, &fname, &rdataset, &sigrdataset, + dbuf, DNS_SECTION_AUTHORITY); + } + if (rdataset != NULL) + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + if (fname != NULL) + query_releasename(client, &fname); + if (have_wname) { + ispositive = ISC_TRUE; /* prevent loop */ + if (!dns_name_equal(name, wname)) { + name = wname; + goto again; + } + } + cleanup: + if (rdataset != NULL) + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + if (fname != NULL) + query_releasename(client, &fname); +} + +static void +query_addnxrrsetnsec(ns_client_t *client, dns_db_t *db, dns_name_t **namep, + dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp) +{ + dns_name_t *name; + dns_rdataset_t *sigrdataset; + dns_rdata_t sigrdata; + dns_rdata_rrsig_t sig; + unsigned int labels; + isc_buffer_t *dbuf, b; + dns_name_t *fname; + isc_result_t result; + + name = *namep; + if ((name->attributes & DNS_NAMEATTR_WILDCARD) == 0) { + query_addrrset(client, namep, rdatasetp, sigrdatasetp, + NULL, DNS_SECTION_AUTHORITY); + return; + } + + if (sigrdatasetp == NULL) + return; + sigrdataset = *sigrdatasetp; + if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset)) + return; + result = dns_rdataset_first(sigrdataset); + if (result != ISC_R_SUCCESS) + return; + dns_rdata_init(&sigrdata); + dns_rdataset_current(sigrdataset, &sigrdata); + result = dns_rdata_tostruct(&sigrdata, &sig, NULL); + if (result != ISC_R_SUCCESS) + return; + + labels = dns_name_countlabels(name); + if ((unsigned int)sig.labels + 1 >= labels) + return; + + /* XXX */ + query_addwildcardproof(client, db, + client->query.qname, + ISC_TRUE); + + /* + * We'll need some resources... + */ + dbuf = query_getnamebuf(client); + if (dbuf == NULL) + return; + fname = query_newname(client, dbuf, &b); + if (fname == NULL) + return; + dns_name_split(name, sig.labels + 1, NULL, fname); + /* This will succeed, since we've stripped labels. */ + RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname, fname, fname, + NULL) == ISC_R_SUCCESS); + query_addrrset(client, &fname, rdatasetp, sigrdatasetp, + dbuf, DNS_SECTION_AUTHORITY); +} + +static void +query_resume(isc_task_t *task, isc_event_t *event) { + dns_fetchevent_t *devent = (dns_fetchevent_t *)event; + ns_client_t *client; + isc_boolean_t fetch_cancelled, client_shuttingdown; + + /* + * Resume a query after recursion. + */ + + UNUSED(task); + + REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); + client = devent->ev_arg; + REQUIRE(NS_CLIENT_VALID(client)); + REQUIRE(task == client->task); + REQUIRE(RECURSING(client)); + + LOCK(&client->query.fetchlock); + if (client->query.fetch != NULL) { + /* + * This is the fetch we've been waiting for. + */ + INSIST(devent->fetch == client->query.fetch); + client->query.fetch = NULL; + fetch_cancelled = ISC_FALSE; + /* + * Update client->now. + */ + isc_stdtime_get(&client->now); + } else { + /* + * This is a fetch completion event for a cancelled fetch. + * Clean up and don't resume the find. + */ + fetch_cancelled = ISC_TRUE; + } + UNLOCK(&client->query.fetchlock); + INSIST(client->query.fetch == NULL); + + client->query.attributes &= ~NS_QUERYATTR_RECURSING; + dns_resolver_destroyfetch(&devent->fetch); + + /* + * If this client is shutting down, or this transaction + * has timed out, do not resume the find. + */ + client_shuttingdown = ns_client_shuttingdown(client); + if (fetch_cancelled || client_shuttingdown) { + if (devent->node != NULL) + dns_db_detachnode(devent->db, &devent->node); + if (devent->db != NULL) + dns_db_detach(&devent->db); + query_putrdataset(client, &devent->rdataset); + if (devent->sigrdataset != NULL) + query_putrdataset(client, &devent->sigrdataset); + isc_event_free(&event); + if (fetch_cancelled) + query_error(client, DNS_R_SERVFAIL); + else + query_next(client, ISC_R_CANCELED); + /* + * This may destroy the client. + */ + ns_client_detach(&client); + } else { + query_find(client, devent, 0); + } +} + +static isc_result_t +query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain, + dns_rdataset_t *nameservers) +{ + isc_result_t result; + dns_rdataset_t *rdataset, *sigrdataset; + + inc_stats(client, dns_statscounter_recursion); + + /* + * We are about to recurse, which means that this client will + * be unavailable for serving new requests for an indeterminate + * amount of time. If this client is currently responsible + * for handling incoming queries, set up a new client + * object to handle them while we are waiting for a + * response. There is no need to replace TCP clients + * because those have already been replaced when the + * connection was accepted (if allowed by the TCP quota). + */ + if (client->recursionquota == NULL) { + result = isc_quota_attach(&ns_g_server->recursionquota, + &client->recursionquota); + if (result == ISC_R_SOFTQUOTA) { + ns_client_log(client, NS_LOGCATEGORY_CLIENT, + NS_LOGMODULE_QUERY, ISC_LOG_WARNING, + "recursive-clients soft limit exceeded, " + "aborting oldest query"); + ns_client_killoldestquery(client); + result = ISC_R_SUCCESS; + } else if (result == ISC_R_QUOTA) { + ns_client_log(client, NS_LOGCATEGORY_CLIENT, + NS_LOGMODULE_QUERY, ISC_LOG_WARNING, + "no more recursive clients: %s", + isc_result_totext(result)); + ns_client_killoldestquery(client); + } + if (result == ISC_R_SUCCESS && !client->mortal && + (client->attributes & NS_CLIENTATTR_TCP) == 0) { + result = ns_client_replace(client); + if (result != ISC_R_SUCCESS) { + ns_client_log(client, NS_LOGCATEGORY_CLIENT, + NS_LOGMODULE_QUERY, + ISC_LOG_WARNING, + "ns_client_replace() failed: %s", + isc_result_totext(result)); + isc_quota_detach(&client->recursionquota); + } + } + if (result != ISC_R_SUCCESS) + return (result); + ns_client_recursing(client); + } + + /* + * Invoke the resolver. + */ + REQUIRE(nameservers == NULL || nameservers->type == dns_rdatatype_ns); + REQUIRE(client->query.fetch == NULL); + + rdataset = query_newrdataset(client); + if (rdataset == NULL) + return (ISC_R_NOMEMORY); + if (WANTDNSSEC(client)) { + sigrdataset = query_newrdataset(client); + if (sigrdataset == NULL) { + query_putrdataset(client, &rdataset); + return (ISC_R_NOMEMORY); + } + } else + sigrdataset = NULL; + + if (client->query.timerset == ISC_FALSE) + ns_client_settimeout(client, 60); + result = dns_resolver_createfetch(client->view->resolver, + client->query.qname, + qtype, qdomain, nameservers, + NULL, client->query.fetchoptions, + client->task, + query_resume, client, + rdataset, sigrdataset, + &client->query.fetch); + + if (result == ISC_R_SUCCESS) { + /* + * Record that we're waiting for an event. A client which + * is shutting down will not be destroyed until all the + * events have been received. + */ + } else { + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + } + + return (result); +} + +#define MAX_RESTARTS 16 + +#define QUERY_ERROR(r) \ +do { \ + eresult = r; \ + want_restart = ISC_FALSE; \ +} while (0) + +/* + * Extract a network address from the RDATA of an A or AAAA + * record. + * + * Returns: + * ISC_R_SUCCESS + * ISC_R_NOTIMPLEMENTED The rdata is not a known address type. + */ +static isc_result_t +rdata_tonetaddr(dns_rdata_t *rdata, isc_netaddr_t *netaddr) { + struct in_addr ina; + struct in6_addr in6a; + + switch (rdata->type) { + case dns_rdatatype_a: + INSIST(rdata->length == 4); + memcpy(&ina.s_addr, rdata->data, 4); + isc_netaddr_fromin(netaddr, &ina); + return (ISC_R_SUCCESS); + case dns_rdatatype_aaaa: + INSIST(rdata->length == 16); + memcpy(in6a.s6_addr, rdata->data, 16); + isc_netaddr_fromin6(netaddr, &in6a); + return (ISC_R_SUCCESS); + default: + return (ISC_R_NOTIMPLEMENTED); + } +} + +/* + * Find the sort order of 'rdata' in the topology-like + * ACL forming the second element in a 2-element top-level + * sortlist statement. + */ +static int +query_sortlist_order_2element(dns_rdata_t *rdata, void *arg) { + isc_netaddr_t netaddr; + + if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS) + return (INT_MAX); + return (ns_sortlist_addrorder2(&netaddr, arg)); +} + +/* + * Find the sort order of 'rdata' in the matching element + * of a 1-element top-level sortlist statement. + */ +static int +query_sortlist_order_1element(dns_rdata_t *rdata, void *arg) { + isc_netaddr_t netaddr; + + if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS) + return (INT_MAX); + return (ns_sortlist_addrorder1(&netaddr, arg)); +} + +/* + * Find the sortlist statement that applies to 'client' and set up + * the sortlist info in in client->message appropriately. + */ +static void +setup_query_sortlist(ns_client_t *client) { + isc_netaddr_t netaddr; + dns_rdatasetorderfunc_t order = NULL; + void *order_arg = NULL; + + isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); + switch (ns_sortlist_setup(client->view->sortlist, + &netaddr, &order_arg)) { + case NS_SORTLISTTYPE_1ELEMENT: + order = query_sortlist_order_1element; + break; + case NS_SORTLISTTYPE_2ELEMENT: + order = query_sortlist_order_2element; + break; + case NS_SORTLISTTYPE_NONE: + order = NULL; + break; + default: + INSIST(0); + break; + } + dns_message_setsortorder(client->message, order, order_arg); +} + +static void +query_addnoqnameproof(ns_client_t *client, dns_rdataset_t *rdataset) { + isc_buffer_t *dbuf, b; + dns_name_t *fname; + dns_rdataset_t *nsec, *nsecsig; + isc_result_t result = ISC_R_NOMEMORY; + + CTRACE("query_addnoqnameproof"); + + fname = NULL; + nsec = NULL; + nsecsig = NULL; + + dbuf = query_getnamebuf(client); + if (dbuf == NULL) + goto cleanup; + fname = query_newname(client, dbuf, &b); + nsec = query_newrdataset(client); + nsecsig = query_newrdataset(client); + if (fname == NULL || nsec == NULL || nsecsig == NULL) + goto cleanup; + + result = dns_rdataset_getnoqname(rdataset, fname, nsec, nsecsig); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + query_addrrset(client, &fname, &nsec, &nsecsig, dbuf, + DNS_SECTION_AUTHORITY); + + cleanup: + if (nsec != NULL) + query_putrdataset(client, &nsec); + if (nsecsig != NULL) + query_putrdataset(client, &nsecsig); + if (fname != NULL) + query_releasename(client, &fname); +} + +static inline void +answer_in_glue(ns_client_t *client, dns_rdatatype_t qtype) { + dns_name_t *name; + dns_message_t *msg; + dns_section_t section = DNS_SECTION_ADDITIONAL; + dns_rdataset_t *rdataset = NULL; + + msg = client->message; + for (name = ISC_LIST_HEAD(msg->sections[section]); + name != NULL; + name = ISC_LIST_NEXT(name, link)) + if (dns_name_equal(name, client->query.qname)) { + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + if (rdataset->type == qtype) + break; + break; + } + if (rdataset != NULL) { + ISC_LIST_UNLINK(msg->sections[section], name, link); + ISC_LIST_PREPEND(msg->sections[section], name, link); + ISC_LIST_UNLINK(name->list, rdataset, link); + ISC_LIST_PREPEND(name->list, rdataset, link); + rdataset->attributes |= DNS_RDATASETATTR_REQUIREDGLUE; + } +} + +/* + * Do the bulk of query processing for the current query of 'client'. + * If 'event' is non-NULL, we are returning from recursion and 'qtype' + * is ignored. Otherwise, 'qtype' is the query type. + */ +static void +query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) +{ + dns_db_t *db, *zdb; + dns_dbnode_t *node; + dns_rdatatype_t type; + dns_name_t *fname, *zfname, *tname, *prefix; + dns_rdataset_t *rdataset, *trdataset; + dns_rdataset_t *sigrdataset, *zrdataset, *zsigrdataset; + dns_rdataset_t **sigrdatasetp; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatasetiter_t *rdsiter; + isc_boolean_t want_restart, authoritative, is_zone, need_wildcardproof; + unsigned int n, nlabels; + dns_namereln_t namereln; + int order; + isc_buffer_t *dbuf; + isc_buffer_t b; + isc_result_t result, eresult; + dns_fixedname_t fixed; + dns_fixedname_t wildcardname; + dns_dbversion_t *version; + dns_zone_t *zone; + dns_rdata_cname_t cname; + dns_rdata_dname_t dname; + unsigned int options; + isc_boolean_t empty_wild; + dns_rdataset_t *noqname; + + CTRACE("query_find"); + + /* + * One-time initialization. + * + * It's especially important to initialize anything that the cleanup + * code might cleanup. + */ + + eresult = ISC_R_SUCCESS; + fname = NULL; + zfname = NULL; + rdataset = NULL; + zrdataset = NULL; + sigrdataset = NULL; + zsigrdataset = NULL; + node = NULL; + db = NULL; + zdb = NULL; + version = NULL; + zone = NULL; + need_wildcardproof = ISC_FALSE; + empty_wild = ISC_FALSE; + options = 0; + + if (event != NULL) { + /* + * We're returning from recursion. Restore the query context + * and resume. + */ + + want_restart = ISC_FALSE; + authoritative = ISC_FALSE; + is_zone = ISC_FALSE; + + qtype = event->qtype; + if (qtype == dns_rdatatype_rrsig) + type = dns_rdatatype_any; + else + type = qtype; + db = event->db; + node = event->node; + rdataset = event->rdataset; + sigrdataset = event->sigrdataset; + + /* + * We'll need some resources... + */ + dbuf = query_getnamebuf(client); + if (dbuf == NULL) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + fname = query_newname(client, dbuf, &b); + if (fname == NULL) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + tname = dns_fixedname_name(&event->foundname); + result = dns_name_copy(tname, fname, NULL); + if (result != ISC_R_SUCCESS) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + + result = event->result; + + goto resume; + } + + /* + * Not returning from recursion. + */ + + /* + * If it's a SIG query, we'll iterate the node. + */ + if (qtype == dns_rdatatype_rrsig) + type = dns_rdatatype_any; + else + type = qtype; + + restart: + CTRACE("query_find: restart"); + want_restart = ISC_FALSE; + authoritative = ISC_FALSE; + version = NULL; + need_wildcardproof = ISC_FALSE; + + if (client->view->checknames && + !dns_rdata_checkowner(client->query.qname, + client->message->rdclass, + qtype, ISC_FALSE)) { + char namebuf[DNS_NAME_FORMATSIZE]; + char typename[DNS_RDATATYPE_FORMATSIZE]; + char classname[DNS_RDATACLASS_FORMATSIZE]; + + dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); + dns_rdatatype_format(qtype, typename, sizeof(typename)); + dns_rdataclass_format(client->message->rdclass, classname, + sizeof(classname)); + ns_client_log(client, DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_QUERY, ISC_LOG_ERROR, + "check-names failure %s/%s/%s", namebuf, + typename, classname); + QUERY_ERROR(DNS_R_REFUSED); + goto cleanup; + } + + /* + * First we must find the right database. + */ + options = 0; + if (dns_rdatatype_atparent(qtype) && + !dns_name_equal(client->query.qname, dns_rootname)) + options |= DNS_GETDB_NOEXACT; + result = query_getdb(client, client->query.qname, qtype, options, + &zone, &db, &version, &is_zone); + if ((result != ISC_R_SUCCESS || !is_zone) && !RECURSIONOK(client) && + (options & DNS_GETDB_NOEXACT) != 0 && qtype == dns_rdatatype_ds) { + /* + * Look to see if we are authoritative for the + * child zone if the query type is DS. + */ + dns_db_t *tdb = NULL; + dns_zone_t *tzone = NULL; + dns_dbversion_t *tversion = NULL; + isc_result_t tresult; + + tresult = query_getzonedb(client, client->query.qname, qtype, + DNS_GETDB_PARTIAL, &tzone, &tdb, + &tversion); + if (tresult == ISC_R_SUCCESS) { + options &= ~DNS_GETDB_NOEXACT; + query_putrdataset(client, &rdataset); + if (db != NULL) + dns_db_detach(&db); + if (zone != NULL) + dns_zone_detach(&zone); + version = tversion; + db = tdb; + zone = tzone; + is_zone = ISC_TRUE; + result = ISC_R_SUCCESS; + } else { + if (tdb != NULL) + dns_db_detach(&tdb); + if (tzone != NULL) + dns_zone_detach(&tzone); + } + } + if (result != ISC_R_SUCCESS) { + if (result == DNS_R_REFUSED) + QUERY_ERROR(DNS_R_REFUSED); + else + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + + if (is_zone) + authoritative = ISC_TRUE; + + if (event == NULL && client->query.restarts == 0) { + if (is_zone) { + dns_zone_attach(zone, &client->query.authzone); + dns_db_attach(db, &client->query.authdb); + } + client->query.authdbset = ISC_TRUE; + } + + db_find: + CTRACE("query_find: db_find"); + /* + * We'll need some resources... + */ + dbuf = query_getnamebuf(client); + if (dbuf == NULL) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + fname = query_newname(client, dbuf, &b); + rdataset = query_newrdataset(client); + if (fname == NULL || rdataset == NULL) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + if (WANTDNSSEC(client)) { + sigrdataset = query_newrdataset(client); + if (sigrdataset == NULL) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + } + + /* + * Now look for an answer in the database. + */ + result = dns_db_find(db, client->query.qname, version, type, + client->query.dboptions, client->now, + &node, fname, rdataset, sigrdataset); + + resume: + CTRACE("query_find: resume"); + switch (result) { + case ISC_R_SUCCESS: + /* + * This case is handled in the main line below. + */ + break; + case DNS_R_GLUE: + case DNS_R_ZONECUT: + /* + * These cases are handled in the main line below. + */ + INSIST(is_zone); + authoritative = ISC_FALSE; + break; + case ISC_R_NOTFOUND: + /* + * The cache doesn't even have the root NS. Get them from + * the hints DB. + */ + INSIST(!is_zone); + if (db != NULL) + dns_db_detach(&db); + + if (client->view->hints == NULL) { + /* We have no hints. */ + result = ISC_R_FAILURE; + } else { + dns_db_attach(client->view->hints, &db); + result = dns_db_find(db, dns_rootname, + NULL, dns_rdatatype_ns, + 0, client->now, &node, fname, + rdataset, sigrdataset); + } + if (result != ISC_R_SUCCESS) { + /* + * Nonsensical root hints may require cleanup. + */ + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + if (sigrdataset != NULL && + dns_rdataset_isassociated(sigrdataset)) + dns_rdataset_disassociate(sigrdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + + /* + * We don't have any root server hints, but + * we may have working forwarders, so try to + * recurse anyway. + */ + if (RECURSIONOK(client)) { + result = query_recurse(client, qtype, + NULL, NULL); + if (result == ISC_R_SUCCESS) + client->query.attributes |= + NS_QUERYATTR_RECURSING; + else { + /* Unable to recurse. */ + QUERY_ERROR(DNS_R_SERVFAIL); + } + goto cleanup; + } else { + /* Unable to give root server referral. */ + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + } + /* + * XXXRTH We should trigger root server priming here. + */ + /* FALLTHROUGH */ + case DNS_R_DELEGATION: + authoritative = ISC_FALSE; + if (is_zone) { + /* + * Look to see if we are authoritative for the + * child zone if the query type is DS. + */ + if (!RECURSIONOK(client) && + (options & DNS_GETDB_NOEXACT) != 0 && + qtype == dns_rdatatype_ds) { + dns_db_t *tdb = NULL; + dns_zone_t *tzone = NULL; + dns_dbversion_t *tversion = NULL; + result = query_getzonedb(client, + client->query.qname, + qtype, + DNS_GETDB_PARTIAL, + &tzone, &tdb, + &tversion); + if (result == ISC_R_SUCCESS) { + options &= ~DNS_GETDB_NOEXACT; + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, + &sigrdataset); + if (fname != NULL) + query_releasename(client, + &fname); + if (node != NULL) + dns_db_detachnode(db, &node); + if (db != NULL) + dns_db_detach(&db); + if (zone != NULL) + dns_zone_detach(&zone); + version = tversion; + db = tdb; + zone = tzone; + authoritative = ISC_TRUE; + goto db_find; + } + if (tdb != NULL) + dns_db_detach(&tdb); + if (tzone != NULL) + dns_zone_detach(&tzone); + } + /* + * We're authoritative for an ancestor of QNAME. + */ + if (!USECACHE(client) || !RECURSIONOK(client)) { + /* + * If we don't have a cache, this is the best + * answer. + * + * If the client is making a nonrecursive + * query we always give out the authoritative + * delegation. This way even if we get + * junk in our cache, we won't fail in our + * role as the delegating authority if another + * nameserver asks us about a delegated + * subzone. + * + * We enable the retrieval of glue for this + * database by setting client->query.gluedb. + */ + client->query.gluedb = db; + client->query.isreferral = ISC_TRUE; + /* + * We must ensure NOADDITIONAL is off, + * because the generation of + * additional data is required in + * delegations. + */ + client->query.attributes &= + ~NS_QUERYATTR_NOADDITIONAL; + if (sigrdataset != NULL) + sigrdatasetp = &sigrdataset; + else + sigrdatasetp = NULL; + query_addrrset(client, &fname, + &rdataset, sigrdatasetp, + dbuf, DNS_SECTION_AUTHORITY); + client->query.gluedb = NULL; + if (WANTDNSSEC(client) && dns_db_issecure(db)) + query_addds(client, db, node); + } else { + /* + * We might have a better answer or delegation + * in the cache. We'll remember the current + * values of fname, rdataset, and sigrdataset. + * We'll then go looking for QNAME in the + * cache. If we find something better, we'll + * use it instead. + */ + query_keepname(client, fname, dbuf); + zdb = db; + zfname = fname; + fname = NULL; + zrdataset = rdataset; + rdataset = NULL; + zsigrdataset = sigrdataset; + sigrdataset = NULL; + dns_db_detachnode(db, &node); + version = NULL; + db = NULL; + dns_db_attach(client->view->cachedb, &db); + is_zone = ISC_FALSE; + goto db_find; + } + } else { + if (zfname != NULL && + !dns_name_issubdomain(fname, zfname)) { + /* + * We've already got a delegation from + * authoritative data, and it is better + * than what we found in the cache. Use + * it instead of the cache delegation. + */ + query_releasename(client, &fname); + fname = zfname; + zfname = NULL; + /* + * We've already done query_keepname() on + * zfname, so we must set dbuf to NULL to + * prevent query_addrrset() from trying to + * call query_keepname() again. + */ + dbuf = NULL; + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, + &sigrdataset); + rdataset = zrdataset; + zrdataset = NULL; + sigrdataset = zsigrdataset; + zsigrdataset = NULL; + /* + * We don't clean up zdb here because we + * may still need it. It will get cleaned + * up by the main cleanup code. + */ + } + + if (RECURSIONOK(client)) { + /* + * Recurse! + */ + if (dns_rdatatype_atparent(type)) + result = query_recurse(client, qtype, + NULL, NULL); + else + result = query_recurse(client, qtype, + fname, rdataset); + if (result == ISC_R_SUCCESS) + client->query.attributes |= + NS_QUERYATTR_RECURSING; + else + QUERY_ERROR(DNS_R_SERVFAIL); + } else { + /* + * This is the best answer. + */ + client->query.attributes |= + NS_QUERYATTR_CACHEGLUEOK; + client->query.gluedb = zdb; + client->query.isreferral = ISC_TRUE; + /* + * We must ensure NOADDITIONAL is off, + * because the generation of + * additional data is required in + * delegations. + */ + client->query.attributes &= + ~NS_QUERYATTR_NOADDITIONAL; + if (sigrdataset != NULL) + sigrdatasetp = &sigrdataset; + else + sigrdatasetp = NULL; + query_addrrset(client, &fname, + &rdataset, sigrdatasetp, + dbuf, DNS_SECTION_AUTHORITY); + client->query.gluedb = NULL; + client->query.attributes &= + ~NS_QUERYATTR_CACHEGLUEOK; + if (WANTDNSSEC(client)) + query_addds(client, db, node); + } + } + goto cleanup; + case DNS_R_EMPTYNAME: + result = DNS_R_NXRRSET; + /* FALLTHROUGH */ + case DNS_R_NXRRSET: + INSIST(is_zone); + if (dns_rdataset_isassociated(rdataset)) { + /* + * If we've got a NSEC record, we need to save the + * name now because we're going call query_addsoa() + * below, and it needs to use the name buffer. + */ + query_keepname(client, fname, dbuf); + } else { + /* + * We're not going to use fname, and need to release + * our hold on the name buffer so query_addsoa() + * may use it. + */ + query_releasename(client, &fname); + } + /* + * Add SOA. + */ + result = query_addsoa(client, db, ISC_FALSE); + if (result != ISC_R_SUCCESS) { + QUERY_ERROR(result); + goto cleanup; + } + /* + * Add NSEC record if we found one. + */ + if (WANTDNSSEC(client)) { + if (dns_rdataset_isassociated(rdataset)) + query_addnxrrsetnsec(client, db, &fname, + &rdataset, &sigrdataset); + } + goto cleanup; + case DNS_R_EMPTYWILD: + empty_wild = ISC_TRUE; + /* FALLTHROUGH */ + case DNS_R_NXDOMAIN: + INSIST(is_zone); + if (dns_rdataset_isassociated(rdataset)) { + /* + * If we've got a NSEC record, we need to save the + * name now because we're going call query_addsoa() + * below, and it needs to use the name buffer. + */ + query_keepname(client, fname, dbuf); + } else { + /* + * We're not going to use fname, and need to release + * our hold on the name buffer so query_addsoa() + * may use it. + */ + query_releasename(client, &fname); + } + /* + * Add SOA. If the query was for a SOA record force the + * ttl to zero so that it is possible for clients to find + * the containing zone of an arbitrary name with a stub + * resolver and not have it cached. + */ + if (qtype == dns_rdatatype_soa) + result = query_addsoa(client, db, ISC_TRUE); + else + result = query_addsoa(client, db, ISC_FALSE); + if (result != ISC_R_SUCCESS) { + QUERY_ERROR(result); + goto cleanup; + } + /* + * Add NSEC record if we found one. + */ + if (dns_rdataset_isassociated(rdataset)) { + if (WANTDNSSEC(client)) { + query_addrrset(client, &fname, &rdataset, + &sigrdataset, + NULL, DNS_SECTION_AUTHORITY); + query_addwildcardproof(client, db, + client->query.qname, + ISC_FALSE); + } + } + /* + * Set message rcode. + */ + if (empty_wild) + client->message->rcode = dns_rcode_noerror; + else + client->message->rcode = dns_rcode_nxdomain; + goto cleanup; + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NCACHENXRRSET: + INSIST(!is_zone); + authoritative = ISC_FALSE; + /* + * Set message rcode, if required. + */ + if (result == DNS_R_NCACHENXDOMAIN) + client->message->rcode = dns_rcode_nxdomain; + /* + * We don't call query_addrrset() because we don't need any + * of its extra features (and things would probably break!). + */ + query_keepname(client, fname, dbuf); + dns_message_addname(client->message, fname, + DNS_SECTION_AUTHORITY); + ISC_LIST_APPEND(fname->list, rdataset, link); + fname = NULL; + rdataset = NULL; + goto cleanup; + case DNS_R_CNAME: + /* + * Keep a copy of the rdataset. We have to do this because + * query_addrrset may clear 'rdataset' (to prevent the + * cleanup code from cleaning it up). + */ + trdataset = rdataset; + /* + * Add the CNAME to the answer section. + */ + if (sigrdataset != NULL) + sigrdatasetp = &sigrdataset; + else + sigrdatasetp = NULL; + if (WANTDNSSEC(client) && + (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) + { + dns_fixedname_init(&wildcardname); + dns_name_copy(fname, dns_fixedname_name(&wildcardname), + NULL); + need_wildcardproof = ISC_TRUE; + } + if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0 && + WANTDNSSEC(client)) + noqname = rdataset; + else + noqname = NULL; + query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, + DNS_SECTION_ANSWER); + if (noqname != NULL) + query_addnoqnameproof(client, noqname); + /* + * We set the PARTIALANSWER attribute so that if anything goes + * wrong later on, we'll return what we've got so far. + */ + client->query.attributes |= NS_QUERYATTR_PARTIALANSWER; + /* + * Reset qname to be the target name of the CNAME and restart + * the query. + */ + tname = NULL; + result = dns_message_gettempname(client->message, &tname); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = dns_rdataset_first(trdataset); + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(client->message, &tname); + goto cleanup; + } + dns_rdataset_current(trdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &cname, NULL); + dns_rdata_reset(&rdata); + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(client->message, &tname); + goto cleanup; + } + dns_name_init(tname, NULL); + result = dns_name_dup(&cname.cname, client->mctx, tname); + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(client->message, &tname); + dns_rdata_freestruct(&cname); + goto cleanup; + } + dns_rdata_freestruct(&cname); + query_maybeputqname(client); + client->query.qname = tname; + want_restart = ISC_TRUE; + goto addauth; + case DNS_R_DNAME: + /* + * Compare the current qname to the found name. We need + * to know how many labels and bits are in common because + * we're going to have to split qname later on. + */ + namereln = dns_name_fullcompare(client->query.qname, fname, + &order, &nlabels); + INSIST(namereln == dns_namereln_subdomain); + /* + * Keep a copy of the rdataset. We have to do this because + * query_addrrset may clear 'rdataset' (to prevent the + * cleanup code from cleaning it up). + */ + trdataset = rdataset; + /* + * Add the DNAME to the answer section. + */ + if (sigrdataset != NULL) + sigrdatasetp = &sigrdataset; + else + sigrdatasetp = NULL; + if (WANTDNSSEC(client) && + (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) + { + dns_fixedname_init(&wildcardname); + dns_name_copy(fname, dns_fixedname_name(&wildcardname), + NULL); + need_wildcardproof = ISC_TRUE; + } + query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, + DNS_SECTION_ANSWER); + /* + * We set the PARTIALANSWER attribute so that if anything goes + * wrong later on, we'll return what we've got so far. + */ + client->query.attributes |= NS_QUERYATTR_PARTIALANSWER; + /* + * Get the target name of the DNAME. + */ + tname = NULL; + result = dns_message_gettempname(client->message, &tname); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = dns_rdataset_first(trdataset); + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(client->message, &tname); + goto cleanup; + } + dns_rdataset_current(trdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &dname, NULL); + dns_rdata_reset(&rdata); + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(client->message, &tname); + goto cleanup; + } + dns_name_init(tname, NULL); + dns_name_clone(&dname.dname, tname); + dns_rdata_freestruct(&dname); + /* + * Construct the new qname. + */ + dns_fixedname_init(&fixed); + prefix = dns_fixedname_name(&fixed); + dns_name_split(client->query.qname, nlabels, prefix, NULL); + INSIST(fname == NULL); + dbuf = query_getnamebuf(client); + if (dbuf == NULL) { + dns_message_puttempname(client->message, &tname); + goto cleanup; + } + fname = query_newname(client, dbuf, &b); + if (fname == NULL) { + dns_message_puttempname(client->message, &tname); + goto cleanup; + } + result = dns_name_concatenate(prefix, tname, fname, NULL); + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(client->message, &tname); + if (result == ISC_R_NOSPACE) { + /* + * RFC 2672, section 4.1, subsection 3c says + * we should return YXDOMAIN if the constructed + * name would be too long. + */ + client->message->rcode = dns_rcode_yxdomain; + } + goto cleanup; + } + query_keepname(client, fname, dbuf); + /* + * Synthesize a CNAME for this DNAME. + * + * We want to synthesize a CNAME since if we don't + * then older software that doesn't understand DNAME + * will not chain like it should. + * + * We do not try to synthesize a signature because we hope + * that security aware servers will understand DNAME. Also, + * even if we had an online key, making a signature + * on-the-fly is costly, and not really legitimate anyway + * since the synthesized CNAME is NOT in the zone. + */ + dns_name_init(tname, NULL); + (void)query_addcnamelike(client, client->query.qname, fname, + trdataset->trust, &tname, + dns_rdatatype_cname); + if (tname != NULL) + dns_message_puttempname(client->message, &tname); + /* + * Switch to the new qname and restart. + */ + query_maybeputqname(client); + client->query.qname = fname; + fname = NULL; + want_restart = ISC_TRUE; + goto addauth; + default: + /* + * Something has gone wrong. + */ + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + + if (WANTDNSSEC(client) && + (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) + { + dns_fixedname_init(&wildcardname); + dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL); + need_wildcardproof = ISC_TRUE; + } + + if (type == dns_rdatatype_any) { + /* + * XXXRTH Need to handle zonecuts with special case + * code. + */ + n = 0; + rdsiter = NULL; + result = dns_db_allrdatasets(db, node, version, 0, &rdsiter); + if (result != ISC_R_SUCCESS) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + /* + * Calling query_addrrset() with a non-NULL dbuf is going + * to either keep or release the name. We don't want it to + * release fname, since we may have to call query_addrrset() + * more than once. That means we have to call query_keepname() + * now, and pass a NULL dbuf to query_addrrset(). + * + * If we do a query_addrrset() below, we must set fname to + * NULL before leaving this block, otherwise we might try to + * cleanup fname even though we're using it! + */ + query_keepname(client, fname, dbuf); + tname = fname; + result = dns_rdatasetiter_first(rdsiter); + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(rdsiter, rdataset); + if ((qtype == dns_rdatatype_any || + rdataset->type == qtype) && rdataset->type != 0) { + query_addrrset(client, + fname != NULL ? &fname : &tname, + &rdataset, NULL, + NULL, DNS_SECTION_ANSWER); + n++; + INSIST(tname != NULL); + /* + * rdataset is non-NULL only in certain pathological + * cases involving DNAMEs. + */ + if (rdataset != NULL) + query_putrdataset(client, &rdataset); + rdataset = query_newrdataset(client); + if (rdataset == NULL) + break; + } else { + /* + * We're not interested in this rdataset. + */ + dns_rdataset_disassociate(rdataset); + } + result = dns_rdatasetiter_next(rdsiter); + } + + if (fname != NULL) + dns_message_puttempname(client->message, &fname); + + if (n == 0) { + /* + * We didn't match any rdatasets. + */ + if (qtype == dns_rdatatype_rrsig && + result == ISC_R_NOMORE) { + /* + * XXXRTH If this is a secure zone and we + * didn't find any SIGs, we should generate + * an error unless we were searching for + * glue. Ugh. + */ + /* + * We were searching for SIG records in + * a nonsecure zone. Send a "no error, + * no data" response. + */ + /* + * Add SOA. + */ + result = query_addsoa(client, db, ISC_FALSE); + if (result == ISC_R_SUCCESS) + result = ISC_R_NOMORE; + } else { + /* + * Something went wrong. + */ + result = DNS_R_SERVFAIL; + } + } + dns_rdatasetiter_destroy(&rdsiter); + if (result != ISC_R_NOMORE) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + } else { + /* + * This is the "normal" case -- an ordinary question to which + * we know the answer. + */ + if (sigrdataset != NULL) + sigrdatasetp = &sigrdataset; + else + sigrdatasetp = NULL; + if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0 && + WANTDNSSEC(client)) + noqname = rdataset; + else + noqname = NULL; + query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, + DNS_SECTION_ANSWER); + if (noqname != NULL) + query_addnoqnameproof(client, noqname); + /* + * We shouldn't ever fail to add 'rdataset' + * because it's already in the answer. + */ + INSIST(rdataset == NULL); + } + + addauth: + CTRACE("query_find: addauth"); + /* + * Add NS records to the authority section (if we haven't already + * added them to the answer section). + */ + if (!want_restart && !NOAUTHORITY(client)) { + if (is_zone) { + if (!((qtype == dns_rdatatype_ns || + qtype == dns_rdatatype_any) && + dns_name_equal(client->query.qname, + dns_db_origin(db)))) + (void)query_addns(client, db); + } else if (qtype != dns_rdatatype_ns) { + if (fname != NULL) + query_releasename(client, &fname); + query_addbestns(client); + } + } + + /* + * Add NSEC records to the authority section if they're needed for + * DNSSEC wildcard proofs. + */ + if (need_wildcardproof && dns_db_issecure(db)) + query_addwildcardproof(client, db, + dns_fixedname_name(&wildcardname), + ISC_TRUE); + cleanup: + CTRACE("query_find: cleanup"); + /* + * General cleanup. + */ + if (rdataset != NULL) + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); + if (fname != NULL) + query_releasename(client, &fname); + if (node != NULL) + dns_db_detachnode(db, &node); + if (db != NULL) + dns_db_detach(&db); + if (zone != NULL) + dns_zone_detach(&zone); + if (zdb != NULL) { + query_putrdataset(client, &zrdataset); + if (zsigrdataset != NULL) + query_putrdataset(client, &zsigrdataset); + if (zfname != NULL) + query_releasename(client, &zfname); + dns_db_detach(&zdb); + } + if (event != NULL) + isc_event_free(ISC_EVENT_PTR(&event)); + + /* + * AA bit. + */ + if (client->query.restarts == 0 && !authoritative) { + /* + * We're not authoritative, so we must ensure the AA bit + * isn't set. + */ + client->message->flags &= ~DNS_MESSAGEFLAG_AA; + } + + /* + * Restart the query? + */ + if (want_restart && client->query.restarts < MAX_RESTARTS) { + client->query.restarts++; + goto restart; + } + + if (eresult != ISC_R_SUCCESS && + (!PARTIALANSWER(client) || WANTRECURSION(client))) { + /* + * If we don't have any answer to give the client, + * or if the client requested recursion and thus wanted + * the complete answer, send an error response. + */ + query_error(client, eresult); + ns_client_detach(&client); + } else if (!RECURSING(client)) { + /* + * We are done. Set up sortlist data for the message + * rendering code, make a final tweak to the AA bit if the + * auth-nxdomain config option says so, then render and + * send the response. + */ + setup_query_sortlist(client); + + /* + * If this is a referral and the answer to the question + * is in the glue sort it to the start of the additional + * section. + */ + if (client->message->counts[DNS_SECTION_ANSWER] == 0 && + client->message->rcode == dns_rcode_noerror && + (qtype == dns_rdatatype_a || qtype == dns_rdatatype_aaaa)) + answer_in_glue(client, qtype); + + if (client->message->rcode == dns_rcode_nxdomain && + client->view->auth_nxdomain == ISC_TRUE) + client->message->flags |= DNS_MESSAGEFLAG_AA; + + query_send(client); + ns_client_detach(&client); + } + CTRACE("query_find: done"); +} + +static inline void +log_query(ns_client_t *client) { + char namebuf[DNS_NAME_FORMATSIZE]; + char typename[DNS_RDATATYPE_FORMATSIZE]; + char classname[DNS_RDATACLASS_FORMATSIZE]; + dns_rdataset_t *rdataset; + int level = ISC_LOG_INFO; + + if (! isc_log_wouldlog(ns_g_lctx, level)) + return; + + rdataset = ISC_LIST_HEAD(client->query.qname->list); + INSIST(rdataset != NULL); + dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); + dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname)); + dns_rdatatype_format(rdataset->type, typename, sizeof(typename)); + + ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY, + level, "query: %s %s %s %s%s%s", namebuf, classname, + typename, WANTRECURSION(client) ? "+" : "-", + (client->signer != NULL) ? "S": "", + (client->opt != NULL) ? "E" : ""); +} + +void +ns_query_start(ns_client_t *client) { + isc_result_t result; + dns_message_t *message = client->message; + dns_rdataset_t *rdataset; + ns_client_t *qclient; + dns_rdatatype_t qtype; + + CTRACE("ns_query_start"); + + /* + * Ensure that appropriate cleanups occur. + */ + client->next = query_next_callback; + + /* + * Behave as if we don't support DNSSEC if not enabled. + */ + if (!client->view->enablednssec) { + message->flags &= ~DNS_MESSAGEFLAG_CD; + client->extflags &= ~DNS_MESSAGEEXTFLAG_DO; + } + + if ((message->flags & DNS_MESSAGEFLAG_RD) != 0) + client->query.attributes |= NS_QUERYATTR_WANTRECURSION; + + if ((client->extflags & DNS_MESSAGEEXTFLAG_DO) != 0) + client->attributes |= NS_CLIENTATTR_WANTDNSSEC; + + if (client->view->minimalresponses) + client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | + NS_QUERYATTR_NOADDITIONAL); + + if ((client->view->cachedb == NULL) + || (!client->view->additionalfromcache)) { + /* + * We don't have a cache. Turn off cache support and + * recursion. + */ + client->query.attributes &= + ~(NS_QUERYATTR_RECURSIONOK|NS_QUERYATTR_CACHEOK); + } else if ((client->attributes & NS_CLIENTATTR_RA) == 0 || + (message->flags & DNS_MESSAGEFLAG_RD) == 0) { + /* + * If the client isn't allowed to recurse (due to + * "recursion no", the allow-recursion ACL, or the + * lack of a resolver in this view), or if it + * doesn't want recursion, turn recursion off. + */ + client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK; + } + + /* + * Get the question name. + */ + result = dns_message_firstname(message, DNS_SECTION_QUESTION); + if (result != ISC_R_SUCCESS) { + query_error(client, result); + return; + } + dns_message_currentname(message, DNS_SECTION_QUESTION, + &client->query.qname); + client->query.origqname = client->query.qname; + result = dns_message_nextname(message, DNS_SECTION_QUESTION); + if (result != ISC_R_NOMORE) { + if (result == ISC_R_SUCCESS) { + /* + * There's more than one QNAME in the question + * section. + */ + query_error(client, DNS_R_FORMERR); + } else + query_error(client, result); + return; + } + + if (ns_g_server->log_queries) + log_query(client); + + /* + * Check for multiple question queries, since edns1 is dead. + */ + if (message->counts[DNS_SECTION_QUESTION] > 1) { + query_error(client, DNS_R_FORMERR); + return; + } + + /* + * Check for meta-queries like IXFR and AXFR. + */ + rdataset = ISC_LIST_HEAD(client->query.qname->list); + INSIST(rdataset != NULL); + qtype = rdataset->type; + if (dns_rdatatype_ismeta(qtype)) { + switch (qtype) { + case dns_rdatatype_any: + break; /* Let query_find handle it. */ + case dns_rdatatype_ixfr: + case dns_rdatatype_axfr: + ns_xfr_start(client, rdataset->type); + return; + case dns_rdatatype_maila: + case dns_rdatatype_mailb: + query_error(client, DNS_R_NOTIMP); + return; + case dns_rdatatype_tkey: + result = dns_tkey_processquery(client->message, + ns_g_server->tkeyctx, + client->view->dynamickeys); + if (result == ISC_R_SUCCESS) + query_send(client); + else + query_error(client, result); + return; + default: /* TSIG, etc. */ + query_error(client, DNS_R_FORMERR); + return; + } + } + + /* + * If the client has requested that DNSSEC checking be disabled, + * allow lookups to return pending data and instruct the resolver + * to return data before validation has completed. + */ + if (message->flags & DNS_MESSAGEFLAG_CD || + qtype == dns_rdatatype_rrsig) + { + client->query.dboptions |= DNS_DBFIND_PENDINGOK; + client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE; + } + + /* + * Allow glue NS records to be added to the authority section + * if the answer is secure. + */ + if (message->flags & DNS_MESSAGEFLAG_CD) + client->query.attributes &= ~NS_QUERYATTR_SECURE; + + /* + * This is an ordinary query. + */ + result = dns_message_reply(message, ISC_TRUE); + if (result != ISC_R_SUCCESS) { + query_next(client, result); + return; + } + + /* + * Assume authoritative response until it is known to be + * otherwise. + */ + message->flags |= DNS_MESSAGEFLAG_AA; + + /* + * Set AD. We must clear it if we add non-validated data to a + * response. + */ + if (client->view->enablednssec) + message->flags |= DNS_MESSAGEFLAG_AD; + + qclient = NULL; + ns_client_attach(client, &qclient); + query_find(qclient, NULL, qtype); +} diff -Nuar bind-9.3.2-orig/bin/named/sdlz_helper.c bind-9.3.2-mod/bin/named/sdlz_helper.c --- bind-9.3.2-orig/bin/named/sdlz_helper.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/sdlz_helper.c 2006-02-20 19:41:46.000000000 +0100 @@ -0,0 +1,536 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +/*** + *** method prototypes + *** Declared here to shut up compiler warnings + *** about "no previous prototype" + ***/ + +void +destroy_querylist(isc_mem_t *mctx, query_list_t **querylist); + +isc_result_t +build_querylist(isc_mem_t *mctx, const char *query_str, char **zone, char **zone_domain, char **zone_tld, + char **record, char **client, query_list_t **querylist, + unsigned int flags); + +/*** + *** sdlz helper methods + ***/ + + /* properly destroys a querylist by de-allocating the + * memory for each query segment, and then the list itself + */ +void +destroy_querylist(isc_mem_t *mctx, query_list_t **querylist) +{ + query_segment_t *tseg = NULL; + query_segment_t *nseg = NULL; + + REQUIRE(mctx != NULL); + + // if query list is null, nothing to do + if(*querylist == NULL) + return; + + // start at the top of the list + nseg = ISC_LIST_HEAD(**querylist); + while(nseg != NULL){ // loop, until end of list + tseg = nseg; + // free the query segment's text string + // but only if it was really a query segment, and not + // a pointer to %zone%, or %record%, or %client% + if (tseg->sql != NULL && tseg->direct == isc_boolean_true) + isc_mem_free(mctx, tseg->sql); + // get the next query segment, before we destroy this one. + nseg = ISC_LIST_NEXT(nseg, link); + // deallocate this query segment. + isc_mem_put(mctx, tseg, sizeof(query_segment_t)); + } + // deallocate the query segment list + isc_mem_put(mctx, *querylist, sizeof(query_list_t)); +} + + /* constructs a query list by parsing a string into query segments */ +isc_result_t +build_querylist(isc_mem_t *mctx, const char *query_str, char **zone, char **zone_domain, char **zone_tld, + char **record, char **client, query_list_t **querylist, + unsigned int flags) +{ + isc_result_t result; + isc_boolean_t foundzone = isc_boolean_false; + isc_boolean_t foundzone_domain = isc_boolean_false; + isc_boolean_t foundzone_tld = isc_boolean_false; + isc_boolean_t foundrecord = isc_boolean_false; + isc_boolean_t foundclient = isc_boolean_false; + char *temp_str = NULL; + char *right_str = NULL; + query_list_t *tql; + query_segment_t *tseg = NULL; + + REQUIRE(querylist != NULL && *querylist == NULL); + REQUIRE(mctx != NULL); + + // if query string is null, or zero length + if(query_str == NULL || strlen(query_str) < 1){ + if((flags & SDLZH_REQUIRE_QUERY) == 0) // we don't need it were ok. + return (ISC_R_SUCCESS); + else // we did need it, PROBLEM!!! + return (ISC_R_FAILURE); + } + + /* allocate memory for query list */ + tql = isc_mem_get(mctx, sizeof(query_list_t)); + // couldn't allocate memory. Problem!! + if (tql == NULL) + return (ISC_R_NOMEMORY); + + // initialize the query segment list + ISC_LIST_INIT(*tql); + + // make a copy of query_str so we can chop it up + temp_str = right_str = isc_mem_strdup(mctx, query_str); + // couldn't make a copy, problem!! + if (right_str == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + + // loop through the string and chop it up + while(right_str != NULL){ + // allocate memory for tseg + tseg = isc_mem_get(mctx, sizeof(query_segment_t)); + if (tseg == NULL) { // no memory, clean everything up. + result = ISC_R_NOMEMORY; + goto cleanup; + } + tseg->sql = NULL; + tseg->direct = isc_boolean_false; + // initialize the query segment link + ISC_LINK_INIT(tseg, link); + // append the query segment to the list + ISC_LIST_APPEND(*tql, tseg, link); + + //split string at the first "%". set query segment to left portion + tseg->sql = isc_mem_strdup(mctx, isc_string_separate(&right_str, "%")); + if (tseg->sql == NULL) { // no memory, clean everything up. + result = ISC_R_NOMEMORY; + goto cleanup; + } + // tseg->sql points directly to a string. + tseg->direct = isc_boolean_true; + tseg->strlen = strlen(tseg->sql); + + // check if we encountered "%zone%" token + if(strcasecmp(tseg->sql, "zone") == 0){ + // we don't really need, or want the "zone" text, so get rid of it. + isc_mem_free(mctx, tseg->sql); + tseg->sql = (char**) zone; // set tseg->sql to in-direct zone string + tseg->strlen = 0; + // tseg->sql points in-directly to a string + tseg->direct = isc_boolean_false; + foundzone = isc_boolean_true; + // check if we encountered "%record%" token + } else if(strcasecmp(tseg->sql, "record") == 0){ + // we don't really need, or want the "record" text, so get rid of it. + isc_mem_free(mctx, tseg->sql); + // set tseg->sql to in-direct record string + tseg->sql = (char**) record; + tseg->strlen = 0; + // tseg->sql points in-directly poinsts to a string + tseg->direct = isc_boolean_false; + foundrecord = isc_boolean_true; + // check if we encountered "%client%" token + } else if(strcasecmp(tseg->sql, "client") == 0){ + // we don't really need, or want the "client" text, so get rid of it. + isc_mem_free(mctx, tseg->sql); + // set tseg->sql to in-direct record string + tseg->sql = (char**) client; + tseg->strlen = 0; + // tseg->sql points in-directly poinsts to a string + tseg->direct = isc_boolean_false; + foundclient = isc_boolean_true; + // check if we encountered zone_domain token (Extended patch) + } else if(strcasecmp(tseg->sql, "zone_domain") == 0){ + // we don't really need, or want the "zone_domain" text, so get rid of it. + isc_mem_free(mctx, tseg->sql); + tseg->sql = (char **) zone_domain; + tseg->strlen = 0; + // tseg->sql points in-directly to a string + tseg->direct = isc_boolean_false; + foundzone_domain = isc_boolean_true; // isn't usefull ... :-P + foundzone = isc_boolean_true; // no segfault + // check if we encountered zone_tld token (Extended patch) + } else if(strcasecmp(tseg->sql, "zone_tld") == 0){ + // we don't really need, or want the "zone_tld" text, so get rid of it. + isc_mem_free(mctx, tseg->sql); + tseg->sql = (char **) zone_tld; + tseg->strlen = 0; + // tseg->sql points in-directly to a string + tseg->direct = isc_boolean_false; + foundzone_tld = isc_boolean_true; // isn't usefull ... :-P + foundzone = isc_boolean_true; // no segfault + } + } + + // we don't need temp_str any more + isc_mem_free(mctx, temp_str); + // add checks later to verify zone and record are found if necessary. + + // if this query requires %client%, make sure we found it + if( ((flags & SDLZH_REQUIRE_CLIENT) != 0) && (!foundclient) ){ + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%client%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + // if this query requires %record%, make sure we found it + if( ((flags & SDLZH_REQUIRE_RECORD) != 0) && (!foundrecord) ){ + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%record%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + // if this query requires %zone%, make sure we found it + if( ((flags & SDLZH_REQUIRE_ZONE) != 0) && (!foundzone) ){ + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%zone%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + // pass back the query list + *querylist = (query_list_t *) tql; + + // return success + return (ISC_R_SUCCESS); + +cleanup: + // get rid of temp_str + if(temp_str != NULL) + isc_mem_free(mctx, temp_str); + +flag_fail: + // get rid of what was build of the query list + if(tql != NULL) + destroy_querylist(mctx, &tql); + return result; +} + + /* build a query string from query segments, and dynamic segments + * dynamic segments replace where the tokens %zone%, %record%, %client% + * used to be in our queries from named.conf + */ +char * +build_querystring(isc_mem_t *mctx, query_list_t *querylist) +{ + query_segment_t *tseg = NULL; + unsigned int length = 0; + char *qs = NULL; + + REQUIRE(mctx != NULL); + REQUIRE(querylist != NULL); + + // start at the top of the list + tseg = ISC_LIST_HEAD(*querylist); + while(tseg != NULL){ // loop to the end of the list + // if this is a query segment, use the + // precalculated string length + if (tseg->direct == isc_boolean_true) + length += tseg->strlen; + else { + if (tseg->sql && *(char**)tseg->sql) + // calculate string length for dynamic segments. + length += strlen(* (char**) tseg->sql); + } + tseg = ISC_LIST_NEXT(tseg, link); // get the next segment + } + + // allocate memory for the string + qs = isc_mem_allocate(mctx, length + 1); + // couldn't allocate memory, We need more ram! + if(qs == NULL) + return NULL; + + // start at the top of the list again + tseg = ISC_LIST_HEAD(*querylist); + // copy the first item in the list to the query string + if(tseg->direct == isc_boolean_true) // query segment + strcpy(qs, tseg->sql); + else + if (tseg->sql && *(char**)tseg->sql) + strcpy(qs, * (char**) tseg->sql); // dynamic segment + + // concatonate the rest of the segments + while((tseg = ISC_LIST_NEXT(tseg, link)) != NULL){ + if(tseg->direct == isc_boolean_true) // query segments + strcat(qs, tseg->sql); + else + if (tseg->sql && *(char**)tseg->sql) + strcat(qs, * (char**) tseg->sql); // dynamic segments + } + + return qs; +} + + /* constructs a sql dbinstance (DBI) */ +isc_result_t +build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str, + const char *allowxfr_str, const char *authority_str, + const char *findzone_str, const char *lookup_str, + const char *countzone_str, dbinstance_t **dbi) +{ + + isc_result_t result; + dbinstance_t *db = NULL; + + REQUIRE(dbi != NULL && *dbi == NULL); + REQUIRE(mctx != NULL); + + /* allocate and zero memory for driver structure */ + db = isc_mem_get(mctx, sizeof(dbinstance_t)); + if (db == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not allocate memory for database instance object."); + return (ISC_R_NOMEMORY); + } + memset(db, 0, sizeof(dbinstance_t)); + db->dbconn = NULL; + db->client = NULL; + db->record = NULL; + db->zone = NULL; + db->zone_domain = NULL; + db->zone_tld = NULL; + db->mctx = NULL; + db->query_buf = NULL; + db->allnodes_q = NULL; + db->allowxfr_q = NULL; + db->authority_q = NULL; + db->findzone_q = NULL; + db->countzone_q = NULL; + db->lookup_q = NULL; + + /* attach to the memory context */ + isc_mem_attach(mctx, &db->mctx); + + /* initialize the reference count mutex */ + result = isc_mutex_init(&db->instance_lock); + if (result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + goto cleanup; + } + + // build the all nodes query list + result = build_querylist(mctx, allnodes_str, &db->zone, &db->zone_domain, &db->zone_tld, &db->record, &db->client, + &db->allnodes_q, SDLZH_REQUIRE_ZONE); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build all nodes query list"); + goto cleanup; + } + + // build the allow zone transfer query list + result = build_querylist(mctx, allowxfr_str, &db->zone, &db->zone_domain, &db->zone_tld, &db->record, &db->client, + &db->allowxfr_q, SDLZH_REQUIRE_ZONE | SDLZH_REQUIRE_CLIENT); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build allow xfr query list"); + goto cleanup; + } + + // build the authority query, query list + result = build_querylist(mctx, authority_str, &db->zone, &db->zone_domain, &db->zone_tld, &db->record, &db->client, + &db->authority_q, SDLZH_REQUIRE_ZONE); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build authority query list"); + goto cleanup; + } + + // build findzone query, query list + result = build_querylist(mctx, findzone_str, &db->zone, &db->zone_domain, &db->zone_tld, &db->record, &db->client, + &db->findzone_q, SDLZH_REQUIRE_ZONE); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build find zone query list"); + goto cleanup; + } + + // build countzone query, query list + result = build_querylist(mctx, countzone_str, &db->zone, &db->zone_domain, &db->zone_tld, &db->record, &db->client, + &db->countzone_q, SDLZH_REQUIRE_ZONE); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build count zone query list"); + goto cleanup; + } + + // build lookup query, query list + result = build_querylist(mctx, lookup_str, &db->zone, &db->zone_domain, &db->zone_tld, &db->record, &db->client, + &db->lookup_q, SDLZH_REQUIRE_RECORD); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build lookup query list"); + goto cleanup; + } + + /* pass back the db instance */ + *dbi = (dbinstance_t *) db; + + // return success + return (ISC_R_SUCCESS); + +cleanup: + // destroy whatever was build of the db instance + destroy_sqldbinstance(db); + // return failure + return (ISC_R_FAILURE); +} + +void +destroy_sqldbinstance(dbinstance_t *dbi) +{ + isc_mem_t *mctx; + + /* save mctx for later */ + mctx = dbi->mctx; + + /* destroy any query lists we created */ + destroy_querylist(mctx, &dbi->allnodes_q); + destroy_querylist(mctx, &dbi->allowxfr_q); + destroy_querylist(mctx, &dbi->authority_q); + destroy_querylist(mctx, &dbi->findzone_q); + destroy_querylist(mctx, &dbi->countzone_q); + destroy_querylist(mctx, &dbi->lookup_q); + + /* get rid of the mutex */ + isc_mutex_destroy(&dbi->instance_lock); + + /* return, and detach the memory */ + isc_mem_put(mctx, dbi, sizeof(dbinstance_t)); + isc_mem_detach(&mctx); +} + +char * +getParameterValue(const char *input, const char* key) +{ + int keylen; + char *keystart; + char value[255]; + int i; + + if(key == NULL || input == NULL || strlen(input) < 1) + return NULL; + + keylen = strlen(key); + + if(keylen < 1) + return NULL; + + keystart = strstr(input, key); + + if(keystart == NULL) + return NULL; + + for(i=0; i < 255; i++){ + value[i] = keystart[keylen + i]; + if(value[i] == ' ' || value[i] == '\0'){ + value[i] = '\0'; + break; + } + } + + return isc_mem_strdup(ns_g_mctx, value); + +} + +#endif + diff -Nuar bind-9.3.2-orig/bin/named/sdlz_helper.c.orig bind-9.3.2-mod/bin/named/sdlz_helper.c.orig --- bind-9.3.2-orig/bin/named/sdlz_helper.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/sdlz_helper.c.orig 2006-02-20 19:35:32.000000000 +0100 @@ -0,0 +1,512 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +/*** + *** method prototypes + *** Declared here to shut up compiler warnings + *** about "no previous prototype" + ***/ + +void +destroy_querylist(isc_mem_t *mctx, query_list_t **querylist); + +isc_result_t +build_querylist(isc_mem_t *mctx, const char *query_str, char **zone, + char **record, char **client, query_list_t **querylist, + unsigned int flags); + +/*** + *** sdlz helper methods + ***/ + + /* properly destroys a querylist by de-allocating the + * memory for each query segment, and then the list itself + */ +void +destroy_querylist(isc_mem_t *mctx, query_list_t **querylist) +{ + query_segment_t *tseg = NULL; + query_segment_t *nseg = NULL; + + REQUIRE(mctx != NULL); + + // if query list is null, nothing to do + if(*querylist == NULL) + return; + + // start at the top of the list + nseg = ISC_LIST_HEAD(**querylist); + while(nseg != NULL){ // loop, until end of list + tseg = nseg; + // free the query segment's text string + // but only if it was really a query segment, and not + // a pointer to %zone%, or %record%, or %client% + if (tseg->sql != NULL && tseg->direct == isc_boolean_true) + isc_mem_free(mctx, tseg->sql); + // get the next query segment, before we destroy this one. + nseg = ISC_LIST_NEXT(nseg, link); + // deallocate this query segment. + isc_mem_put(mctx, tseg, sizeof(query_segment_t)); + } + // deallocate the query segment list + isc_mem_put(mctx, *querylist, sizeof(query_list_t)); +} + + /* constructs a query list by parsing a string into query segments */ +isc_result_t +build_querylist(isc_mem_t *mctx, const char *query_str, char **zone, + char **record, char **client, query_list_t **querylist, + unsigned int flags) +{ + isc_result_t result; + isc_boolean_t foundzone = isc_boolean_false; + isc_boolean_t foundrecord = isc_boolean_false; + isc_boolean_t foundclient = isc_boolean_false; + char *temp_str = NULL; + char *right_str = NULL; + query_list_t *tql; + query_segment_t *tseg = NULL; + + REQUIRE(querylist != NULL && *querylist == NULL); + REQUIRE(mctx != NULL); + + // if query string is null, or zero length + if(query_str == NULL || strlen(query_str) < 1){ + if((flags & SDLZH_REQUIRE_QUERY) == 0) // we don't need it were ok. + return (ISC_R_SUCCESS); + else // we did need it, PROBLEM!!! + return (ISC_R_FAILURE); + } + + /* allocate memory for query list */ + tql = isc_mem_get(mctx, sizeof(query_list_t)); + // couldn't allocate memory. Problem!! + if (tql == NULL) + return (ISC_R_NOMEMORY); + + // initialize the query segment list + ISC_LIST_INIT(*tql); + + // make a copy of query_str so we can chop it up + temp_str = right_str = isc_mem_strdup(mctx, query_str); + // couldn't make a copy, problem!! + if (right_str == NULL){ + result = ISC_R_NOMEMORY; + goto cleanup; + } + + // loop through the string and chop it up + while(right_str != NULL){ + // allocate memory for tseg + tseg = isc_mem_get(mctx, sizeof(query_segment_t)); + if (tseg == NULL) { // no memory, clean everything up. + result = ISC_R_NOMEMORY; + goto cleanup; + } + tseg->sql = NULL; + tseg->direct = isc_boolean_false; + // initialize the query segment link + ISC_LINK_INIT(tseg, link); + // append the query segment to the list + ISC_LIST_APPEND(*tql, tseg, link); + + //split string at the first "%". set query segment to left portion + tseg->sql = isc_mem_strdup(mctx, isc_string_separate(&right_str, "%")); + if (tseg->sql == NULL) { // no memory, clean everything up. + result = ISC_R_NOMEMORY; + goto cleanup; + } + // tseg->sql points directly to a string. + tseg->direct = isc_boolean_true; + tseg->strlen = strlen(tseg->sql); + + // check if we encountered "%zone%" token + if(strcasecmp(tseg->sql, "zone") == 0){ + // we don't really need, or want the "zone" text, so get rid of it. + isc_mem_free(mctx, tseg->sql); + tseg->sql = (char**) zone; // set tseg->sql to in-direct zone string + tseg->strlen = 0; + // tseg->sql points in-directly to a string + tseg->direct = isc_boolean_false; + foundzone = isc_boolean_true; + // check if we encountered "%record%" token + } else if(strcasecmp(tseg->sql, "record") == 0){ + // we don't really need, or want the "record" text, so get rid of it. + isc_mem_free(mctx, tseg->sql); + // set tseg->sql to in-direct record string + tseg->sql = (char**) record; + tseg->strlen = 0; + // tseg->sql points in-directly poinsts to a string + tseg->direct = isc_boolean_false; + foundrecord = isc_boolean_true; + // check if we encountered "%client%" token + } else if(strcasecmp(tseg->sql, "client") == 0){ + // we don't really need, or want the "client" text, so get rid of it. + isc_mem_free(mctx, tseg->sql); + // set tseg->sql to in-direct record string + tseg->sql = (char**) client; + tseg->strlen = 0; + // tseg->sql points in-directly poinsts to a string + tseg->direct = isc_boolean_false; + foundclient = isc_boolean_true; + } + } + + // we don't need temp_str any more + isc_mem_free(mctx, temp_str); + // add checks later to verify zone and record are found if necessary. + + // if this query requires %client%, make sure we found it + if( ((flags & SDLZH_REQUIRE_CLIENT) != 0) && (!foundclient) ){ + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%client%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + // if this query requires %record%, make sure we found it + if( ((flags & SDLZH_REQUIRE_RECORD) != 0) && (!foundrecord) ){ + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%record%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + // if this query requires %zone%, make sure we found it + if( ((flags & SDLZH_REQUIRE_ZONE) != 0) && (!foundzone) ){ + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%zone%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + // pass back the query list + *querylist = (query_list_t *) tql; + + // return success + return (ISC_R_SUCCESS); + +cleanup: + // get rid of temp_str + if(temp_str != NULL) + isc_mem_free(mctx, temp_str); + +flag_fail: + // get rid of what was build of the query list + if(tql != NULL) + destroy_querylist(mctx, &tql); + return result; +} + + /* build a query string from query segments, and dynamic segments + * dynamic segments replace where the tokens %zone%, %record%, %client% + * used to be in our queries from named.conf + */ +char * +build_querystring(isc_mem_t *mctx, query_list_t *querylist) +{ + query_segment_t *tseg = NULL; + unsigned int length = 0; + char *qs = NULL; + + REQUIRE(mctx != NULL); + REQUIRE(querylist != NULL); + + // start at the top of the list + tseg = ISC_LIST_HEAD(*querylist); + while(tseg != NULL){ // loop to the end of the list + // if this is a query segment, use the + // precalculated string length + if (tseg->direct == isc_boolean_true) + length += tseg->strlen; + else { + if (tseg->sql && *(char**)tseg->sql) + // calculate string length for dynamic segments. + length += strlen(* (char**) tseg->sql); + } + tseg = ISC_LIST_NEXT(tseg, link); // get the next segment + } + + // allocate memory for the string + qs = isc_mem_allocate(mctx, length + 1); + // couldn't allocate memory, We need more ram! + if(qs == NULL) + return NULL; + + // start at the top of the list again + tseg = ISC_LIST_HEAD(*querylist); + // copy the first item in the list to the query string + if(tseg->direct == isc_boolean_true) // query segment + strcpy(qs, tseg->sql); + else + if (tseg->sql && *(char**)tseg->sql) + strcpy(qs, * (char**) tseg->sql); // dynamic segment + + // concatonate the rest of the segments + while((tseg = ISC_LIST_NEXT(tseg, link)) != NULL){ + if(tseg->direct == isc_boolean_true) // query segments + strcat(qs, tseg->sql); + else + if (tseg->sql && *(char**)tseg->sql) + strcat(qs, * (char**) tseg->sql); // dynamic segments + } + + return qs; +} + + /* constructs a sql dbinstance (DBI) */ +isc_result_t +build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str, + const char *allowxfr_str, const char *authority_str, + const char *findzone_str, const char *lookup_str, + const char *countzone_str, dbinstance_t **dbi) +{ + + isc_result_t result; + dbinstance_t *db = NULL; + + REQUIRE(dbi != NULL && *dbi == NULL); + REQUIRE(mctx != NULL); + + /* allocate and zero memory for driver structure */ + db = isc_mem_get(mctx, sizeof(dbinstance_t)); + if (db == NULL){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not allocate memory for database instance object."); + return (ISC_R_NOMEMORY); + } + memset(db, 0, sizeof(dbinstance_t)); + db->dbconn = NULL; + db->client = NULL; + db->record = NULL; + db->zone = NULL; + db->mctx = NULL; + db->query_buf = NULL; + db->allnodes_q = NULL; + db->allowxfr_q = NULL; + db->authority_q = NULL; + db->findzone_q = NULL; + db->countzone_q = NULL; + db->lookup_q = NULL; + + /* attach to the memory context */ + isc_mem_attach(mctx, &db->mctx); + + /* initialize the reference count mutex */ + result = isc_mutex_init(&db->instance_lock); + if (result != ISC_R_SUCCESS){ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + goto cleanup; + } + + // build the all nodes query list + result = build_querylist(mctx, allnodes_str, &db->zone, &db->record, &db->client, + &db->allnodes_q, SDLZH_REQUIRE_ZONE); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build all nodes query list"); + goto cleanup; + } + + // build the allow zone transfer query list + result = build_querylist(mctx, allowxfr_str, &db->zone, &db->record, &db->client, + &db->allowxfr_q, SDLZH_REQUIRE_ZONE | SDLZH_REQUIRE_CLIENT); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build allow xfr query list"); + goto cleanup; + } + + // build the authority query, query list + result = build_querylist(mctx, authority_str, &db->zone, &db->record, &db->client, + &db->authority_q, SDLZH_REQUIRE_ZONE); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build authority query list"); + goto cleanup; + } + + // build findzone query, query list + result = build_querylist(mctx, findzone_str, &db->zone, &db->record, &db->client, + &db->findzone_q, SDLZH_REQUIRE_ZONE); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build find zone query list"); + goto cleanup; + } + + // build countzone query, query list + result = build_querylist(mctx, countzone_str, &db->zone, &db->record, &db->client, + &db->countzone_q, SDLZH_REQUIRE_ZONE); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build count zone query list"); + goto cleanup; + } + + // build lookup query, query list + result = build_querylist(mctx, lookup_str, &db->zone, &db->record, &db->client, + &db->lookup_q, SDLZH_REQUIRE_RECORD); + // if unsuccessful, log err msg and cleanup + if(result != ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build lookup query list"); + goto cleanup; + } + + /* pass back the db instance */ + *dbi = (dbinstance_t *) db; + + // return success + return (ISC_R_SUCCESS); + +cleanup: + // destroy whatever was build of the db instance + destroy_sqldbinstance(db); + // return failure + return (ISC_R_FAILURE); +} + +void +destroy_sqldbinstance(dbinstance_t *dbi) +{ + isc_mem_t *mctx; + + /* save mctx for later */ + mctx = dbi->mctx; + + /* destroy any query lists we created */ + destroy_querylist(mctx, &dbi->allnodes_q); + destroy_querylist(mctx, &dbi->allowxfr_q); + destroy_querylist(mctx, &dbi->authority_q); + destroy_querylist(mctx, &dbi->findzone_q); + destroy_querylist(mctx, &dbi->countzone_q); + destroy_querylist(mctx, &dbi->lookup_q); + + /* get rid of the mutex */ + isc_mutex_destroy(&dbi->instance_lock); + + /* return, and detach the memory */ + isc_mem_put(mctx, dbi, sizeof(dbinstance_t)); + isc_mem_detach(&mctx); +} + +char * +getParameterValue(const char *input, const char* key) +{ + int keylen; + char *keystart; + char value[255]; + int i; + + if(key == NULL || input == NULL || strlen(input) < 1) + return NULL; + + keylen = strlen(key); + + if(keylen < 1) + return NULL; + + keystart = strstr(input, key); + + if(keystart == NULL) + return NULL; + + for(i=0; i < 255; i++){ + value[i] = keystart[keylen + i]; + if(value[i] == ' ' || value[i] == '\0'){ + value[i] = '\0'; + break; + } + } + + return isc_mem_strdup(ns_g_mctx, value); + +} + +#endif + diff -Nuar bind-9.3.2-orig/bin/named/server.c bind-9.3.2-mod/bin/named/server.c --- bind-9.3.2-orig/bin/named/server.c 2005-07-27 04:53:15.000000000 +0200 +++ bind-9.3.2-mod/bin/named/server.c 2006-02-20 19:13:47.000000000 +0100 @@ -45,6 +45,9 @@ #include #include #include +#ifdef DLZ +#include +#endif #include #include #include @@ -729,6 +732,11 @@ cfg_obj_t *forwarders; cfg_obj_t *alternates; cfg_obj_t *zonelist; +#ifdef DLZ + cfg_obj_t *dlz; + unsigned int dlzargc; + char **dlzargv; +#endif cfg_obj_t *disabled; cfg_obj_t *obj; cfg_listelt_t *element; @@ -797,6 +805,34 @@ actx)); } +#ifdef DLZ + /* + * Create Dynamically Loadable Zone driver. + */ + dlz = NULL; + if (voptions != NULL) + (void)cfg_map_get(voptions, "dlz", &dlz); + else + (void)cfg_map_get(config, "dlz", &dlz); + + obj = NULL; + if (dlz != NULL){ + (void)cfg_map_get(cfg_tuple_get(dlz, "options"), "database", &obj); + if(obj != NULL){ + dns_dlzstrtoargv(mctx, cfg_obj_asstring(obj), &dlzargc, &dlzargv); + result = dns_dlzcreate(mctx, cfg_obj_asstring( + cfg_tuple_get(dlz, "name")), dlzargv[0], dlzargc, dlzargv, + &view->dlzdatabase); + if(result == ISC_R_SUCCESS){ + isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv)); + } else { + goto cleanup; + } + } + } + +#endif + /* * Configure the view's cache. Try to reuse an existing * cache if possible, otherwise create a new cache. diff -Nuar bind-9.3.2-orig/bin/named/server.c.orig bind-9.3.2-mod/bin/named/server.c.orig --- bind-9.3.2-orig/bin/named/server.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/bin/named/server.c.orig 2005-07-27 04:53:15.000000000 +0200 @@ -0,0 +1,4153 @@ +/* + * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2003 Internet Software Consortium. + * + * 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: server.c,v 1.339.2.15.2.65 2005/07/27 02:53:15 marka Exp $ */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBSCF +#include +#include +#endif + +/* + * Check an operation for failure. Assumes that the function + * using it has a 'result' variable and a 'cleanup' label. + */ +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto cleanup; \ + } while (0) + +#define CHECKM(op, msg) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) { \ + isc_log_write(ns_g_lctx, \ + NS_LOGCATEGORY_GENERAL, \ + NS_LOGMODULE_SERVER, \ + ISC_LOG_ERROR, \ + "%s: %s", msg, \ + isc_result_totext(result)); \ + goto cleanup; \ + } \ + } while (0) \ + +#define CHECKMF(op, msg, file) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) { \ + isc_log_write(ns_g_lctx, \ + NS_LOGCATEGORY_GENERAL, \ + NS_LOGMODULE_SERVER, \ + ISC_LOG_ERROR, \ + "%s '%s': %s", msg, file, \ + isc_result_totext(result)); \ + goto cleanup; \ + } \ + } while (0) \ + +#define CHECKFATAL(op, msg) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) \ + fatal(msg, result); \ + } while (0) \ + +struct ns_dispatch { + isc_sockaddr_t addr; + unsigned int dispatchgen; + dns_dispatch_t *dispatch; + ISC_LINK(struct ns_dispatch) link; +}; + +struct dumpcontext { + isc_mem_t *mctx; + isc_boolean_t dumpcache; + isc_boolean_t dumpzones; + FILE *fp; + ISC_LIST(struct viewlistentry) viewlist; + struct viewlistentry *view; + struct zonelistentry *zone; + dns_dumpctx_t *mdctx; + dns_db_t *db; + dns_db_t *cache; + isc_task_t *task; + dns_dbversion_t *version; +}; + +struct viewlistentry { + dns_view_t *view; + ISC_LINK(struct viewlistentry) link; + ISC_LIST(struct zonelistentry) zonelist; +}; + +struct zonelistentry { + dns_zone_t *zone; + ISC_LINK(struct zonelistentry) link; +}; + +static void +fatal(const char *msg, isc_result_t result); + +static void +ns_server_reload(isc_task_t *task, isc_event_t *event); + +static isc_result_t +ns_listenelt_fromconfig(cfg_obj_t *listener, cfg_obj_t *config, + ns_aclconfctx_t *actx, + isc_mem_t *mctx, ns_listenelt_t **target); +static isc_result_t +ns_listenlist_fromconfig(cfg_obj_t *listenlist, cfg_obj_t *config, + ns_aclconfctx_t *actx, + isc_mem_t *mctx, ns_listenlist_t **target); + +static isc_result_t +configure_forward(cfg_obj_t *config, dns_view_t *view, dns_name_t *origin, + cfg_obj_t *forwarders, cfg_obj_t *forwardtype); + +static isc_result_t +configure_alternates(cfg_obj_t *config, dns_view_t *view, + cfg_obj_t *alternates); + +static isc_result_t +configure_zone(cfg_obj_t *config, cfg_obj_t *zconfig, cfg_obj_t *vconfig, + isc_mem_t *mctx, dns_view_t *view, + ns_aclconfctx_t *aclconf); + +static void +end_reserved_dispatches(ns_server_t *server, isc_boolean_t all); + +/* + * Configure a single view ACL at '*aclp'. Get its configuration by + * calling 'getvcacl' (for per-view configuration) and maybe 'getscacl' + * (for a global default). + */ +static isc_result_t +configure_view_acl(cfg_obj_t *vconfig, cfg_obj_t *config, + const char *aclname, ns_aclconfctx_t *actx, + isc_mem_t *mctx, dns_acl_t **aclp) +{ + isc_result_t result; + cfg_obj_t *maps[3]; + cfg_obj_t *aclobj = NULL; + int i = 0; + + if (*aclp != NULL) + dns_acl_detach(aclp); + if (vconfig != NULL) + maps[i++] = cfg_tuple_get(vconfig, "options"); + if (config != NULL) { + cfg_obj_t *options = NULL; + (void)cfg_map_get(config, "options", &options); + if (options != NULL) + maps[i++] = options; + } + maps[i] = NULL; + + result = ns_config_get(maps, aclname, &aclobj); + if (aclobj == NULL) + /* + * No value available. *aclp == NULL. + */ + return (ISC_R_SUCCESS); + + result = ns_acl_fromconfig(aclobj, config, actx, mctx, aclp); + + return (result); +} + +static isc_result_t +configure_view_dnsseckey(cfg_obj_t *vconfig, cfg_obj_t *key, + dns_keytable_t *keytable, isc_mem_t *mctx) +{ + dns_rdataclass_t viewclass; + dns_rdata_dnskey_t keystruct; + isc_uint32_t flags, proto, alg; + char *keystr, *keynamestr; + unsigned char keydata[4096]; + isc_buffer_t keydatabuf; + unsigned char rrdata[4096]; + isc_buffer_t rrdatabuf; + isc_region_t r; + dns_fixedname_t fkeyname; + dns_name_t *keyname; + isc_buffer_t namebuf; + isc_result_t result; + dst_key_t *dstkey = NULL; + + flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags")); + proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol")); + alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm")); + keyname = dns_fixedname_name(&fkeyname); + keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); + + if (vconfig == NULL) + viewclass = dns_rdataclass_in; + else { + cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class"); + CHECK(ns_config_getclass(classobj, dns_rdataclass_in, + &viewclass)); + } + keystruct.common.rdclass = viewclass; + keystruct.common.rdtype = dns_rdatatype_dnskey; + /* + * The key data in keystruct is not dynamically allocated. + */ + keystruct.mctx = NULL; + + ISC_LINK_INIT(&keystruct.common, link); + + if (flags > 0xffff) + CHECKM(ISC_R_RANGE, "key flags"); + if (proto > 0xff) + CHECKM(ISC_R_RANGE, "key protocol"); + if (alg > 0xff) + CHECKM(ISC_R_RANGE, "key algorithm"); + keystruct.flags = (isc_uint16_t)flags; + keystruct.protocol = (isc_uint8_t)proto; + keystruct.algorithm = (isc_uint8_t)alg; + + isc_buffer_init(&keydatabuf, keydata, sizeof(keydata)); + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); + + keystr = cfg_obj_asstring(cfg_tuple_get(key, "key")); + CHECK(isc_base64_decodestring(keystr, &keydatabuf)); + isc_buffer_usedregion(&keydatabuf, &r); + keystruct.datalen = r.length; + keystruct.data = r.base; + + CHECK(dns_rdata_fromstruct(NULL, + keystruct.common.rdclass, + keystruct.common.rdtype, + &keystruct, &rrdatabuf)); + dns_fixedname_init(&fkeyname); + isc_buffer_init(&namebuf, keynamestr, strlen(keynamestr)); + isc_buffer_add(&namebuf, strlen(keynamestr)); + CHECK(dns_name_fromtext(keyname, &namebuf, + dns_rootname, ISC_FALSE, + NULL)); + CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf, + mctx, &dstkey)); + + CHECK(dns_keytable_add(keytable, &dstkey)); + INSIST(dstkey == NULL); + return (ISC_R_SUCCESS); + + cleanup: + if (result == DST_R_NOCRYPTO) { + cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR, + "ignoring trusted key for '%s': no crypto support", + keynamestr); + result = ISC_R_SUCCESS; + } else { + cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR, + "configuring trusted key for '%s': %s", + keynamestr, isc_result_totext(result)); + result = ISC_R_FAILURE; + } + + if (dstkey != NULL) + dst_key_free(&dstkey); + + return (result); +} + +/* + * Configure DNSSEC keys for a view. Currently used only for + * the security roots. + * + * The per-view configuration values and the server-global defaults are read + * from 'vconfig' and 'config'. The variable to be configured is '*target'. + */ +static isc_result_t +configure_view_dnsseckeys(cfg_obj_t *vconfig, cfg_obj_t *config, + isc_mem_t *mctx, dns_keytable_t **target) +{ + isc_result_t result; + cfg_obj_t *keys = NULL; + cfg_obj_t *voptions = NULL; + cfg_listelt_t *element, *element2; + cfg_obj_t *keylist; + cfg_obj_t *key; + dns_keytable_t *keytable = NULL; + + CHECK(dns_keytable_create(mctx, &keytable)); + + if (vconfig != NULL) + voptions = cfg_tuple_get(vconfig, "options"); + + keys = NULL; + if (voptions != NULL) + (void)cfg_map_get(voptions, "trusted-keys", &keys); + if (keys == NULL) + (void)cfg_map_get(config, "trusted-keys", &keys); + + for (element = cfg_list_first(keys); + element != NULL; + element = cfg_list_next(element)) + { + keylist = cfg_listelt_value(element); + for (element2 = cfg_list_first(keylist); + element2 != NULL; + element2 = cfg_list_next(element2)) + { + key = cfg_listelt_value(element2); + CHECK(configure_view_dnsseckey(vconfig, key, + keytable, mctx)); + } + } + + dns_keytable_detach(target); + *target = keytable; /* Transfer ownership. */ + keytable = NULL; + result = ISC_R_SUCCESS; + + cleanup: + return (result); +} + +static isc_result_t +mustbesecure(cfg_obj_t *mbs, dns_resolver_t *resolver) +{ + cfg_listelt_t *element; + cfg_obj_t *obj; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_boolean_t value; + isc_result_t result; + isc_buffer_t b; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + for (element = cfg_list_first(mbs); + element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, + ISC_FALSE, NULL)); + value = cfg_obj_asboolean(cfg_tuple_get(obj, "value")); + CHECK(dns_resolver_setmustbesecure(resolver, name, value)); + } + + result = ISC_R_SUCCESS; + + cleanup: + return (result); +} + +/* + * Get a dispatch appropriate for the resolver of a given view. + */ +static isc_result_t +get_view_querysource_dispatch(cfg_obj_t **maps, + int af, dns_dispatch_t **dispatchp) +{ + isc_result_t result; + dns_dispatch_t *disp; + isc_sockaddr_t sa; + unsigned int attrs, attrmask; + cfg_obj_t *obj = NULL; + + /* + * Make compiler happy. + */ + result = ISC_R_FAILURE; + + switch (af) { + case AF_INET: + result = ns_config_get(maps, "query-source", &obj); + INSIST(result == ISC_R_SUCCESS); + + break; + case AF_INET6: + result = ns_config_get(maps, "query-source-v6", &obj); + INSIST(result == ISC_R_SUCCESS); + break; + default: + INSIST(0); + } + + sa = *(cfg_obj_assockaddr(obj)); + INSIST(isc_sockaddr_pf(&sa) == af); + + /* + * If we don't support this address family, we're done! + */ + switch (af) { + case AF_INET: + result = isc_net_probeipv4(); + break; + case AF_INET6: + result = isc_net_probeipv6(); + break; + default: + INSIST(0); + } + if (result != ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + + /* + * Try to find a dispatcher that we can share. + */ + attrs = 0; + attrs |= DNS_DISPATCHATTR_UDP; + switch (af) { + case AF_INET: + attrs |= DNS_DISPATCHATTR_IPV4; + break; + case AF_INET6: + attrs |= DNS_DISPATCHATTR_IPV6; + break; + } + attrmask = 0; + attrmask |= DNS_DISPATCHATTR_UDP; + attrmask |= DNS_DISPATCHATTR_TCP; + attrmask |= DNS_DISPATCHATTR_IPV4; + attrmask |= DNS_DISPATCHATTR_IPV6; + + disp = NULL; + result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr, + ns_g_taskmgr, &sa, 4096, + 1000, 32768, 16411, 16433, + attrs, attrmask, &disp); + if (result != ISC_R_SUCCESS) { + isc_sockaddr_t any; + char buf[ISC_SOCKADDR_FORMATSIZE]; + + switch (af) { + case AF_INET: + isc_sockaddr_any(&any); + break; + case AF_INET6: + isc_sockaddr_any6(&any); + break; + } + if (isc_sockaddr_equal(&sa, &any)) + return (ISC_R_SUCCESS); + isc_sockaddr_format(&sa, buf, sizeof(buf)); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "could not get query source dispatcher (%s)", + buf); + return (result); + } + + *dispatchp = disp; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +configure_order(dns_order_t *order, cfg_obj_t *ent) { + dns_rdataclass_t rdclass; + dns_rdatatype_t rdtype; + cfg_obj_t *obj; + dns_fixedname_t fixed; + unsigned int mode = 0; + const char *str; + isc_buffer_t b; + isc_result_t result; + isc_boolean_t addroot; + + result = ns_config_getclass(cfg_tuple_get(ent, "class"), + dns_rdataclass_any, &rdclass); + if (result != ISC_R_SUCCESS) + return (result); + + result = ns_config_gettype(cfg_tuple_get(ent, "type"), + dns_rdatatype_any, &rdtype); + if (result != ISC_R_SUCCESS) + return (result); + + obj = cfg_tuple_get(ent, "name"); + if (cfg_obj_isstring(obj)) + str = cfg_obj_asstring(obj); + else + str = "*"; + addroot = ISC_TF(strcmp(str, "*") == 0); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + dns_fixedname_init(&fixed); + result = dns_name_fromtext(dns_fixedname_name(&fixed), &b, + dns_rootname, ISC_FALSE, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + obj = cfg_tuple_get(ent, "ordering"); + INSIST(cfg_obj_isstring(obj)); + str = cfg_obj_asstring(obj); + if (!strcasecmp(str, "fixed")) + mode = DNS_RDATASETATTR_FIXEDORDER; + else if (!strcasecmp(str, "random")) + mode = DNS_RDATASETATTR_RANDOMIZE; + else if (!strcasecmp(str, "cyclic")) + mode = 0; + else + INSIST(0); + + /* + * "*" should match everything including the root (BIND 8 compat). + * As dns_name_matcheswildcard(".", "*.") returns FALSE add a + * explict entry for "." when the name is "*". + */ + if (addroot) { + result = dns_order_add(order, dns_rootname, + rdtype, rdclass, mode); + if (result != ISC_R_SUCCESS) + return (result); + } + + return (dns_order_add(order, dns_fixedname_name(&fixed), + rdtype, rdclass, mode)); +} + +static isc_result_t +configure_peer(cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) { + isc_sockaddr_t *sa; + isc_netaddr_t na; + dns_peer_t *peer; + cfg_obj_t *obj; + char *str; + isc_result_t result; + + sa = cfg_obj_assockaddr(cfg_map_getname(cpeer)); + isc_netaddr_fromsockaddr(&na, sa); + + peer = NULL; + result = dns_peer_new(mctx, &na, &peer); + if (result != ISC_R_SUCCESS) + return (result); + + obj = NULL; + (void)cfg_map_get(cpeer, "bogus", &obj); + if (obj != NULL) + CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj))); + + obj = NULL; + (void)cfg_map_get(cpeer, "provide-ixfr", &obj); + if (obj != NULL) + CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj))); + + obj = NULL; + (void)cfg_map_get(cpeer, "request-ixfr", &obj); + if (obj != NULL) + CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj))); + + obj = NULL; + (void)cfg_map_get(cpeer, "edns", &obj); + if (obj != NULL) + CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj))); + + obj = NULL; + (void)cfg_map_get(cpeer, "transfers", &obj); + if (obj != NULL) + CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj))); + + obj = NULL; + (void)cfg_map_get(cpeer, "transfer-format", &obj); + if (obj != NULL) { + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "many-answers") == 0) + CHECK(dns_peer_settransferformat(peer, + dns_many_answers)); + else if (strcasecmp(str, "one-answer") == 0) + CHECK(dns_peer_settransferformat(peer, + dns_one_answer)); + else + INSIST(0); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "keys", &obj); + if (obj != NULL) { + result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj)); + if (result != ISC_R_SUCCESS) + goto cleanup; + } + + obj = NULL; + if (isc_sockaddr_pf(sa) == AF_INET) + (void)cfg_map_get(cpeer, "transfer-source", &obj); + else + (void)cfg_map_get(cpeer, "transfer-source-v6", &obj); + if (obj != NULL) { + result = dns_peer_settransfersource(peer, + cfg_obj_assockaddr(obj)); + if (result != ISC_R_SUCCESS) + goto cleanup; + } + *peerp = peer; + return (ISC_R_SUCCESS); + + cleanup: + dns_peer_detach(&peer); + return (result); +} + +static isc_result_t +disable_algorithms(cfg_obj_t *disabled, dns_resolver_t *resolver) { + isc_result_t result; + cfg_obj_t *algorithms; + cfg_listelt_t *element; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t b; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + str = cfg_obj_asstring(cfg_tuple_get(disabled, "name")); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL)); + + algorithms = cfg_tuple_get(disabled, "algorithms"); + for (element = cfg_list_first(algorithms); + element != NULL; + element = cfg_list_next(element)) + { + isc_textregion_t r; + dns_secalg_t alg; + + r.base = cfg_obj_asstring(cfg_listelt_value(element)); + r.length = strlen(r.base); + + result = dns_secalg_fromtext(&alg, &r); + if (result != ISC_R_SUCCESS) { + isc_uint8_t ui; + result = isc_parse_uint8(&ui, r.base, 10); + alg = ui; + } + if (result != ISC_R_SUCCESS) { + cfg_obj_log(cfg_listelt_value(element), + ns_g_lctx, ISC_LOG_ERROR, + "invalid algorithm"); + CHECK(result); + } + CHECK(dns_resolver_disable_algorithm(resolver, name, alg)); + } + cleanup: + return (result); +} + +/* + * Configure 'view' according to 'vconfig', taking defaults from 'config' + * where values are missing in 'vconfig'. + * + * When configuring the default view, 'vconfig' will be NULL and the + * global defaults in 'config' used exclusively. + */ +static isc_result_t +configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, + isc_mem_t *mctx, ns_aclconfctx_t *actx, + isc_boolean_t need_hints) +{ + cfg_obj_t *maps[4]; + cfg_obj_t *cfgmaps[3]; + cfg_obj_t *options = NULL; + cfg_obj_t *voptions = NULL; + cfg_obj_t *forwardtype; + cfg_obj_t *forwarders; + cfg_obj_t *alternates; + cfg_obj_t *zonelist; + cfg_obj_t *disabled; + cfg_obj_t *obj; + cfg_listelt_t *element; + in_port_t port; + dns_cache_t *cache = NULL; + isc_result_t result; + isc_uint32_t max_adb_size; + isc_uint32_t max_cache_size; + isc_uint32_t lame_ttl; + dns_tsig_keyring_t *ring; + dns_view_t *pview = NULL; /* Production view */ + isc_mem_t *cmctx; + dns_dispatch_t *dispatch4 = NULL; + dns_dispatch_t *dispatch6 = NULL; + isc_boolean_t reused_cache = ISC_FALSE; + int i; + const char *str; + dns_order_t *order = NULL; + isc_uint32_t udpsize; + unsigned int check = 0; + + REQUIRE(DNS_VIEW_VALID(view)); + + cmctx = NULL; + + if (config != NULL) + (void)cfg_map_get(config, "options", &options); + + i = 0; + if (vconfig != NULL) { + voptions = cfg_tuple_get(vconfig, "options"); + maps[i++] = voptions; + } + if (options != NULL) + maps[i++] = options; + maps[i++] = ns_g_defaults; + maps[i] = NULL; + + i = 0; + if (voptions != NULL) + cfgmaps[i++] = voptions; + if (config != NULL) + cfgmaps[i++] = config; + cfgmaps[i] = NULL; + + /* + * Set the view's port number for outgoing queries. + */ + CHECKM(ns_config_getport(config, &port), "port"); + dns_view_setdstport(view, port); + + /* + * Configure the zones. + */ + zonelist = NULL; + if (voptions != NULL) + (void)cfg_map_get(voptions, "zone", &zonelist); + else + (void)cfg_map_get(config, "zone", &zonelist); + for (element = cfg_list_first(zonelist); + element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *zconfig = cfg_listelt_value(element); + CHECK(configure_zone(config, zconfig, vconfig, mctx, view, + actx)); + } + + /* + * Configure the view's cache. Try to reuse an existing + * cache if possible, otherwise create a new cache. + * Note that the ADB is not preserved in either case. + * + * XXX Determining when it is safe to reuse a cache is + * tricky. When the view's configuration changes, the cached + * data may become invalid because it reflects our old + * view of the world. As more view attributes become + * configurable, we will have to add code here to check + * whether they have changed in ways that could + * invalidate the cache. + */ + result = dns_viewlist_find(&ns_g_server->viewlist, + view->name, view->rdclass, + &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) + goto cleanup; + if (pview != NULL) { + INSIST(pview->cache != NULL); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3), + "reusing existing cache"); + reused_cache = ISC_TRUE; + dns_cache_attach(pview->cache, &cache); + dns_view_detach(&pview); + } else { + CHECK(isc_mem_create(0, 0, &cmctx)); + CHECK(dns_cache_create(cmctx, ns_g_taskmgr, ns_g_timermgr, + view->rdclass, "rbt", 0, NULL, &cache)); + } + dns_view_setcache(view, cache); + + /* + * cache-file cannot be inherited if views are present, but this + * should be caught by the configuration checking stage. + */ + obj = NULL; + result = ns_config_get(maps, "cache-file", &obj); + if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) { + CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj))); + if (!reused_cache) + CHECK(dns_cache_load(cache)); + } + + obj = NULL; + result = ns_config_get(maps, "cleaning-interval", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_cache_setcleaninginterval(cache, cfg_obj_asuint32(obj) * 60); + + obj = NULL; + result = ns_config_get(maps, "max-cache-size", &obj); + INSIST(result == ISC_R_SUCCESS); + if (cfg_obj_isstring(obj)) { + str = cfg_obj_asstring(obj); + INSIST(strcasecmp(str, "unlimited") == 0); + max_cache_size = ISC_UINT32_MAX; + } else { + isc_resourcevalue_t value; + value = cfg_obj_asuint64(obj); + if (value > ISC_UINT32_MAX) { + cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, + "'max-cache-size " + "%" ISC_PRINT_QUADFORMAT "d' is too large", + value); + result = ISC_R_RANGE; + goto cleanup; + } + max_cache_size = (isc_uint32_t)value; + } + dns_cache_setcachesize(cache, max_cache_size); + + dns_cache_detach(&cache); + + /* + * Check-names. + */ + obj = NULL; + result = ns_checknames_get(maps, "response", &obj); + INSIST(result == ISC_R_SUCCESS); + + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "fail") == 0) { + check = DNS_RESOLVER_CHECKNAMES | + DNS_RESOLVER_CHECKNAMESFAIL; + view->checknames = ISC_TRUE; + } else if (strcasecmp(str, "warn") == 0) { + check = DNS_RESOLVER_CHECKNAMES; + view->checknames = ISC_FALSE; + } else if (strcasecmp(str, "ignore") == 0) { + check = 0; + view->checknames = ISC_FALSE; + } else + INSIST(0); + + /* + * Resolver. + * + * XXXRTH Hardwired number of tasks. + */ + CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4)); + CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6)); + if (dispatch4 == NULL && dispatch6 == NULL) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "unable to obtain neither an IPv4 nor" + " an IPv6 dispatch"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31, + ns_g_socketmgr, ns_g_timermgr, + check, ns_g_dispatchmgr, + dispatch4, dispatch6)); + + /* + * Set the ADB cache size to 1/8th of the max-cache-size. + */ + max_adb_size = 0; + if (max_cache_size != 0) { + max_adb_size = max_cache_size / 8; + if (max_adb_size == 0) + max_adb_size = 1; /* Force minimum. */ + } + dns_adb_setadbsize(view->adb, max_adb_size); + + /* + * Set resolver's lame-ttl. + */ + obj = NULL; + result = ns_config_get(maps, "lame-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + lame_ttl = cfg_obj_asuint32(obj); + if (lame_ttl > 1800) + lame_ttl = 1800; + dns_resolver_setlamettl(view->resolver, lame_ttl); + + /* + * Set the resolver's EDNS UDP size. + */ + obj = NULL; + result = ns_config_get(maps, "edns-udp-size", &obj); + INSIST(result == ISC_R_SUCCESS); + udpsize = cfg_obj_asuint32(obj); + if (udpsize < 512) + udpsize = 512; + if (udpsize > 4096) + udpsize = 4096; + dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize); + + /* + * Set supported DNSSEC algorithms. + */ + dns_resolver_reset_algorithms(view->resolver); + disabled = NULL; + (void)ns_config_get(maps, "disable-algorithms", &disabled); + if (disabled != NULL) { + for (element = cfg_list_first(disabled); + element != NULL; + element = cfg_list_next(element)) + CHECK(disable_algorithms(cfg_listelt_value(element), + view->resolver)); + } + + /* + * A global or view "forwarders" option, if present, + * creates an entry for "." in the forwarding table. + */ + forwardtype = NULL; + forwarders = NULL; + (void)ns_config_get(maps, "forward", &forwardtype); + (void)ns_config_get(maps, "forwarders", &forwarders); + if (forwarders != NULL) + CHECK(configure_forward(config, view, dns_rootname, + forwarders, forwardtype)); + + /* + * Dual Stack Servers. + */ + alternates = NULL; + (void)ns_config_get(maps, "dual-stack-servers", &alternates); + if (alternates != NULL) + CHECK(configure_alternates(config, view, alternates)); + + /* + * We have default hints for class IN if we need them. + */ + if (view->rdclass == dns_rdataclass_in && view->hints == NULL) + dns_view_sethints(view, ns_g_server->in_roothints); + + /* + * If we still have no hints, this is a non-IN view with no + * "hints zone" configured. Issue a warning, except if this + * is a root server. Root servers never need to consult + * their hints, so it's no point requiring users to configure + * them. + */ + if (view->hints == NULL) { + dns_zone_t *rootzone = NULL; + (void)dns_view_findzone(view, dns_rootname, &rootzone); + if (rootzone != NULL) { + dns_zone_detach(&rootzone); + need_hints = ISC_FALSE; + } + if (need_hints) + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_WARNING, + "no root hints for view '%s'", + view->name); + } + + /* + * Configure the view's TSIG keys. + */ + ring = NULL; + CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring)); + dns_view_setkeyring(view, ring); + + /* + * Configure the view's peer list. + */ + { + cfg_obj_t *peers = NULL; + cfg_listelt_t *element; + dns_peerlist_t *newpeers = NULL; + + (void)ns_config_get(cfgmaps, "server", &peers); + CHECK(dns_peerlist_new(mctx, &newpeers)); + for (element = cfg_list_first(peers); + element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *cpeer = cfg_listelt_value(element); + dns_peer_t *peer; + + CHECK(configure_peer(cpeer, mctx, &peer)); + dns_peerlist_addpeer(newpeers, peer); + dns_peer_detach(&peer); + } + dns_peerlist_detach(&view->peers); + view->peers = newpeers; /* Transfer ownership. */ + } + + /* + * Configure the views rrset-order. + */ + { + cfg_obj_t *rrsetorder = NULL; + cfg_listelt_t *element; + + (void)ns_config_get(maps, "rrset-order", &rrsetorder); + CHECK(dns_order_create(mctx, &order)); + for (element = cfg_list_first(rrsetorder); + element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *ent = cfg_listelt_value(element); + + CHECK(configure_order(order, ent)); + } + if (view->order != NULL) + dns_order_detach(&view->order); + dns_order_attach(order, &view->order); + dns_order_detach(&order); + } + /* + * Copy the aclenv object. + */ + dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv); + + /* + * Configure the "match-clients" and "match-destinations" ACL. + */ + CHECK(configure_view_acl(vconfig, config, "match-clients", actx, + ns_g_mctx, &view->matchclients)); + CHECK(configure_view_acl(vconfig, config, "match-destinations", actx, + ns_g_mctx, &view->matchdestinations)); + + /* + * Configure the "match-recursive-only" option. + */ + obj = NULL; + (void) ns_config_get(maps, "match-recursive-only", &obj); + if (obj != NULL && cfg_obj_asboolean(obj)) + view->matchrecursiveonly = ISC_TRUE; + else + view->matchrecursiveonly = ISC_FALSE; + + /* + * Configure other configurable data. + */ + obj = NULL; + result = ns_config_get(maps, "recursion", &obj); + INSIST(result == ISC_R_SUCCESS); + view->recursion = cfg_obj_asboolean(obj); + + obj = NULL; + result = ns_config_get(maps, "auth-nxdomain", &obj); + INSIST(result == ISC_R_SUCCESS); + view->auth_nxdomain = cfg_obj_asboolean(obj); + + obj = NULL; + result = ns_config_get(maps, "minimal-responses", &obj); + INSIST(result == ISC_R_SUCCESS); + view->minimalresponses = cfg_obj_asboolean(obj); + + obj = NULL; + result = ns_config_get(maps, "transfer-format", &obj); + INSIST(result == ISC_R_SUCCESS); + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "many-answers") == 0) + view->transfer_format = dns_many_answers; + else if (strcasecmp(str, "one-answer") == 0) + view->transfer_format = dns_one_answer; + else + INSIST(0); + + /* + * Set sources where additional data and CNAME/DNAME + * targets for authoritative answers may be found. + */ + obj = NULL; + result = ns_config_get(maps, "additional-from-auth", &obj); + INSIST(result == ISC_R_SUCCESS); + view->additionalfromauth = cfg_obj_asboolean(obj); + if (view->recursion && ! view->additionalfromauth) { + cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, + "'additional-from-auth no' is only supported " + "with 'recursion no'"); + view->additionalfromauth = ISC_TRUE; + } + + obj = NULL; + result = ns_config_get(maps, "additional-from-cache", &obj); + INSIST(result == ISC_R_SUCCESS); + view->additionalfromcache = cfg_obj_asboolean(obj); + if (view->recursion && ! view->additionalfromcache) { + cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, + "'additional-from-cache no' is only supported " + "with 'recursion no'"); + view->additionalfromcache = ISC_TRUE; + } + + CHECK(configure_view_acl(vconfig, config, "allow-query", + actx, ns_g_mctx, &view->queryacl)); + + if (strcmp(view->name, "_bind") != 0) + CHECK(configure_view_acl(vconfig, config, "allow-recursion", + actx, ns_g_mctx, &view->recursionacl)); + + /* + * Warning if both "recursion no;" and allow-recursion are active + * except for "allow-recursion { none; };". + */ + if (!view->recursion && view->recursionacl != NULL && + (view->recursionacl->length != 1 || + view->recursionacl->elements[0].type != dns_aclelementtype_any || + view->recursionacl->elements[0].negative != ISC_TRUE)) { + const char *forview = " for view "; + const char *viewname = view->name; + + if (!strcmp(view->name, "_bind") || + !strcmp(view->name, "_default")) { + forview = ""; + viewname = ""; + } + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_WARNING, + "both \"recursion no;\" and \"allow-recursion\" " + "active%s%s", forview, viewname); + } + + CHECK(configure_view_acl(vconfig, config, "sortlist", + actx, ns_g_mctx, &view->sortlist)); + + obj = NULL; + result = ns_config_get(maps, "request-ixfr", &obj); + INSIST(result == ISC_R_SUCCESS); + view->requestixfr = cfg_obj_asboolean(obj); + + obj = NULL; + result = ns_config_get(maps, "provide-ixfr", &obj); + INSIST(result == ISC_R_SUCCESS); + view->provideixfr = cfg_obj_asboolean(obj); + + obj = NULL; + result = ns_config_get(maps, "dnssec-enable", &obj); + INSIST(result == ISC_R_SUCCESS); + view->enablednssec = cfg_obj_asboolean(obj); + + obj = NULL; + result = ns_config_get(maps, "dnssec-lookaside", &obj); + if (result == ISC_R_SUCCESS) { + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + const char *str; + isc_buffer_t b; + dns_name_t *dlv; + + obj = cfg_listelt_value(element); +#if 0 + dns_fixedname_t fixed; + dns_name_t *name; + + /* + * When we support multiple dnssec-lookaside + * entries this is how to find the domain to be + * checked. XXXMPA + */ + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + str = cfg_obj_asstring(cfg_tuple_get(obj, + "domain")); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, + ISC_TRUE, NULL)); +#endif + str = cfg_obj_asstring(cfg_tuple_get(obj, + "trust-anchor")); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + dlv = dns_fixedname_name(&view->dlv_fixed); + CHECK(dns_name_fromtext(dlv, &b, dns_rootname, + ISC_TRUE, NULL)); + view->dlv = dns_fixedname_name(&view->dlv_fixed); + } + } else + view->dlv = NULL; + + /* + * For now, there is only one kind of trusted keys, the + * "security roots". + */ + if (view->enablednssec) { + CHECK(configure_view_dnsseckeys(vconfig, config, mctx, + &view->secroots)); + dns_resolver_resetmustbesecure(view->resolver); + obj = NULL; + result = ns_config_get(maps, "dnssec-must-be-secure", &obj); + if (result == ISC_R_SUCCESS) + CHECK(mustbesecure(obj, view->resolver)); + } + + obj = NULL; + result = ns_config_get(maps, "max-cache-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + view->maxcachettl = cfg_obj_asuint32(obj); + + obj = NULL; + result = ns_config_get(maps, "max-ncache-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + view->maxncachettl = cfg_obj_asuint32(obj); + if (view->maxncachettl > 7 * 24 * 3600) + view->maxncachettl = 7 * 24 * 3600; + + obj = NULL; + result = ns_config_get(maps, "preferred-glue", &obj); + if (result == ISC_R_SUCCESS) { + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "a") == 0) + view->preferred_glue = dns_rdatatype_a; + else if (strcasecmp(str, "aaaa") == 0) + view->preferred_glue = dns_rdatatype_aaaa; + else + view->preferred_glue = 0; + } else + view->preferred_glue = 0; + + obj = NULL; + result = ns_config_get(maps, "root-delegation-only", &obj); + if (result == ISC_R_SUCCESS) { + dns_view_setrootdelonly(view, ISC_TRUE); + if (!cfg_obj_isvoid(obj)) { + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t b; + char *str; + cfg_obj_t *exclude; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) { + exclude = cfg_listelt_value(element); + str = cfg_obj_asstring(exclude); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, + ISC_FALSE, NULL)); + CHECK(dns_view_excludedelegationonly(view, + name)); + } + } + } else + dns_view_setrootdelonly(view, ISC_FALSE); + + result = ISC_R_SUCCESS; + + cleanup: + if (dispatch4 != NULL) + dns_dispatch_detach(&dispatch4); + if (dispatch6 != NULL) + dns_dispatch_detach(&dispatch6); + if (order != NULL) + dns_order_detach(&order); + if (cmctx != NULL) + isc_mem_detach(&cmctx); + + if (cache != NULL) + dns_cache_detach(&cache); + + return (result); +} + +static isc_result_t +configure_hints(dns_view_t *view, const char *filename) { + isc_result_t result; + dns_db_t *db; + + db = NULL; + result = dns_rootns_create(view->mctx, view->rdclass, filename, &db); + if (result == ISC_R_SUCCESS) { + dns_view_sethints(view, db); + dns_db_detach(&db); + } + + return (result); +} + +static isc_result_t +configure_alternates(cfg_obj_t *config, dns_view_t *view, + cfg_obj_t *alternates) +{ + cfg_obj_t *portobj; + cfg_obj_t *addresses; + cfg_listelt_t *element; + isc_result_t result = ISC_R_SUCCESS; + in_port_t port; + + /* + * Determine which port to send requests to. + */ + if (ns_g_lwresdonly && ns_g_port != 0) + port = ns_g_port; + else + CHECKM(ns_config_getport(config, &port), "port"); + + if (alternates != NULL) { + portobj = cfg_tuple_get(alternates, "port"); + if (cfg_obj_isuint32(portobj)) { + isc_uint32_t val = cfg_obj_asuint32(portobj); + if (val > ISC_UINT16_MAX) { + cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, + "port '%u' out of range", val); + return (ISC_R_RANGE); + } + port = (in_port_t) val; + } + } + + addresses = NULL; + if (alternates != NULL) + addresses = cfg_tuple_get(alternates, "addresses"); + + for (element = cfg_list_first(addresses); + element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *alternate = cfg_listelt_value(element); + isc_sockaddr_t sa; + + if (!cfg_obj_issockaddr(alternate)) { + dns_fixedname_t fixed; + dns_name_t *name; + char *str = cfg_obj_asstring(cfg_tuple_get(alternate, + "name")); + isc_buffer_t buffer; + in_port_t myport = port; + + isc_buffer_init(&buffer, str, strlen(str)); + isc_buffer_add(&buffer, strlen(str)); + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + CHECK(dns_name_fromtext(name, &buffer, dns_rootname, + ISC_FALSE, NULL)); + + portobj = cfg_tuple_get(alternate, "port"); + if (cfg_obj_isuint32(portobj)) { + isc_uint32_t val = cfg_obj_asuint32(portobj); + if (val > ISC_UINT16_MAX) { + cfg_obj_log(portobj, ns_g_lctx, + ISC_LOG_ERROR, + "port '%u' out of range", + val); + return (ISC_R_RANGE); + } + myport = (in_port_t) val; + } + CHECK(dns_resolver_addalternate(view->resolver, NULL, + name, myport)); + continue; + } + + sa = *cfg_obj_assockaddr(alternate); + if (isc_sockaddr_getport(&sa) == 0) + isc_sockaddr_setport(&sa, port); + CHECK(dns_resolver_addalternate(view->resolver, &sa, + NULL, 0)); + } + + cleanup: + return (result); +} + +static isc_result_t +configure_forward(cfg_obj_t *config, dns_view_t *view, dns_name_t *origin, + cfg_obj_t *forwarders, cfg_obj_t *forwardtype) +{ + cfg_obj_t *portobj; + cfg_obj_t *faddresses; + cfg_listelt_t *element; + dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none; + isc_sockaddrlist_t addresses; + isc_sockaddr_t *sa; + isc_result_t result; + in_port_t port; + + /* + * Determine which port to send forwarded requests to. + */ + if (ns_g_lwresdonly && ns_g_port != 0) + port = ns_g_port; + else + CHECKM(ns_config_getport(config, &port), "port"); + + if (forwarders != NULL) { + portobj = cfg_tuple_get(forwarders, "port"); + if (cfg_obj_isuint32(portobj)) { + isc_uint32_t val = cfg_obj_asuint32(portobj); + if (val > ISC_UINT16_MAX) { + cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, + "port '%u' out of range", val); + return (ISC_R_RANGE); + } + port = (in_port_t) val; + } + } + + faddresses = NULL; + if (forwarders != NULL) + faddresses = cfg_tuple_get(forwarders, "addresses"); + + ISC_LIST_INIT(addresses); + + for (element = cfg_list_first(faddresses); + element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *forwarder = cfg_listelt_value(element); + sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t)); + if (sa == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + *sa = *cfg_obj_assockaddr(forwarder); + if (isc_sockaddr_getport(sa) == 0) + isc_sockaddr_setport(sa, port); + ISC_LINK_INIT(sa, link); + ISC_LIST_APPEND(addresses, sa, link); + } + + if (ISC_LIST_EMPTY(addresses)) { + if (forwardtype != NULL) + cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING, + "no forwarders seen; disabling " + "forwarding"); + fwdpolicy = dns_fwdpolicy_none; + } else { + if (forwardtype == NULL) + fwdpolicy = dns_fwdpolicy_first; + else { + char *forwardstr = cfg_obj_asstring(forwardtype); + if (strcasecmp(forwardstr, "first") == 0) + fwdpolicy = dns_fwdpolicy_first; + else if (strcasecmp(forwardstr, "only") == 0) + fwdpolicy = dns_fwdpolicy_only; + else + INSIST(0); + } + } + + result = dns_fwdtable_add(view->fwdtable, origin, &addresses, + fwdpolicy); + if (result != ISC_R_SUCCESS) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(origin, namebuf, sizeof(namebuf)); + cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING, + "could not set up forwarding for domain '%s': %s", + namebuf, isc_result_totext(result)); + goto cleanup; + } + + result = ISC_R_SUCCESS; + + cleanup: + + while (!ISC_LIST_EMPTY(addresses)) { + sa = ISC_LIST_HEAD(addresses); + ISC_LIST_UNLINK(addresses, sa, link); + isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t)); + } + + return (result); +} + +/* + * Create a new view and add it to the list. + * + * If 'vconfig' is NULL, create the default view. + * + * The view created is attached to '*viewp'. + */ +static isc_result_t +create_view(cfg_obj_t *vconfig, dns_viewlist_t *viewlist, dns_view_t **viewp) { + isc_result_t result; + const char *viewname; + dns_rdataclass_t viewclass; + dns_view_t *view = NULL; + + if (vconfig != NULL) { + cfg_obj_t *classobj = NULL; + + viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); + classobj = cfg_tuple_get(vconfig, "class"); + result = ns_config_getclass(classobj, dns_rdataclass_in, + &viewclass); + } else { + viewname = "_default"; + viewclass = dns_rdataclass_in; + } + result = dns_viewlist_find(viewlist, viewname, viewclass, &view); + if (result == ISC_R_SUCCESS) + return (ISC_R_EXISTS); + if (result != ISC_R_NOTFOUND) + return (result); + INSIST(view == NULL); + + result = dns_view_create(ns_g_mctx, viewclass, viewname, &view); + if (result != ISC_R_SUCCESS) + return (result); + + ISC_LIST_APPEND(*viewlist, view, link); + dns_view_attach(view, viewp); + return (ISC_R_SUCCESS); +} + +/* + * Configure or reconfigure a zone. + */ +static isc_result_t +configure_zone(cfg_obj_t *config, cfg_obj_t *zconfig, cfg_obj_t *vconfig, + isc_mem_t *mctx, dns_view_t *view, + ns_aclconfctx_t *aclconf) +{ + dns_view_t *pview = NULL; /* Production view */ + dns_zone_t *zone = NULL; /* New or reused zone */ + dns_zone_t *dupzone = NULL; + cfg_obj_t *options = NULL; + cfg_obj_t *zoptions = NULL; + cfg_obj_t *typeobj = NULL; + cfg_obj_t *forwarders = NULL; + cfg_obj_t *forwardtype = NULL; + cfg_obj_t *only = NULL; + isc_result_t result; + isc_result_t tresult; + isc_buffer_t buffer; + dns_fixedname_t fixorigin; + dns_name_t *origin; + const char *zname; + dns_rdataclass_t zclass; + const char *ztypestr; + + options = NULL; + (void)cfg_map_get(config, "options", &options); + + zoptions = cfg_tuple_get(zconfig, "options"); + + /* + * Get the zone origin as a dns_name_t. + */ + zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + isc_buffer_init(&buffer, zname, strlen(zname)); + isc_buffer_add(&buffer, strlen(zname)); + dns_fixedname_init(&fixorigin); + CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin), + &buffer, dns_rootname, ISC_FALSE, NULL)); + origin = dns_fixedname_name(&fixorigin); + + CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"), + view->rdclass, &zclass)); + if (zclass != view->rdclass) { + const char *vname = NULL; + if (vconfig != NULL) + vname = cfg_obj_asstring(cfg_tuple_get(vconfig, + "name")); + else + vname = ""; + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "zone '%s': wrong class for view '%s'", + zname, vname); + result = ISC_R_FAILURE; + goto cleanup; + } + + (void)cfg_map_get(zoptions, "type", &typeobj); + if (typeobj == NULL) { + cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR, + "zone '%s' 'type' not specified", zname); + return (ISC_R_FAILURE); + } + ztypestr = cfg_obj_asstring(typeobj); + + /* + * "hints zones" aren't zones. If we've got one, + * configure it and return. + */ + if (strcasecmp(ztypestr, "hint") == 0) { + cfg_obj_t *fileobj = NULL; + if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "zone '%s': 'file' not specified", + zname); + result = ISC_R_FAILURE; + goto cleanup; + } + if (dns_name_equal(origin, dns_rootname)) { + char *hintsfile = cfg_obj_asstring(fileobj); + + result = configure_hints(view, hintsfile); + if (result != ISC_R_SUCCESS) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, + ISC_LOG_ERROR, + "could not configure root hints " + "from '%s': %s", hintsfile, + isc_result_totext(result)); + goto cleanup; + } + /* + * Hint zones may also refer to delegation only points. + */ + only = NULL; + tresult = cfg_map_get(zoptions, "delegation-only", + &only); + if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only)) + CHECK(dns_view_adddelegationonly(view, origin)); + } else { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_WARNING, + "ignoring non-root hint zone '%s'", + zname); + result = ISC_R_SUCCESS; + } + /* Skip ordinary zone processing. */ + goto cleanup; + } + + /* + * "forward zones" aren't zones either. Translate this syntax into + * the appropriate selective forwarding configuration and return. + */ + if (strcasecmp(ztypestr, "forward") == 0) { + forwardtype = NULL; + forwarders = NULL; + + (void)cfg_map_get(zoptions, "forward", &forwardtype); + (void)cfg_map_get(zoptions, "forwarders", &forwarders); + result = configure_forward(config, view, origin, forwarders, + forwardtype); + goto cleanup; + } + + /* + * "delegation-only zones" aren't zones either. + */ + if (strcasecmp(ztypestr, "delegation-only") == 0) { + result = dns_view_adddelegationonly(view, origin); + goto cleanup; + } + + /* + * Check for duplicates in the new zone table. + */ + result = dns_view_findzone(view, origin, &dupzone); + if (result == ISC_R_SUCCESS) { + /* + * We already have this zone! + */ + cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR, + "zone '%s' already exists", zname); + dns_zone_detach(&dupzone); + result = ISC_R_EXISTS; + goto cleanup; + } + INSIST(dupzone == NULL); + + /* + * See if we can reuse an existing zone. This is + * only possible if all of these are true: + * - The zone's view exists + * - A zone with the right name exists in the view + * - The zone is compatible with the config + * options (e.g., an existing master zone cannot + * be reused if the options specify a slave zone) + */ + result = dns_viewlist_find(&ns_g_server->viewlist, + view->name, view->rdclass, + &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) + goto cleanup; + if (pview != NULL) + result = dns_view_findzone(pview, origin, &zone); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) + goto cleanup; + if (zone != NULL) { + if (! ns_zone_reusable(zone, zconfig)) + dns_zone_detach(&zone); + } + + if (zone != NULL) { + /* + * We found a reusable zone. Make it use the + * new view. + */ + dns_zone_setview(zone, view); + } else { + /* + * We cannot reuse an existing zone, we have + * to create a new one. + */ + CHECK(dns_zone_create(&zone, mctx)); + CHECK(dns_zone_setorigin(zone, origin)); + dns_zone_setview(zone, view); + CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); + } + + /* + * If the zone contains a 'forwarders' statement, configure + * selective forwarding. + */ + forwarders = NULL; + if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS) + { + forwardtype = NULL; + (void)cfg_map_get(zoptions, "forward", &forwardtype); + CHECK(configure_forward(config, view, origin, forwarders, + forwardtype)); + } + + /* + * Stub and forward zones may also refer to delegation only points. + */ + only = NULL; + if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS) + { + if (cfg_obj_asboolean(only)) + CHECK(dns_view_adddelegationonly(view, origin)); + } + + /* + * Configure the zone. + */ + CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone)); + + /* + * Add the zone to its view in the new view list. + */ + CHECK(dns_view_addzone(view, zone)); + + cleanup: + if (zone != NULL) + dns_zone_detach(&zone); + if (pview != NULL) + dns_view_detach(&pview); + + return (result); +} + +/* + * Configure a single server quota. + */ +static void +configure_server_quota(cfg_obj_t **maps, const char *name, isc_quota_t *quota) +{ + cfg_obj_t *obj = NULL; + isc_result_t result; + + result = ns_config_get(maps, name, &obj); + INSIST(result == ISC_R_SUCCESS); + isc_quota_max(quota, cfg_obj_asuint32(obj)); +} + +/* + * This function is called as soon as the 'directory' statement has been + * parsed. This can be extended to support other options if necessary. + */ +static isc_result_t +directory_callback(const char *clausename, cfg_obj_t *obj, void *arg) { + isc_result_t result; + char *directory; + + REQUIRE(strcasecmp("directory", clausename) == 0); + + UNUSED(arg); + UNUSED(clausename); + + /* + * Change directory. + */ + directory = cfg_obj_asstring(obj); + + if (! isc_file_ischdiridempotent(directory)) + cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, + "option 'directory' contains relative path '%s'", + directory); + + result = isc_dir_chdir(directory); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, + "change directory to '%s' failed: %s", + directory, isc_result_totext(result)); + return (result); + } + + return (ISC_R_SUCCESS); +} + +static void +scan_interfaces(ns_server_t *server, isc_boolean_t verbose) { + isc_boolean_t match_mapped = server->aclenv.match_mapped; + + ns_interfacemgr_scan(server->interfacemgr, verbose); + /* + * Update the "localhost" and "localnets" ACLs to match the + * current set of network interfaces. + */ + dns_aclenv_copy(&server->aclenv, + ns_interfacemgr_getaclenv(server->interfacemgr)); + + server->aclenv.match_mapped = match_mapped; +} + +static isc_result_t +add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr) { + ns_listenelt_t *lelt = NULL; + dns_acl_t *src_acl = NULL; + dns_aclelement_t aelt; + isc_result_t result; + isc_sockaddr_t any_sa6; + + REQUIRE(isc_sockaddr_pf(addr) == AF_INET6); + + isc_sockaddr_any6(&any_sa6); + if (!isc_sockaddr_equal(&any_sa6, addr)) { + aelt.type = dns_aclelementtype_ipprefix; + aelt.negative = ISC_FALSE; + aelt.u.ip_prefix.prefixlen = 128; + isc_netaddr_fromin6(&aelt.u.ip_prefix.address, + &addr->type.sin6.sin6_addr); + + result = dns_acl_create(mctx, 1, &src_acl); + if (result != ISC_R_SUCCESS) + return (result); + result = dns_acl_appendelement(src_acl, &aelt); + if (result != ISC_R_SUCCESS) + goto clean; + + result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr), + src_acl, &lelt); + if (result != ISC_R_SUCCESS) + goto clean; + ISC_LIST_APPEND(list->elts, lelt, link); + } + + return (ISC_R_SUCCESS); + + clean: + INSIST(lelt == NULL); + if (src_acl != NULL) + dns_acl_detach(&src_acl); + + return (result); +} + +/* + * Make a list of xxx-source addresses and call ns_interfacemgr_adjust() + * to update the listening interfaces accordingly. + * We currently only consider IPv6, because this only affects IPv6 wildcard + * sockets. + */ +static void +adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) { + isc_result_t result; + ns_listenlist_t *list = NULL; + dns_view_t *view; + dns_zone_t *zone, *next; + isc_sockaddr_t addr, *addrp; + + result = ns_listenlist_create(mctx, &list); + if (result != ISC_R_SUCCESS) + return; + + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL; + view = ISC_LIST_NEXT(view, link)) { + dns_dispatch_t *dispatch6; + + dispatch6 = dns_resolver_dispatchv6(view->resolver); + if (dispatch6 == NULL) + continue; + result = dns_dispatch_getlocaladdress(dispatch6, &addr); + if (result != ISC_R_SUCCESS) + goto fail; + result = add_listenelt(mctx, list, &addr); + if (result != ISC_R_SUCCESS) + goto fail; + } + + zone = NULL; + for (result = dns_zone_first(server->zonemgr, &zone); + result == ISC_R_SUCCESS; + next = NULL, result = dns_zone_next(zone, &next), zone = next) { + dns_view_t *zoneview; + + /* + * At this point the zone list may contain a stale zone + * just removed from the configuration. To see the validity, + * check if the corresponding view is in our current view list. + * There may also be old zones that are still in the process + * of shutting down and have detached from their old view + * (zoneview == NULL). + */ + zoneview = dns_zone_getview(zone); + if (zoneview == NULL) + continue; + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL && view != zoneview; + view = ISC_LIST_NEXT(view, link)) + ; + if (view == NULL) + continue; + + addrp = dns_zone_getnotifysrc6(zone); + result = add_listenelt(mctx, list, addrp); + if (result != ISC_R_SUCCESS) + goto fail; + + addrp = dns_zone_getxfrsource6(zone); + result = add_listenelt(mctx, list, addrp); + if (result != ISC_R_SUCCESS) + goto fail; + } + + ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE); + + clean: + ns_listenlist_detach(&list); + return; + + fail: + /* + * Even when we failed the procedure, most of other interfaces + * should work correctly. We therefore just warn it. + */ + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_WARNING, + "could not adjust the listen-on list; " + "some interfaces may not work"); + goto clean; +} + +/* + * This event callback is invoked to do periodic network + * interface scanning. + */ +static void +interface_timer_tick(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + ns_server_t *server = (ns_server_t *) event->ev_arg; + INSIST(task == server->task); + UNUSED(task); + isc_event_free(&event); + /* + * XXX should scan interfaces unlocked and get exclusive access + * only to replace ACLs. + */ + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + scan_interfaces(server, ISC_FALSE); + isc_task_endexclusive(server->task); +} + +static void +heartbeat_timer_tick(isc_task_t *task, isc_event_t *event) { + ns_server_t *server = (ns_server_t *) event->ev_arg; + dns_view_t *view; + + UNUSED(task); + isc_event_free(&event); + view = ISC_LIST_HEAD(server->viewlist); + while (view != NULL) { + dns_view_dialup(view); + view = ISC_LIST_NEXT(view, link); + } +} + +/* + * Replace the current value of '*field', a dynamically allocated + * string or NULL, with a dynamically allocated copy of the + * null-terminated string pointed to by 'value', or NULL. + */ +static isc_result_t +setstring(ns_server_t *server, char **field, const char *value) { + char *copy; + + if (value != NULL) { + copy = isc_mem_strdup(server->mctx, value); + if (copy == NULL) + return (ISC_R_NOMEMORY); + } else { + copy = NULL; + } + + if (*field != NULL) + isc_mem_free(server->mctx, *field); + + *field = copy; + return (ISC_R_SUCCESS); +} + +/* + * Replace the current value of '*field', a dynamically allocated + * string or NULL, with another dynamically allocated string + * or NULL if whether 'obj' is a string or void value, respectively. + */ +static isc_result_t +setoptstring(ns_server_t *server, char **field, cfg_obj_t *obj) { + if (cfg_obj_isvoid(obj)) + return (setstring(server, field, NULL)); + else + return (setstring(server, field, cfg_obj_asstring(obj))); +} + +static void +set_limit(cfg_obj_t **maps, const char *configname, const char *description, + isc_resource_t resourceid, isc_resourcevalue_t defaultvalue) +{ + cfg_obj_t *obj = NULL; + char *resource; + isc_resourcevalue_t value; + isc_result_t result; + + if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS) + return; + + if (cfg_obj_isstring(obj)) { + resource = cfg_obj_asstring(obj); + if (strcasecmp(resource, "unlimited") == 0) + value = ISC_RESOURCE_UNLIMITED; + else { + INSIST(strcasecmp(resource, "default") == 0); + value = defaultvalue; + } + } else + value = cfg_obj_asuint64(obj); + + result = isc_resource_setlimit(resourceid, value); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + result == ISC_R_SUCCESS ? + ISC_LOG_DEBUG(3) : ISC_LOG_WARNING, + "set maximum %s to %" ISC_PRINT_QUADFORMAT "d: %s", + description, value, isc_result_totext(result)); +} + +#define SETLIMIT(cfgvar, resource, description) \ + set_limit(maps, cfgvar, description, isc_resource_ ## resource, \ + ns_g_init ## resource) + +static void +set_limits(cfg_obj_t **maps) { + SETLIMIT("stacksize", stacksize, "stack size"); + SETLIMIT("datasize", datasize, "data size"); + SETLIMIT("coresize", coresize, "core size"); + SETLIMIT("files", openfiles, "open files"); +} + +static isc_result_t +portlist_fromconf(dns_portlist_t *portlist, unsigned int family, + cfg_obj_t *ports) +{ + cfg_listelt_t *element; + isc_result_t result = ISC_R_SUCCESS; + + for (element = cfg_list_first(ports); + element != NULL; + element = cfg_list_next(element)) { + cfg_obj_t *obj = cfg_listelt_value(element); + in_port_t port = (in_port_t)cfg_obj_asuint32(obj); + + result = dns_portlist_add(portlist, family, port); + if (result != ISC_R_SUCCESS) + break; + } + return (result); +} + +static isc_result_t +load_configuration(const char *filename, ns_server_t *server, + isc_boolean_t first_time) +{ + isc_result_t result; + cfg_parser_t *parser = NULL; + cfg_obj_t *config; + cfg_obj_t *options; + cfg_obj_t *views; + cfg_obj_t *obj; + cfg_obj_t *v4ports, *v6ports; + cfg_obj_t *maps[3]; + cfg_obj_t *builtin_views; + cfg_listelt_t *element; + dns_view_t *view = NULL; + dns_view_t *view_next; + dns_viewlist_t viewlist; + dns_viewlist_t tmpviewlist; + ns_aclconfctx_t aclconfctx; + isc_uint32_t interface_interval; + isc_uint32_t heartbeat_interval; + isc_uint32_t udpsize; + in_port_t listen_port; + int i; + + ns_aclconfctx_init(&aclconfctx); + ISC_LIST_INIT(viewlist); + + /* Ensure exclusive access to configuration data. */ + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + /* + * Parse the global default pseudo-config file. + */ + if (first_time) { + CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config)); + RUNTIME_CHECK(cfg_map_get(ns_g_config, "options", + &ns_g_defaults) == + ISC_R_SUCCESS); + } + + /* + * Parse the configuration file using the new config code. + */ + result = ISC_R_FAILURE; + config = NULL; + + /* + * Unless this is lwresd with the -C option, parse the config file. + */ + if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) { + isc_log_write(ns_g_lctx, + NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_INFO, "loading configuration from '%s'", + filename); + CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &parser)); + cfg_parser_setcallback(parser, directory_callback, NULL); + result = cfg_parse_file(parser, filename, &cfg_type_namedconf, + &config); + } + + /* + * If this is lwresd with the -C option, or lwresd with no -C or -c + * option where the above parsing failed, parse resolv.conf. + */ + if (ns_g_lwresdonly && + (lwresd_g_useresolvconf || + (!ns_g_conffileset && result == ISC_R_FILENOTFOUND))) + { + isc_log_write(ns_g_lctx, + NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_INFO, "loading configuration from '%s'", + lwresd_g_resolvconffile); + if (parser != NULL) + cfg_parser_destroy(&parser); + CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &parser)); + result = ns_lwresd_parseeresolvconf(ns_g_mctx, parser, + &config); + } + CHECK(result); + + /* + * Check the validity of the configuration. + */ + CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx)); + + /* + * Fill in the maps array, used for resolving defaults. + */ + i = 0; + options = NULL; + result = cfg_map_get(config, "options", &options); + if (result == ISC_R_SUCCESS) + maps[i++] = options; + maps[i++] = ns_g_defaults; + maps[i++] = NULL; + + /* + * Set process limits, which (usually) needs to be done as root. + */ + set_limits(maps); + + /* + * Configure various server options. + */ + configure_server_quota(maps, "transfers-out", &server->xfroutquota); + configure_server_quota(maps, "tcp-clients", &server->tcpquota); + configure_server_quota(maps, "recursive-clients", + &server->recursionquota); + if (server->recursionquota.max > 1000) + isc_quota_soft(&server->recursionquota, + server->recursionquota.max - 100); + else + isc_quota_soft(&server->recursionquota, 0); + + CHECK(configure_view_acl(NULL, config, "blackhole", &aclconfctx, + ns_g_mctx, &server->blackholeacl)); + if (server->blackholeacl != NULL) + dns_dispatchmgr_setblackhole(ns_g_dispatchmgr, + server->blackholeacl); + + obj = NULL; + result = ns_config_get(maps, "match-mapped-addresses", &obj); + INSIST(result == ISC_R_SUCCESS); + server->aclenv.match_mapped = cfg_obj_asboolean(obj); + + v4ports = NULL; + v6ports = NULL; + (void)ns_config_get(maps, "avoid-v4-udp-ports", &v4ports); + (void)ns_config_get(maps, "avoid-v6-udp-ports", &v6ports); + if (v4ports != NULL || v6ports != NULL) { + dns_portlist_t *portlist = NULL; + result = dns_portlist_create(ns_g_mctx, &portlist); + if (result == ISC_R_SUCCESS && v4ports != NULL) + result = portlist_fromconf(portlist, AF_INET, v4ports); + if (result == ISC_R_SUCCESS && v6ports != NULL) + portlist_fromconf(portlist, AF_INET6, v6ports); + if (result == ISC_R_SUCCESS) + dns_dispatchmgr_setblackportlist(ns_g_dispatchmgr, portlist); + if (portlist != NULL) + dns_portlist_detach(&portlist); + CHECK(result); + } else + dns_dispatchmgr_setblackportlist(ns_g_dispatchmgr, NULL); + + /* + * Set the EDNS UDP size when we don't match a view. + */ + obj = NULL; + result = ns_config_get(maps, "edns-udp-size", &obj); + INSIST(result == ISC_R_SUCCESS); + udpsize = cfg_obj_asuint32(obj); + if (udpsize < 512) + udpsize = 512; + if (udpsize > 4096) + udpsize = 4096; + ns_g_udpsize = (isc_uint16_t)udpsize; + + /* + * Configure the zone manager. + */ + obj = NULL; + result = ns_config_get(maps, "transfers-in", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj)); + + obj = NULL; + result = ns_config_get(maps, "transfers-per-ns", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj)); + + obj = NULL; + result = ns_config_get(maps, "serial-query-rate", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj)); + + /* + * Determine which port to use for listening for incoming connections. + */ + if (ns_g_port != 0) + listen_port = ns_g_port; + else + CHECKM(ns_config_getport(config, &listen_port), "port"); + + /* + * Find the listen queue depth. + */ + obj = NULL; + result = ns_config_get(maps, "tcp-listen-queue", &obj); + INSIST(result == ISC_R_SUCCESS); + ns_g_listen = cfg_obj_asuint32(obj); + if (ns_g_listen < 3) + ns_g_listen = 3; + + /* + * Configure the interface manager according to the "listen-on" + * statement. + */ + { + cfg_obj_t *clistenon = NULL; + ns_listenlist_t *listenon = NULL; + + clistenon = NULL; + /* + * Even though listen-on is present in the default + * configuration, we can't use it here, since it isn't + * used if we're in lwresd mode. This way is easier. + */ + if (options != NULL) + (void)cfg_map_get(options, "listen-on", &clistenon); + if (clistenon != NULL) { + result = ns_listenlist_fromconfig(clistenon, + config, + &aclconfctx, + ns_g_mctx, + &listenon); + } else if (!ns_g_lwresdonly) { + /* + * Not specified, use default. + */ + CHECK(ns_listenlist_default(ns_g_mctx, listen_port, + ISC_TRUE, &listenon)); + } + if (listenon != NULL) { + ns_interfacemgr_setlistenon4(server->interfacemgr, + listenon); + ns_listenlist_detach(&listenon); + } + } + /* + * Ditto for IPv6. + */ + { + cfg_obj_t *clistenon = NULL; + ns_listenlist_t *listenon = NULL; + + if (options != NULL) + (void)cfg_map_get(options, "listen-on-v6", &clistenon); + if (clistenon != NULL) { + result = ns_listenlist_fromconfig(clistenon, + config, + &aclconfctx, + ns_g_mctx, + &listenon); + } else if (!ns_g_lwresdonly) { + /* + * Not specified, use default. + */ + CHECK(ns_listenlist_default(ns_g_mctx, listen_port, + ISC_FALSE, &listenon)); + } + if (listenon != NULL) { + ns_interfacemgr_setlistenon6(server->interfacemgr, + listenon); + ns_listenlist_detach(&listenon); + } + } + + /* + * Rescan the interface list to pick up changes in the + * listen-on option. It's important that we do this before we try + * to configure the query source, since the dispatcher we use might + * be shared with an interface. + */ + scan_interfaces(server, ISC_TRUE); + + /* + * Arrange for further interface scanning to occur periodically + * as specified by the "interface-interval" option. + */ + obj = NULL; + result = ns_config_get(maps, "interface-interval", &obj); + INSIST(result == ISC_R_SUCCESS); + interface_interval = cfg_obj_asuint32(obj) * 60; + if (interface_interval == 0) { + CHECK(isc_timer_reset(server->interface_timer, + isc_timertype_inactive, + NULL, NULL, ISC_TRUE)); + } else if (server->interface_interval != interface_interval) { + isc_interval_t interval; + isc_interval_set(&interval, interface_interval, 0); + CHECK(isc_timer_reset(server->interface_timer, + isc_timertype_ticker, + NULL, &interval, ISC_FALSE)); + } + server->interface_interval = interface_interval; + + /* + * Configure the dialup heartbeat timer. + */ + obj = NULL; + result = ns_config_get(maps, "heartbeat-interval", &obj); + INSIST(result == ISC_R_SUCCESS); + heartbeat_interval = cfg_obj_asuint32(obj) * 60; + if (heartbeat_interval == 0) { + CHECK(isc_timer_reset(server->heartbeat_timer, + isc_timertype_inactive, + NULL, NULL, ISC_TRUE)); + } else if (server->heartbeat_interval != heartbeat_interval) { + isc_interval_t interval; + isc_interval_set(&interval, heartbeat_interval, 0); + CHECK(isc_timer_reset(server->heartbeat_timer, + isc_timertype_ticker, + NULL, &interval, ISC_FALSE)); + } + server->heartbeat_interval = heartbeat_interval; + + /* + * Configure and freeze all explicit views. Explicit + * views that have zones were already created at parsing + * time, but views with no zones must be created here. + */ + views = NULL; + (void)cfg_map_get(config, "view", &views); + for (element = cfg_list_first(views); + element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *vconfig = cfg_listelt_value(element); + view = NULL; + + CHECK(create_view(vconfig, &viewlist, &view)); + INSIST(view != NULL); + CHECK(configure_view(view, config, vconfig, + ns_g_mctx, &aclconfctx, ISC_TRUE)); + dns_view_freeze(view); + dns_view_detach(&view); + } + + /* + * Make sure we have a default view if and only if there + * were no explicit views. + */ + if (views == NULL) { + /* + * No explicit views; there ought to be a default view. + * There may already be one created as a side effect + * of zone statements, or we may have to create one. + * In either case, we need to configure and freeze it. + */ + CHECK(create_view(NULL, &viewlist, &view)); + CHECK(configure_view(view, config, NULL, ns_g_mctx, + &aclconfctx, ISC_TRUE)); + dns_view_freeze(view); + dns_view_detach(&view); + } + + /* + * Create (or recreate) the built-in views. Currently + * there is only one, the _bind view. + */ + builtin_views = NULL; + RUNTIME_CHECK(cfg_map_get(ns_g_config, "view", + &builtin_views) == ISC_R_SUCCESS); + for (element = cfg_list_first(builtin_views); + element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *vconfig = cfg_listelt_value(element); + CHECK(create_view(vconfig, &viewlist, &view)); + CHECK(configure_view(view, config, vconfig, ns_g_mctx, + &aclconfctx, ISC_FALSE)); + dns_view_freeze(view); + dns_view_detach(&view); + view = NULL; + } + + /* + * Swap our new view list with the production one. + */ + tmpviewlist = server->viewlist; + server->viewlist = viewlist; + viewlist = tmpviewlist; + + /* + * Load the TKEY information from the configuration. + */ + if (options != NULL) { + dns_tkeyctx_t *t = NULL; + CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy, + &t), + "configuring TKEY"); + if (server->tkeyctx != NULL) + dns_tkeyctx_destroy(&server->tkeyctx); + server->tkeyctx = t; + } + + /* + * Bind the control port(s). + */ + CHECKM(ns_controls_configure(ns_g_server->controls, config, + &aclconfctx), + "binding control channel(s)"); + + /* + * Bind the lwresd port(s). + */ + CHECKM(ns_lwresd_configure(ns_g_mctx, config), + "binding lightweight resolver ports"); + + /* + * Open the source of entropy. + */ + if (first_time) { + obj = NULL; + result = ns_config_get(maps, "random-device", &obj); + if (result != ISC_R_SUCCESS) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_INFO, + "no source of entropy found"); + } else { + const char *randomdev = cfg_obj_asstring(obj); + result = isc_entropy_createfilesource(ns_g_entropy, + randomdev); + if (result != ISC_R_SUCCESS) + isc_log_write(ns_g_lctx, + NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, + ISC_LOG_INFO, + "could not open entropy source " + "%s: %s", + randomdev, + isc_result_totext(result)); +#ifdef PATH_RANDOMDEV + if (ns_g_fallbackentropy != NULL) { + if (result != ISC_R_SUCCESS) { + isc_log_write(ns_g_lctx, + NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, + ISC_LOG_INFO, + "using pre-chroot entropy source " + "%s", + PATH_RANDOMDEV); + isc_entropy_detach(&ns_g_entropy); + isc_entropy_attach(ns_g_fallbackentropy, + &ns_g_entropy); + } + isc_entropy_detach(&ns_g_fallbackentropy); + } +#endif + } + } + + /* + * Relinquish root privileges. + */ + if (first_time) + ns_os_changeuser(); + + /* + * Configure the logging system. + * + * Do this after changing UID to make sure that any log + * files specified in named.conf get created by the + * unprivileged user, not root. + */ + if (ns_g_logstderr) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_INFO, + "ignoring config file logging " + "statement due to -g option"); + } else { + cfg_obj_t *logobj = NULL; + isc_logconfig_t *logc = NULL; + + CHECKM(isc_logconfig_create(ns_g_lctx, &logc), + "creating new logging configuration"); + + logobj = NULL; + (void)cfg_map_get(config, "logging", &logobj); + if (logobj != NULL) { + CHECKM(ns_log_configure(logc, logobj), + "configuring logging"); + } else { + CHECKM(ns_log_setdefaultchannels(logc), + "setting up default logging channels"); + CHECKM(ns_log_setunmatchedcategory(logc), + "setting up default 'category unmatched'"); + CHECKM(ns_log_setdefaultcategory(logc), + "setting up default 'category default'"); + } + + result = isc_logconfig_use(ns_g_lctx, logc); + if (result != ISC_R_SUCCESS) { + isc_logconfig_destroy(&logc); + CHECKM(result, "installing logging configuration"); + } + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), + "now using logging configuration from " + "config file"); + } + + /* + * Set the default value of the query logging flag depending + * whether a "queries" category has been defined. This is + * a disgusting hack, but we need to do this for BIND 8 + * compatibility. + */ + if (first_time) { + cfg_obj_t *logobj = NULL; + cfg_obj_t *categories = NULL; + + obj = NULL; + if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) { + server->log_queries = cfg_obj_asboolean(obj); + } else { + + (void)cfg_map_get(config, "logging", &logobj); + if (logobj != NULL) + (void)cfg_map_get(logobj, "category", + &categories); + if (categories != NULL) { + cfg_listelt_t *element; + for (element = cfg_list_first(categories); + element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *catobj; + char *str; + + obj = cfg_listelt_value(element); + catobj = cfg_tuple_get(obj, "name"); + str = cfg_obj_asstring(catobj); + if (strcasecmp(str, "queries") == 0) + server->log_queries = ISC_TRUE; + } + } + } + } + + obj = NULL; + if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS) + if (cfg_obj_isvoid(obj)) + ns_os_writepidfile(NULL, first_time); + else + ns_os_writepidfile(cfg_obj_asstring(obj), first_time); + else if (ns_g_lwresdonly) + ns_os_writepidfile(lwresd_g_defaultpidfile, first_time); + else + ns_os_writepidfile(ns_g_defaultpidfile, first_time); + + obj = NULL; + if (options != NULL && + cfg_map_get(options, "memstatistics-file", &obj) == ISC_R_SUCCESS) + ns_main_setmemstats(cfg_obj_asstring(obj)); + else + ns_main_setmemstats(NULL); + + obj = NULL; + result = ns_config_get(maps, "statistics-file", &obj); + INSIST(result == ISC_R_SUCCESS); + CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)), + "strdup"); + + obj = NULL; + result = ns_config_get(maps, "dump-file", &obj); + INSIST(result == ISC_R_SUCCESS); + CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)), + "strdup"); + + obj = NULL; + result = ns_config_get(maps, "recursing-file", &obj); + INSIST(result == ISC_R_SUCCESS); + CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)), + "strdup"); + + obj = NULL; + result = ns_config_get(maps, "version", &obj); + if (result == ISC_R_SUCCESS) { + CHECKM(setoptstring(server, &server->version, obj), "strdup"); + server->version_set = ISC_TRUE; + } else { + server->version_set = ISC_FALSE; + } + + obj = NULL; + result = ns_config_get(maps, "hostname", &obj); + if (result == ISC_R_SUCCESS) { + CHECKM(setoptstring(server, &server->hostname, obj), "strdup"); + server->hostname_set = ISC_TRUE; + } else { + server->hostname_set = ISC_FALSE; + } + + obj = NULL; + result = ns_config_get(maps, "server-id", &obj); + server->server_usehostname = ISC_FALSE; + if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) { + server->server_usehostname = ISC_TRUE; + } else if (result == ISC_R_SUCCESS) { + CHECKM(setoptstring(server, &server->server_id, obj), "strdup"); + } else { + result = setoptstring(server, &server->server_id, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } + + obj = NULL; + result = ns_config_get(maps, "flush-zones-on-shutdown", &obj); + if (result == ISC_R_SUCCESS) { + server->flushonshutdown = cfg_obj_asboolean(obj); + } else { + server->flushonshutdown = ISC_FALSE; + } + + result = ISC_R_SUCCESS; + + cleanup: + ns_aclconfctx_destroy(&aclconfctx); + + if (parser != NULL) { + if (config != NULL) + cfg_obj_destroy(parser, &config); + cfg_parser_destroy(&parser); + } + + if (view != NULL) + dns_view_detach(&view); + + /* + * This cleans up either the old production view list + * or our temporary list depending on whether they + * were swapped above or not. + */ + for (view = ISC_LIST_HEAD(viewlist); + view != NULL; + view = view_next) { + view_next = ISC_LIST_NEXT(view, link); + ISC_LIST_UNLINK(viewlist, view, link); + dns_view_detach(&view); + + } + + /* + * Adjust the listening interfaces in accordance with the source + * addresses specified in views and zones. + */ + if (isc_net_probeipv6() == ISC_R_SUCCESS) + adjust_interfaces(server, ns_g_mctx); + + /* Relinquish exclusive access to configuration data. */ + isc_task_endexclusive(server->task); + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_DEBUG(1), "load_configuration: %s", + isc_result_totext(result)); + + return (result); +} + +static isc_result_t +load_zones(ns_server_t *server, isc_boolean_t stop) { + isc_result_t result; + dns_view_t *view; + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + /* + * Load zone data from disk. + */ + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + CHECK(dns_view_load(view, stop)); + } + + /* + * Force zone maintenance. Do this after loading + * so that we know when we need to force AXFR of + * slave zones whose master files are missing. + */ + CHECK(dns_zonemgr_forcemaint(server->zonemgr)); + cleanup: + isc_task_endexclusive(server->task); + return (result); +} + +static isc_result_t +load_new_zones(ns_server_t *server, isc_boolean_t stop) { + isc_result_t result; + dns_view_t *view; + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + /* + * Load zone data from disk. + */ + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + CHECK(dns_view_loadnew(view, stop)); + } + /* + * Force zone maintenance. Do this after loading + * so that we know when we need to force AXFR of + * slave zones whose master files are missing. + */ + dns_zonemgr_resumexfrs(server->zonemgr); + cleanup: + isc_task_endexclusive(server->task); + return (result); +} + +static void +run_server(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + ns_server_t *server = (ns_server_t *)event->ev_arg; + + INSIST(task == server->task); + + isc_event_free(&event); + + CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy, + &ns_g_dispatchmgr), + "creating dispatch manager"); + + CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr, + ns_g_socketmgr, ns_g_dispatchmgr, + &server->interfacemgr), + "creating interface manager"); + + CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive, + NULL, NULL, server->task, + interface_timer_tick, + server, &server->interface_timer), + "creating interface timer"); + + CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive, + NULL, NULL, server->task, + heartbeat_timer_tick, + server, &server->heartbeat_timer), + "creating heartbeat timer"); + + CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser), + "creating default configuration parser"); + + if (ns_g_lwresdonly) + CHECKFATAL(load_configuration(lwresd_g_conffile, server, + ISC_TRUE), + "loading configuration"); + else + CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE), + "loading configuration"); + + isc_hash_init(); + + CHECKFATAL(load_zones(server, ISC_FALSE), "loading zones"); + + ns_os_started(); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_NOTICE, "running"); +} + +void +ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) { + + REQUIRE(NS_SERVER_VALID(server)); + + server->flushonshutdown = flush; +} + +static void +shutdown_server(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_view_t *view, *view_next; + ns_server_t *server = (ns_server_t *)event->ev_arg; + isc_boolean_t flush = server->flushonshutdown; + + UNUSED(task); + INSIST(task == server->task); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_INFO, "shutting down%s", + flush ? ": flushing changes" : ""); + + ns_controls_shutdown(server->controls); + end_reserved_dispatches(server, ISC_TRUE); + + cfg_obj_destroy(ns_g_parser, &ns_g_config); + cfg_parser_destroy(&ns_g_parser); + + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL; + view = view_next) { + view_next = ISC_LIST_NEXT(view, link); + ISC_LIST_UNLINK(server->viewlist, view, link); + if (flush) + dns_view_flushanddetach(&view); + else + dns_view_detach(&view); + } + + isc_timer_detach(&server->interface_timer); + isc_timer_detach(&server->heartbeat_timer); + + ns_interfacemgr_shutdown(server->interfacemgr); + ns_interfacemgr_detach(&server->interfacemgr); + + dns_dispatchmgr_destroy(&ns_g_dispatchmgr); + + dns_zonemgr_shutdown(server->zonemgr); + + if (server->blackholeacl != NULL) + dns_acl_detach(&server->blackholeacl); + + dns_db_detach(&server->in_roothints); + + isc_task_endexclusive(server->task); + + isc_task_detach(&server->task); + + isc_event_free(&event); +} + +void +ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { + isc_result_t result; + + ns_server_t *server = isc_mem_get(mctx, sizeof(*server)); + if (server == NULL) + fatal("allocating server object", ISC_R_NOMEMORY); + + server->mctx = mctx; + server->task = NULL; + + /* Initialize configuration data with default values. */ + + result = isc_quota_init(&server->xfroutquota, 10); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + result = isc_quota_init(&server->tcpquota, 10); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + result = isc_quota_init(&server->recursionquota, 100); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + result = dns_aclenv_init(mctx, &server->aclenv); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + /* Initialize server data structures. */ + server->zonemgr = NULL; + server->interfacemgr = NULL; + ISC_LIST_INIT(server->viewlist); + server->in_roothints = NULL; + server->blackholeacl = NULL; + + CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL, + &server->in_roothints), + "setting up root hints"); + + CHECKFATAL(isc_mutex_init(&server->reload_event_lock), + "initializing reload event lock"); + server->reload_event = + isc_event_allocate(ns_g_mctx, server, + NS_EVENT_RELOAD, + ns_server_reload, + server, + sizeof(isc_event_t)); + CHECKFATAL(server->reload_event == NULL ? + ISC_R_NOMEMORY : ISC_R_SUCCESS, + "allocating reload event"); + + CHECKFATAL(dst_lib_init(ns_g_mctx, ns_g_entropy, ISC_ENTROPY_GOODONLY), + "initializing DST"); + + server->tkeyctx = NULL; + CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy, + &server->tkeyctx), + "creating TKEY context"); + + /* + * Setup the server task, which is responsible for coordinating + * startup and shutdown of the server. + */ + CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task), + "creating server task"); + isc_task_setname(server->task, "server", server); + CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server), + "isc_task_onshutdown"); + CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server), + "isc_app_onrun"); + + server->interface_timer = NULL; + server->heartbeat_timer = NULL; + + server->interface_interval = 0; + server->heartbeat_interval = 0; + + CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr, + ns_g_socketmgr, &server->zonemgr), + "dns_zonemgr_create"); + + server->statsfile = isc_mem_strdup(server->mctx, "named.stats"); + CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, + "isc_mem_strdup"); + server->querystats = NULL; + + server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db"); + CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, + "isc_mem_strdup"); + + server->recfile = isc_mem_strdup(server->mctx, "named.recursing"); + CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, + "isc_mem_strdup"); + + server->hostname_set = ISC_FALSE; + server->hostname = NULL; + server->version_set = ISC_FALSE; + server->version = NULL; + server->server_usehostname = ISC_FALSE; + server->server_id = NULL; + + CHECKFATAL(dns_stats_alloccounters(ns_g_mctx, &server->querystats), + "dns_stats_alloccounters"); + + server->flushonshutdown = ISC_FALSE; + server->log_queries = ISC_FALSE; + + server->controls = NULL; + CHECKFATAL(ns_controls_create(server, &server->controls), + "ns_controls_create"); + server->dispatchgen = 0; + ISC_LIST_INIT(server->dispatches); + + server->magic = NS_SERVER_MAGIC; + *serverp = server; +} + +void +ns_server_destroy(ns_server_t **serverp) { + ns_server_t *server = *serverp; + REQUIRE(NS_SERVER_VALID(server)); + + ns_controls_destroy(&server->controls); + + dns_stats_freecounters(server->mctx, &server->querystats); + + isc_mem_free(server->mctx, server->statsfile); + isc_mem_free(server->mctx, server->dumpfile); + isc_mem_free(server->mctx, server->recfile); + + if (server->version != NULL) + isc_mem_free(server->mctx, server->version); + if (server->hostname != NULL) + isc_mem_free(server->mctx, server->hostname); + if (server->server_id != NULL) + isc_mem_free(server->mctx, server->server_id); + + dns_zonemgr_detach(&server->zonemgr); + + if (server->tkeyctx != NULL) + dns_tkeyctx_destroy(&server->tkeyctx); + + dst_lib_destroy(); + + isc_event_free(&server->reload_event); + + INSIST(ISC_LIST_EMPTY(server->viewlist)); + + dns_aclenv_destroy(&server->aclenv); + + isc_quota_destroy(&server->recursionquota); + isc_quota_destroy(&server->tcpquota); + isc_quota_destroy(&server->xfroutquota); + + server->magic = 0; + isc_mem_put(server->mctx, server, sizeof(*server)); + *serverp = NULL; +} + +static void +fatal(const char *msg, isc_result_t result) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_CRITICAL, "%s: %s", msg, + isc_result_totext(result)); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_CRITICAL, "exiting (due to fatal error)"); + exit(1); +} + +static void +start_reserved_dispatches(ns_server_t *server) { + + REQUIRE(NS_SERVER_VALID(server)); + + server->dispatchgen++; +} + +static void +end_reserved_dispatches(ns_server_t *server, isc_boolean_t all) { + ns_dispatch_t *dispatch, *nextdispatch; + + REQUIRE(NS_SERVER_VALID(server)); + + for (dispatch = ISC_LIST_HEAD(server->dispatches); + dispatch != NULL; + dispatch = nextdispatch) { + nextdispatch = ISC_LIST_NEXT(dispatch, link); + if (!all && server->dispatchgen == dispatch-> dispatchgen) + continue; + ISC_LIST_UNLINK(server->dispatches, dispatch, link); + dns_dispatch_detach(&dispatch->dispatch); + isc_mem_put(server->mctx, dispatch, sizeof(*dispatch)); + } +} + +void +ns_add_reserved_dispatch(ns_server_t *server, isc_sockaddr_t *addr) { + ns_dispatch_t *dispatch; + in_port_t port; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_result_t result; + unsigned int attrs, attrmask; + + REQUIRE(NS_SERVER_VALID(server)); + + port = isc_sockaddr_getport(addr); + if (port == 0 || port >= 1024) + return; + + for (dispatch = ISC_LIST_HEAD(server->dispatches); + dispatch != NULL; + dispatch = ISC_LIST_NEXT(dispatch, link)) { + if (isc_sockaddr_equal(&dispatch->addr, addr)) + break; + } + if (dispatch != NULL) { + dispatch->dispatchgen = server->dispatchgen; + return; + } + + dispatch = isc_mem_get(server->mctx, sizeof(*dispatch)); + if (dispatch == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + dispatch->addr = *addr; + dispatch->dispatchgen = server->dispatchgen; + dispatch->dispatch = NULL; + + attrs = 0; + attrs |= DNS_DISPATCHATTR_UDP; + switch (isc_sockaddr_pf(addr)) { + case AF_INET: + attrs |= DNS_DISPATCHATTR_IPV4; + break; + case AF_INET6: + attrs |= DNS_DISPATCHATTR_IPV6; + break; + default: + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + attrmask = 0; + attrmask |= DNS_DISPATCHATTR_UDP; + attrmask |= DNS_DISPATCHATTR_TCP; + attrmask |= DNS_DISPATCHATTR_IPV4; + attrmask |= DNS_DISPATCHATTR_IPV6; + + result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr, + ns_g_taskmgr, &dispatch->addr, 4096, + 1000, 32768, 16411, 16433, + attrs, attrmask, &dispatch->dispatch); + if (result != ISC_R_SUCCESS) + goto cleanup; + + ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link); + + return; + + cleanup: + if (dispatch != NULL) + isc_mem_put(server->mctx, dispatch, sizeof(*dispatch)); + isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_WARNING, + "unable to create dispatch for reserved port %s: %s", + addrbuf, isc_result_totext(result)); +} + + +static isc_result_t +loadconfig(ns_server_t *server) { + isc_result_t result; + start_reserved_dispatches(server); + result = load_configuration(ns_g_lwresdonly ? + lwresd_g_conffile : ns_g_conffile, + server, ISC_FALSE); + if (result == ISC_R_SUCCESS) + end_reserved_dispatches(server, ISC_FALSE); + else + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "reloading configuration failed: %s", + isc_result_totext(result)); + return (result); +} + +static isc_result_t +reload(ns_server_t *server) { + isc_result_t result; + CHECK(loadconfig(server)); + + result = load_zones(server, ISC_FALSE); + if (result != ISC_R_SUCCESS) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "reloading zones failed: %s", + isc_result_totext(result)); + } + cleanup: + return (result); +} + +static void +reconfig(ns_server_t *server) { + isc_result_t result; + CHECK(loadconfig(server)); + + result = load_new_zones(server, ISC_FALSE); + if (result != ISC_R_SUCCESS) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "loading new zones failed: %s", + isc_result_totext(result)); + } + cleanup: ; +} + +/* + * Handle a reload event (from SIGHUP). + */ +static void +ns_server_reload(isc_task_t *task, isc_event_t *event) { + ns_server_t *server = (ns_server_t *)event->ev_arg; + + INSIST(task = server->task); + UNUSED(task); + + (void)reload(server); + + LOCK(&server->reload_event_lock); + INSIST(server->reload_event == NULL); + server->reload_event = event; + UNLOCK(&server->reload_event_lock); +} + +void +ns_server_reloadwanted(ns_server_t *server) { + LOCK(&server->reload_event_lock); + if (server->reload_event != NULL) + isc_task_send(server->task, &server->reload_event); + UNLOCK(&server->reload_event_lock); +} + +static char * +next_token(char **stringp, const char *delim) { + char *res; + + do { + res = strsep(stringp, delim); + if (res == NULL) + break; + } while (*res == '\0'); + return (res); +} + +/* + * Find the zone specified in the control channel command 'args', + * if any. If a zone is specified, point '*zonep' at it, otherwise + * set '*zonep' to NULL. + */ +static isc_result_t +zone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep) { + char *input, *ptr; + const char *zonetxt; + char *classtxt; + const char *viewtxt = NULL; + dns_fixedname_t name; + isc_result_t result; + isc_buffer_t buf; + dns_view_t *view = NULL; + dns_rdataclass_t rdclass; + + REQUIRE(zonep != NULL && *zonep == NULL); + + input = args; + + /* Skip the command name. */ + ptr = next_token(&input, " \t"); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + + /* Look for the zone name. */ + zonetxt = next_token(&input, " \t"); + if (zonetxt == NULL) + return (ISC_R_SUCCESS); + + /* Look for the optional class name. */ + classtxt = next_token(&input, " \t"); + if (classtxt != NULL) { + /* Look for the optional view name. */ + viewtxt = next_token(&input, " \t"); + } + + isc_buffer_init(&buf, zonetxt, strlen(zonetxt)); + isc_buffer_add(&buf, strlen(zonetxt)); + dns_fixedname_init(&name); + result = dns_name_fromtext(dns_fixedname_name(&name), + &buf, dns_rootname, ISC_FALSE, NULL); + if (result != ISC_R_SUCCESS) + goto fail1; + + if (classtxt != NULL) { + isc_textregion_t r; + r.base = classtxt; + r.length = strlen(classtxt); + result = dns_rdataclass_fromtext(&rdclass, &r); + if (result != ISC_R_SUCCESS) + goto fail1; + } else { + rdclass = dns_rdataclass_in; + } + + if (viewtxt == NULL) + viewtxt = "_default"; + result = dns_viewlist_find(&server->viewlist, viewtxt, + rdclass, &view); + if (result != ISC_R_SUCCESS) + goto fail1; + + result = dns_zt_find(view->zonetable, dns_fixedname_name(&name), + 0, NULL, zonep); + /* Partial match? */ + if (result != ISC_R_SUCCESS && *zonep != NULL) + dns_zone_detach(zonep); + dns_view_detach(&view); + fail1: + return (result); +} + +/* + * Act on a "retransfer" command from the command channel. + */ +isc_result_t +ns_server_retransfercommand(ns_server_t *server, char *args) { + isc_result_t result; + dns_zone_t *zone = NULL; + dns_zonetype_t type; + + result = zone_from_args(server, args, &zone); + if (result != ISC_R_SUCCESS) + return (result); + if (zone == NULL) + return (ISC_R_UNEXPECTEDEND); + type = dns_zone_gettype(zone); + if (type == dns_zone_slave || type == dns_zone_stub) + dns_zone_forcereload(zone); + else + result = ISC_R_NOTFOUND; + dns_zone_detach(&zone); + return (result); +} + +/* + * Act on a "reload" command from the command channel. + */ +isc_result_t +ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) { + isc_result_t result; + dns_zone_t *zone = NULL; + dns_zonetype_t type; + const char *msg = NULL; + + result = zone_from_args(server, args, &zone); + if (result != ISC_R_SUCCESS) + return (result); + if (zone == NULL) { + result = reload(server); + if (result == ISC_R_SUCCESS) + msg = "server reload successful"; + } else { + type = dns_zone_gettype(zone); + if (type == dns_zone_slave || type == dns_zone_stub) { + dns_zone_refresh(zone); + msg = "zone refresh queued"; + } else { + result = dns_zone_load(zone); + dns_zone_detach(&zone); + switch (result) { + case ISC_R_SUCCESS: + msg = "zone reload successful"; + break; + case DNS_R_CONTINUE: + msg = "zone reload queued"; + result = ISC_R_SUCCESS; + break; + case DNS_R_UPTODATE: + msg = "zone reload up-to-date"; + result = ISC_R_SUCCESS; + break; + default: + /* failure message will be generated by rndc */ + break; + } + } + } + if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) + isc_buffer_putmem(text, (const unsigned char *)msg, + strlen(msg) + 1); + return (result); +} + +/* + * Act on a "reconfig" command from the command channel. + */ +isc_result_t +ns_server_reconfigcommand(ns_server_t *server, char *args) { + UNUSED(args); + + reconfig(server); + return (ISC_R_SUCCESS); +} + +/* + * Act on a "refresh" command from the command channel. + */ +isc_result_t +ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) { + isc_result_t result; + dns_zone_t *zone = NULL; + const unsigned char msg[] = "zone refresh queued"; + + result = zone_from_args(server, args, &zone); + if (result != ISC_R_SUCCESS) + return (result); + if (zone == NULL) + return (ISC_R_UNEXPECTEDEND); + + dns_zone_refresh(zone); + dns_zone_detach(&zone); + if (sizeof(msg) <= isc_buffer_availablelength(text)) + isc_buffer_putmem(text, msg, sizeof(msg)); + + return (ISC_R_SUCCESS); +} + +isc_result_t +ns_server_togglequerylog(ns_server_t *server) { + server->log_queries = server->log_queries ? ISC_FALSE : ISC_TRUE; + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_INFO, + "query logging is now %s", + server->log_queries ? "on" : "off"); + return (ISC_R_SUCCESS); +} + +static isc_result_t +ns_listenlist_fromconfig(cfg_obj_t *listenlist, cfg_obj_t *config, + ns_aclconfctx_t *actx, + isc_mem_t *mctx, ns_listenlist_t **target) +{ + isc_result_t result; + cfg_listelt_t *element; + ns_listenlist_t *dlist = NULL; + + REQUIRE(target != NULL && *target == NULL); + + result = ns_listenlist_create(mctx, &dlist); + if (result != ISC_R_SUCCESS) + return (result); + + for (element = cfg_list_first(listenlist); + element != NULL; + element = cfg_list_next(element)) + { + ns_listenelt_t *delt = NULL; + cfg_obj_t *listener = cfg_listelt_value(element); + result = ns_listenelt_fromconfig(listener, config, actx, + mctx, &delt); + if (result != ISC_R_SUCCESS) + goto cleanup; + ISC_LIST_APPEND(dlist->elts, delt, link); + } + *target = dlist; + return (ISC_R_SUCCESS); + + cleanup: + ns_listenlist_detach(&dlist); + return (result); +} + +/* + * Create a listen list from the corresponding configuration + * data structure. + */ +static isc_result_t +ns_listenelt_fromconfig(cfg_obj_t *listener, cfg_obj_t *config, + ns_aclconfctx_t *actx, + isc_mem_t *mctx, ns_listenelt_t **target) +{ + isc_result_t result; + cfg_obj_t *portobj; + in_port_t port; + ns_listenelt_t *delt = NULL; + REQUIRE(target != NULL && *target == NULL); + + portobj = cfg_tuple_get(listener, "port"); + if (!cfg_obj_isuint32(portobj)) { + if (ns_g_port != 0) { + port = ns_g_port; + } else { + result = ns_config_getport(config, &port); + if (result != ISC_R_SUCCESS) + return (result); + } + } else { + if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) { + cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, + "port value '%u' is out of range", + cfg_obj_asuint32(portobj)); + return (ISC_R_RANGE); + } + port = (in_port_t)cfg_obj_asuint32(portobj); + } + + result = ns_listenelt_create(mctx, port, NULL, &delt); + if (result != ISC_R_SUCCESS) + return (result); + + result = ns_acl_fromconfig(cfg_tuple_get(listener, "acl"), + config, actx, mctx, &delt->acl); + if (result != ISC_R_SUCCESS) { + ns_listenelt_destroy(delt); + return (result); + } + *target = delt; + return (ISC_R_SUCCESS); +} + +isc_result_t +ns_server_dumpstats(ns_server_t *server) { + isc_result_t result; + dns_zone_t *zone, *next; + isc_stdtime_t now; + FILE *fp = NULL; + int i; + int ncounters; + + isc_stdtime_get(&now); + + CHECKMF(isc_stdio_open(server->statsfile, "a", &fp), + "could not open statistics dump file", server->statsfile); + + ncounters = DNS_STATS_NCOUNTERS; + fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now); + + for (i = 0; i < ncounters; i++) + fprintf(fp, "%s %" ISC_PRINT_QUADFORMAT "u\n", + dns_statscounter_names[i], + server->querystats[i]); + + zone = NULL; + for (result = dns_zone_first(server->zonemgr, &zone); + result == ISC_R_SUCCESS; + next = NULL, result = dns_zone_next(zone, &next), zone = next) + { + isc_uint64_t *zonestats = dns_zone_getstatscounters(zone); + if (zonestats != NULL) { + char zonename[DNS_NAME_FORMATSIZE]; + dns_view_t *view; + char *viewname; + + dns_name_format(dns_zone_getorigin(zone), + zonename, sizeof(zonename)); + view = dns_zone_getview(zone); + viewname = view->name; + for (i = 0; i < ncounters; i++) { + fprintf(fp, "%s %" ISC_PRINT_QUADFORMAT + "u %s", + dns_statscounter_names[i], + zonestats[i], + zonename); + if (strcmp(viewname, "_default") != 0) + fprintf(fp, " %s", viewname); + fprintf(fp, "\n"); + } + } + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + CHECK(result); + + fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now); + + cleanup: + if (fp != NULL) + (void)isc_stdio_close(fp); + return (result); +} + +static isc_result_t +add_zone_tolist(dns_zone_t *zone, void *uap) { + struct dumpcontext *dctx = uap; + struct zonelistentry *zle; + + zle = isc_mem_get(dctx->mctx, sizeof *zle); + if (zle == NULL) + return (ISC_R_NOMEMORY); + zle->zone = NULL; + dns_zone_attach(zone, &zle->zone); + ISC_LINK_INIT(zle, link); + ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link); + return (ISC_R_SUCCESS); +} + +static isc_result_t +add_view_tolist(struct dumpcontext *dctx, dns_view_t *view) { + struct viewlistentry *vle; + isc_result_t result = ISC_R_SUCCESS; + + /* + * Prevent duplicate views. + */ + for (vle = ISC_LIST_HEAD(dctx->viewlist); + vle != NULL; + vle = ISC_LIST_NEXT(vle, link)) + if (vle->view == view) + return (ISC_R_SUCCESS); + + vle = isc_mem_get(dctx->mctx, sizeof *vle); + if (vle == NULL) + return (ISC_R_NOMEMORY); + vle->view = NULL; + dns_view_attach(view, &vle->view); + ISC_LINK_INIT(vle, link); + ISC_LIST_INIT(vle->zonelist); + ISC_LIST_APPEND(dctx->viewlist, vle, link); + if (dctx->dumpzones) + result = dns_zt_apply(view->zonetable, ISC_TRUE, + add_zone_tolist, dctx); + return (result); +} + +static void +dumpcontext_destroy(struct dumpcontext *dctx) { + struct viewlistentry *vle; + struct zonelistentry *zle; + + vle = ISC_LIST_HEAD(dctx->viewlist); + while (vle != NULL) { + ISC_LIST_UNLINK(dctx->viewlist, vle, link); + zle = ISC_LIST_HEAD(vle->zonelist); + while (zle != NULL) { + ISC_LIST_UNLINK(vle->zonelist, zle, link); + dns_zone_detach(&zle->zone); + isc_mem_put(dctx->mctx, zle, sizeof *zle); + zle = ISC_LIST_HEAD(vle->zonelist); + } + dns_view_detach(&vle->view); + isc_mem_put(dctx->mctx, vle, sizeof *vle); + vle = ISC_LIST_HEAD(dctx->viewlist); + } + if (dctx->version != NULL) + dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE); + if (dctx->db != NULL) + dns_db_detach(&dctx->db); + if (dctx->cache != NULL) + dns_db_detach(&dctx->cache); + if (dctx->task != NULL) + isc_task_detach(&dctx->task); + if (dctx->fp != NULL) + (void)isc_stdio_close(dctx->fp); + if (dctx->mdctx != NULL) + dns_dumpctx_detach(&dctx->mdctx); + isc_mem_put(dctx->mctx, dctx, sizeof *dctx); +} + +static void +dumpdone(void *arg, isc_result_t result) { + struct dumpcontext *dctx = arg; + char buf[1024+32]; + const dns_master_style_t *style; + + if (result != ISC_R_SUCCESS) + goto cleanup; + if (dctx->mdctx != NULL) + dns_dumpctx_detach(&dctx->mdctx); + if (dctx->view == NULL) { + dctx->view = ISC_LIST_HEAD(dctx->viewlist); + if (dctx->view == NULL) + goto done; + INSIST(dctx->zone == NULL); + } else + goto resume; + nextview: + fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name); + resume: + if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache) { + style = &dns_master_style_cache; + /* start cache dump */ + if (dctx->view->view->cachedb != NULL) + dns_db_attach(dctx->view->view->cachedb, &dctx->cache); + if (dctx->cache != NULL) { + + fprintf(dctx->fp, ";\n; Cache dump of view '%s'\n;\n", + dctx->view->view->name); + result = dns_master_dumptostreaminc(dctx->mctx, + dctx->cache, NULL, + style, dctx->fp, + dctx->task, + dumpdone, dctx, + &dctx->mdctx); + if (result == DNS_R_CONTINUE) + return; + if (result == ISC_R_NOTIMPLEMENTED) + fprintf(dctx->fp, "; %s\n", + dns_result_totext(result)); + else if (result != ISC_R_SUCCESS) + goto cleanup; + } + } + if (dctx->cache != NULL) { + dns_adb_dump(dctx->view->view->adb, dctx->fp); + dns_db_detach(&dctx->cache); + } + if (dctx->dumpzones) { + style = &dns_master_style_full; + nextzone: + if (dctx->version != NULL) + dns_db_closeversion(dctx->db, &dctx->version, + ISC_FALSE); + if (dctx->db != NULL) + dns_db_detach(&dctx->db); + if (dctx->zone == NULL) + dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist); + else + dctx->zone = ISC_LIST_NEXT(dctx->zone, link); + if (dctx->zone != NULL) { + /* start zone dump */ + dns_zone_name(dctx->zone->zone, buf, sizeof(buf)); + fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf); + result = dns_zone_getdb(dctx->zone->zone, &dctx->db); + if (result != ISC_R_SUCCESS) { + fprintf(dctx->fp, "; %s\n", + dns_result_totext(result)); + goto nextzone; + } + dns_db_currentversion(dctx->db, &dctx->version); + result = dns_master_dumptostreaminc(dctx->mctx, + dctx->db, + dctx->version, + style, dctx->fp, + dctx->task, + dumpdone, dctx, + &dctx->mdctx); + if (result == DNS_R_CONTINUE) + return; + if (result == ISC_R_NOTIMPLEMENTED) { + fprintf(dctx->fp, "; %s\n", + dns_result_totext(result)); + result = ISC_R_SUCCESS; + goto nextzone; + } + if (result != ISC_R_SUCCESS) + goto cleanup; + } + } + if (dctx->view != NULL) + dctx->view = ISC_LIST_NEXT(dctx->view, link); + if (dctx->view != NULL) + goto nextview; + done: + fprintf(dctx->fp, "; Dump complete\n"); + result = isc_stdio_flush(dctx->fp); + if (result == ISC_R_SUCCESS) + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumpdb complete"); + cleanup: + if (result != ISC_R_SUCCESS) + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumpdb failed: %s", dns_result_totext(result)); + dumpcontext_destroy(dctx); +} + +isc_result_t +ns_server_dumpdb(ns_server_t *server, char *args) { + struct dumpcontext *dctx = NULL; + dns_view_t *view; + isc_result_t result; + char *ptr; + const char *sep; + + dctx = isc_mem_get(server->mctx, sizeof(*dctx)); + if (dctx == NULL) + return (ISC_R_NOMEMORY); + + dctx->mctx = server->mctx; + dctx->dumpcache = ISC_TRUE; + dctx->dumpzones = ISC_FALSE; + dctx->fp = NULL; + ISC_LIST_INIT(dctx->viewlist); + dctx->view = NULL; + dctx->zone = NULL; + dctx->cache = NULL; + dctx->mdctx = NULL; + dctx->db = NULL; + dctx->cache = NULL; + dctx->task = NULL; + dctx->version = NULL; + isc_task_attach(server->task, &dctx->task); + + CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp), + "could not open dump file", server->dumpfile); + + /* Skip the command name. */ + ptr = next_token(&args, " \t"); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + + sep = (args == NULL) ? "" : ": "; + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumpdb started%s%s", sep, (args != NULL) ? args : ""); + + ptr = next_token(&args, " \t"); + if (ptr != NULL && strcmp(ptr, "-all") == 0) { + dctx->dumpzones = ISC_TRUE; + dctx->dumpcache = ISC_TRUE; + ptr = next_token(&args, " \t"); + } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) { + dctx->dumpzones = ISC_FALSE; + dctx->dumpcache = ISC_TRUE; + ptr = next_token(&args, " \t"); + } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) { + dctx->dumpzones = ISC_TRUE; + dctx->dumpcache = ISC_FALSE; + ptr = next_token(&args, " \t"); + } + + nextview: + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (ptr != NULL && strcmp(view->name, ptr) != 0) + continue; + CHECK(add_view_tolist(dctx, view)); + } + if (ptr != NULL) { + ptr = next_token(&args, " \t"); + if (ptr != NULL) + goto nextview; + } + dumpdone(dctx, ISC_R_SUCCESS); + return (ISC_R_SUCCESS); + + cleanup: + if (dctx != NULL) + dumpcontext_destroy(dctx); + return (result); +} + +isc_result_t +ns_server_dumprecursing(ns_server_t *server) { + FILE *fp = NULL; + isc_result_t result; + + CHECKMF(isc_stdio_open(server->recfile, "w", &fp), + "could not open dump file", server->recfile); + fprintf(fp,";\n; Recursing Queries\n;\n"); + ns_interfacemgr_dumprecursing(fp, server->interfacemgr); + fprintf(fp, "; Dump complete\n"); + + cleanup: + if (fp != NULL) + result = isc_stdio_close(fp); + return (result); +} + +isc_result_t +ns_server_setdebuglevel(ns_server_t *server, char *args) { + char *ptr; + char *levelstr; + char *endp; + long newlevel; + + UNUSED(server); + + /* Skip the command name. */ + ptr = next_token(&args, " \t"); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + + /* Look for the new level name. */ + levelstr = next_token(&args, " \t"); + if (levelstr == NULL) { + if (ns_g_debuglevel < 99) + ns_g_debuglevel++; + } else { + newlevel = strtol(levelstr, &endp, 10); + if (*endp != '\0' || newlevel < 0 || newlevel > 99) + return (ISC_R_RANGE); + ns_g_debuglevel = (unsigned int)newlevel; + } + isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel); + return (ISC_R_SUCCESS); +} + +isc_result_t +ns_server_flushcache(ns_server_t *server, char *args) { + char *ptr, *viewname; + dns_view_t *view; + isc_boolean_t flushed = ISC_FALSE; + isc_result_t result; + + /* Skip the command name. */ + ptr = next_token(&args, " \t"); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + + /* Look for the view name. */ + viewname = next_token(&args, " \t"); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (viewname != NULL && strcasecmp(viewname, view->name) != 0) + continue; + result = dns_view_flushcache(view); + if (result != ISC_R_SUCCESS) + goto out; + flushed = ISC_TRUE; + } + if (flushed) + result = ISC_R_SUCCESS; + else + result = ISC_R_FAILURE; + out: + isc_task_endexclusive(server->task); + return (result); +} + +isc_result_t +ns_server_flushname(ns_server_t *server, char *args) { + char *ptr, *target, *viewname; + dns_view_t *view; + isc_boolean_t flushed = ISC_FALSE; + isc_result_t result; + isc_buffer_t b; + dns_fixedname_t fixed; + dns_name_t *name; + + /* Skip the command name. */ + ptr = next_token(&args, " \t"); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + + /* Find the domain name to flush. */ + target = next_token(&args, " \t"); + if (target == NULL) + return (ISC_R_UNEXPECTEDEND); + + isc_buffer_init(&b, target, strlen(target)); + isc_buffer_add(&b, strlen(target)); + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + /* Look for the view name. */ + viewname = next_token(&args, " \t"); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + flushed = ISC_TRUE; + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (viewname != NULL && strcasecmp(viewname, view->name) != 0) + continue; + result = dns_view_flushname(view, name); + if (result != ISC_R_SUCCESS) + flushed = ISC_FALSE; + } + if (flushed) + result = ISC_R_SUCCESS; + else + result = ISC_R_FAILURE; + isc_task_endexclusive(server->task); + return (result); +} + +isc_result_t +ns_server_status(ns_server_t *server, isc_buffer_t *text) { + int zonecount, xferrunning, xferdeferred, soaqueries; + unsigned int n; + + zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY); + xferrunning = dns_zonemgr_getcount(server->zonemgr, + DNS_ZONESTATE_XFERRUNNING); + xferdeferred = dns_zonemgr_getcount(server->zonemgr, + DNS_ZONESTATE_XFERDEFERRED); + soaqueries = dns_zonemgr_getcount(server->zonemgr, + DNS_ZONESTATE_SOAQUERY); + n = snprintf((char *)isc_buffer_used(text), + isc_buffer_availablelength(text), + "number of zones: %u\n" + "debug level: %d\n" + "xfers running: %u\n" + "xfers deferred: %u\n" + "soa queries in progress: %u\n" + "query logging is %s\n" + "recursive clients: %d/%d\n" + "tcp clients: %d/%d\n" + "server is up and running", + zonecount, ns_g_debuglevel, xferrunning, xferdeferred, + soaqueries, server->log_queries ? "ON" : "OFF", + server->recursionquota.used, server->recursionquota.max, + server->tcpquota.used, server->tcpquota.max); + if (n >= isc_buffer_availablelength(text)) + return (ISC_R_NOSPACE); + isc_buffer_add(text, n); + return (ISC_R_SUCCESS); +} + +/* + * Act on a "freeze" or "unfreeze" command from the command channel. + */ +isc_result_t +ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args) { + isc_result_t result; + dns_zone_t *zone = NULL; + dns_zonetype_t type; + char classstr[DNS_RDATACLASS_FORMATSIZE]; + char zonename[DNS_NAME_FORMATSIZE]; + dns_view_t *view; + char *journal; + const char *vname, *sep; + isc_boolean_t frozen; + + result = zone_from_args(server, args, &zone); + if (result != ISC_R_SUCCESS) + return (result); + if (zone == NULL) + return (ISC_R_UNEXPECTEDEND); + type = dns_zone_gettype(zone); + if (type != dns_zone_master) { + dns_zone_detach(&zone); + return (ISC_R_NOTFOUND); + } + + frozen = dns_zone_getupdatedisabled(zone); + if (freeze) { + if (frozen) + result = DNS_R_FROZEN; + if (result == ISC_R_SUCCESS) + result = dns_zone_flush(zone); + if (result == ISC_R_SUCCESS) { + journal = dns_zone_getjournal(zone); + if (journal != NULL) + (void)isc_file_remove(journal); + } + } else { + if (frozen) { + result = dns_zone_load(zone); + if (result == DNS_R_CONTINUE || + result == DNS_R_UPTODATE) + result = ISC_R_SUCCESS; + } + } + if (result == ISC_R_SUCCESS) + dns_zone_setupdatedisabled(zone, freeze); + + view = dns_zone_getview(zone); + if (strcmp(view->name, "_bind") == 0 || + strcmp(view->name, "_default") == 0) + { + vname = ""; + sep = ""; + } else { + vname = view->name; + sep = " "; + } + dns_rdataclass_format(dns_zone_getclass(zone), classstr, + sizeof(classstr)); + dns_name_format(dns_zone_getorigin(zone), + zonename, sizeof(zonename)); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_INFO, + "%s zone '%s/%s'%s%s: %s", + freeze ? "freezing" : "unfreezing", + zonename, classstr, sep, vname, + isc_result_totext(result)); + dns_zone_detach(&zone); + return (result); +} + +#ifdef HAVE_LIBSCF +/* + * This function adds a message for rndc to echo if named + * is managed by smf and is also running chroot. + */ +isc_result_t +ns_smf_add_message(isc_buffer_t *text) { + unsigned int n; + + n = snprintf((char *)isc_buffer_used(text), + isc_buffer_availablelength(text), + "use svcadm(1M) to manage named"); + if (n >= isc_buffer_availablelength(text)) + return (ISC_R_NOSPACE); + isc_buffer_add(text, n); + return (ISC_R_SUCCESS); +} +#endif /* HAVE_LIBSCF */ diff -Nuar bind-9.3.2-orig/bin/named/xfrout.c bind-9.3.2-mod/bin/named/xfrout.c --- bind-9.3.2-orig/bin/named/xfrout.c 2005-10-14 04:13:05.000000000 +0200 +++ bind-9.3.2-mod/bin/named/xfrout.c 2006-02-20 19:13:47.000000000 +0100 @@ -27,6 +27,9 @@ #include #include +#ifdef DLZ +#include +#endif #include #include #include @@ -903,6 +906,9 @@ char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")]; char keyname[DNS_NAME_FORMATSIZE]; isc_boolean_t is_poll = ISC_FALSE; +#ifdef DLZ + isc_boolean_t is_dlz = ISC_FALSE; +#endif switch (reqtype) { case dns_rdatatype_axfr: @@ -953,19 +959,59 @@ result = dns_zt_find(client->view->zonetable, question_name, 0, NULL, &zone); + if (result != ISC_R_SUCCESS) - FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", - question_name, question_class); - switch(dns_zone_gettype(zone)) { - case dns_zone_master: - case dns_zone_slave: - break; /* Master and slave zones are OK for transfer. */ - default: - FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", - question_name, question_class); +#ifdef DLZ + { + /* Normal zone table does not have a match. Try the DLZ database */ + if(client->view->dlzdatabase != NULL) + { + result = dns_dlzallowzonexfr(client->view, question_name, &client->peeraddr, &db); + + + if(result == ISC_R_NOPERM){ + result = DNS_R_REFUSED; + { + char _buf1[DNS_NAME_FORMATSIZE]; + char _buf2[DNS_RDATACLASS_FORMATSIZE]; + dns_name_format(question_name, _buf1, sizeof(_buf1)); + dns_rdataclass_format(question_class, _buf2, sizeof(_buf2)); + ns_client_log(client, DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_XFER_OUT, + ISC_LOG_ERROR, "zone transfer '%s/%s' denied", _buf1, _buf2); + } + goto failure; + } + if(result != ISC_R_SUCCESS) +#endif + FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", + question_name, question_class); +#ifdef DLZ + is_dlz = ISC_TRUE; + /* DLZ only support full zone transfer, not incremental */ + if(reqtype != dns_rdatatype_axfr){ + mnemonic = "AXFR-style IXFR"; + reqtype = dns_rdatatype_axfr; + } + + } else { /* not DLZ and not in normal zone table, we are not authoritative */ + FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); + } + } else { /* zone table has a match */ +#endif + switch(dns_zone_gettype(zone)) { + case dns_zone_master: + case dns_zone_slave: + break; /* Master and slave zones are OK for transfer. */ + default: + FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); + } + CHECK(dns_zone_getdb(zone, &db)); + dns_db_currentversion(db, &ver); +#ifdef DLZ } - CHECK(dns_zone_getdb(zone, &db)); - dns_db_currentversion(db, &ver); +#endif + xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), "%s question section OK", mnemonic); @@ -1021,11 +1067,20 @@ /* * Decide whether to allow this transfer. */ +#ifdef DLZ + /* + * if not a DLZ zone decide whether to allow this transfer. + */ + if( !is_dlz ){ +#endif ns_client_aclmsg("zone transfer", question_name, reqtype, client->view->rdclass, msg, sizeof(msg)); CHECK(ns_client_checkacl(client, msg, dns_zone_getxfracl(zone), ISC_TRUE, ISC_LOG_ERROR)); +#ifdef DLZ + } +#endif /* * AXFR over UDP is not possible. @@ -1049,6 +1104,10 @@ /* * Get a dynamically allocated copy of the current SOA. */ +#ifdef DLZ + if( is_dlz ) + dns_db_currentversion(db, &ver); +#endif CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, ¤t_soa_tuple)); @@ -1131,19 +1190,37 @@ * Create the xfrout context object. This transfers the ownership * of "stream", "db", "ver", and "quota" to the xfrout context object. */ - CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, - reqtype, question_class, db, ver, quota, - stream, dns_message_gettsigkey(request), - tsigbuf, - dns_zone_getmaxxfrout(zone), - dns_zone_getidleout(zone), - (format == dns_many_answers) ? - ISC_TRUE : ISC_FALSE, - &xfr)); + + + +#ifdef DLZ + if(is_dlz) + CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, + reqtype, question_class, db, ver, quota, + stream, dns_message_gettsigkey(request), + tsigbuf, + 3600, + 3600, + (format == dns_many_answers) ? + ISC_TRUE : ISC_FALSE, + &xfr)); + else +#endif + CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, + reqtype, question_class, db, ver, quota, + stream, dns_message_gettsigkey(request), + tsigbuf, + dns_zone_getmaxxfrout(zone), + dns_zone_getidleout(zone), + (format == dns_many_answers) ? + ISC_TRUE : ISC_FALSE, + &xfr)); + xfr->mnemonic = mnemonic; stream = NULL; quota = NULL; + CHECK(xfr->stream->methods->first(xfr->stream)); if (xfr->tsigkey != NULL) { diff -Nuar bind-9.3.2-orig/configure bind-9.3.2-mod/configure --- bind-9.3.2-orig/configure 2005-10-21 01:57:38.000000000 +0200 +++ bind-9.3.2-mod/configure 2006-02-20 19:14:09.000000000 +0100 @@ -14,7 +14,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # -# $Id: configure,v 1.284.2.19.2.47 2005/10/20 23:57:38 marka Exp $ +# $Id: COPYRIGHT,v 1.6.2.2.8.3 2005/01/10 23:51:37 marka Exp $ # # Portions Copyright (C) 1996-2001 Nominum, Inc. # @@ -495,7 +495,7 @@ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS subdirs build build_cpu build_vendor build_os host host_cpu host_vendor host_os SET_MAKE RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA STD_CINCLUDES STD_CDEFINES STD_CWARNINGS CCOPT AR ARFLAGS LN ETAGS PERL CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP ISC_SOCKADDR_LEN_T ISC_PLATFORM_HAVELONGLONG ISC_PLATFORM_HAVELIFCONF ISC_PLATFORM_NEEDSYSSELECTH LWRES_PLATFORM_NEEDSYSSELECTH USE_OPENSSL DST_OPENSSL_INC USE_GSSAPI DST_GSSAPI_INC DNS_CRYPTO_LIBS ALWAYS_DEFINES ISC_PLATFORM_USETHREADS ISC_THREAD_DIR MKDEPCC MKDEPCFLAGS MKDEPPROG IRIX_DNSSEC_WARNINGS_HACK purify_path PURIFY LN_S ECHO ac_ct_AR STRIP ac_ct_STRIP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL O A SA LIBTOOL_MKDEP_SED LIBTOOL_MODE_COMPILE LIBTOOL_MODE_INSTALL LIBTOOL_MODE_LINK LIBTOOL_ALLOW_UNDEFINED LIBTOOL_IN_MAIN LIBBIND ISC_PLATFORM_HAVEIPV6 LWRES_PLATFORM_HAVEIPV6 ISC_PLATFORM_NEEDNETINETIN6H LWRES_PLATFORM_NEEDNETINETIN6H ISC_PLATFORM_NEEDNETINET6IN6H LWRES_PLATFORM_NEEDNETINET6IN6H ISC_PLATFORM_HAVEINADDR6 LWRES_PLATFORM_HAVEINADDR6 ISC_PLATFORM_NEEDIN6ADDRANY LWRES_PLATFORM_NEEDIN6ADDRANY ISC_PLATFORM_NEEDIN6ADDRLOOPBACK LWRES_PLATFORM_NEEDIN6ADDRLOOPBACK ISC_PLATFORM_HAVEIN6PKTINFO ISC_PLATFORM_FIXIN6ISADDR ISC_IPV6_H ISC_IPV6_O ISC_ISCIPV6_O ISC_IPV6_C LWRES_HAVE_SIN6_SCOPE_ID ISC_PLATFORM_HAVESCOPEID ISC_PLATFORM_HAVEIF_LADDRREQ ISC_PLATFORM_HAVEIF_LADDRCONF ISC_PLATFORM_NEEDNTOP ISC_PLATFORM_NEEDPTON ISC_PLATFORM_NEEDATON ISC_PLATFORM_HAVESALEN LWRES_PLATFORM_HAVESALEN ISC_PLATFORM_MSGHDRFLAVOR ISC_PLATFORM_NEEDPORTT ISC_LWRES_NEEDADDRINFO ISC_LWRES_NEEDRRSETINFO ISC_LWRES_SETHOSTENTINT ISC_LWRES_ENDHOSTENTINT ISC_LWRES_GETNETBYADDRINADDR ISC_LWRES_SETNETENTINT ISC_LWRES_ENDNETENTINT ISC_LWRES_GETHOSTBYADDRVOID ISC_LWRES_NEEDHERRNO ISC_LWRES_GETIPNODEPROTO ISC_LWRES_GETADDRINFOPROTO ISC_LWRES_GETNAMEINFOPROTO ISC_PLATFORM_NEEDSTRSEP ISC_PLATFORM_NEEDMEMMOVE ISC_PLATFORM_NEEDSTRTOUL LWRES_PLATFORM_NEEDSTRTOUL GENRANDOMLIB ISC_PLATFORM_NEEDSTRLCPY ISC_PLATFORM_NEEDSTRLCAT ISC_PLATFORM_NEEDSPRINTF LWRES_PLATFORM_NEEDSPRINTF ISC_PLATFORM_NEEDVSNPRINTF LWRES_PLATFORM_NEEDVSNPRINTF ISC_EXTRA_OBJS ISC_EXTRA_SRCS ISC_PLATFORM_QUADFORMAT LWRES_PLATFORM_QUADFORMAT ISC_PLATFORM_RLIMITTYPE ISC_PLATFORM_USEDECLSPEC LWRES_PLATFORM_USEDECLSPEC ISC_PLATFORM_BRACEPTHREADONCEINIT ISC_PLATFORM_HAVEIFNAMETOINDEX LATEX PDFLATEX XSLTPROC XMLLINT XSLT_DOCBOOK_STYLE_HTML XSLT_DOCBOOK_STYLE_XHTML XSLT_DOCBOOK_STYLE_MAN XSLT_DOCBOOK_CHUNK_HTML XSLT_DOCBOOK_CHUNK_XHTML XSLT_DB2LATEX_STYLE XSLT_DB2LATEX_ADMONITIONS BIND9_TOP_BUILDDIR BIND9_ISC_BUILDINCLUDE BIND9_ISCCC_BUILDINCLUDE BIND9_ISCCFG_BUILDINCLUDE BIND9_DNS_BUILDINCLUDE BIND9_LWRES_BUILDINCLUDE BIND9_BIND9_BUILDINCLUDE BIND9_VERSION LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS subdirs build build_cpu build_vendor build_os host host_cpu host_vendor host_os SET_MAKE RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA STD_CINCLUDES STD_CDEFINES STD_CWARNINGS CCOPT AR ARFLAGS LN ETAGS PERL CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP ISC_SOCKADDR_LEN_T ISC_PLATFORM_HAVELONGLONG ISC_PLATFORM_HAVELIFCONF ISC_PLATFORM_NEEDSYSSELECTH LWRES_PLATFORM_NEEDSYSSELECTH DLZ_POSTGRES_INC DLZ_POSTGRES_LIBS USE_DLZ_POSTGRES DLZ_MYSQL_INC DLZ_MYSQL_LIBS USE_DLZ_MYSQL DLZ_BDB_UTIL DLZ_BDB_INC DLZ_BDB_LIBS USE_DLZ_BDB USE_DLZ_FILESYSTEM DLZ_LDAP_INC DLZ_LDAP_LIBS USE_DLZ_LDAP DLZ_ODBC_INC DLZ_ODBC_LIBS USE_DLZ_ODBC USE_DLZ_STUB USE_DLZ USE_OPENSSL DST_OPENSSL_INC USE_GSSAPI DST_GSSAPI_INC DNS_CRYPTO_LIBS ALWAYS_DEFINES ISC_PLATFORM_USETHREADS ISC_THREAD_DIR MKDEPCC MKDEPCFLAGS MKDEPPROG IRIX_DNSSEC_WARNINGS_HACK purify_path PURIFY LN_S ECHO ac_ct_AR STRIP ac_ct_STRIP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL O A SA LIBTOOL_MKDEP_SED LIBTOOL_MODE_COMPILE LIBTOOL_MODE_INSTALL LIBTOOL_MODE_LINK LIBTOOL_ALLOW_UNDEFINED LIBTOOL_IN_MAIN LIBBIND ISC_PLATFORM_HAVEIPV6 LWRES_PLATFORM_HAVEIPV6 ISC_PLATFORM_NEEDNETINETIN6H LWRES_PLATFORM_NEEDNETINETIN6H ISC_PLATFORM_NEEDNETINET6IN6H LWRES_PLATFORM_NEEDNETINET6IN6H ISC_PLATFORM_HAVEINADDR6 LWRES_PLATFORM_HAVEINADDR6 ISC_PLATFORM_NEEDIN6ADDRANY LWRES_PLATFORM_NEEDIN6ADDRANY ISC_PLATFORM_NEEDIN6ADDRLOOPBACK LWRES_PLATFORM_NEEDIN6ADDRLOOPBACK ISC_PLATFORM_HAVEIN6PKTINFO ISC_PLATFORM_FIXIN6ISADDR ISC_IPV6_H ISC_IPV6_O ISC_ISCIPV6_O ISC_IPV6_C LWRES_HAVE_SIN6_SCOPE_ID ISC_PLATFORM_HAVESCOPEID ISC_PLATFORM_HAVEIF_LADDRREQ ISC_PLATFORM_HAVEIF_LADDRCONF ISC_PLATFORM_NEEDNTOP ISC_PLATFORM_NEEDPTON ISC_PLATFORM_NEEDATON ISC_PLATFORM_HAVESALEN LWRES_PLATFORM_HAVESALEN ISC_PLATFORM_MSGHDRFLAVOR ISC_PLATFORM_NEEDPORTT ISC_LWRES_NEEDADDRINFO ISC_LWRES_NEEDRRSETINFO ISC_LWRES_SETHOSTENTINT ISC_LWRES_ENDHOSTENTINT ISC_LWRES_GETNETBYADDRINADDR ISC_LWRES_SETNETENTINT ISC_LWRES_ENDNETENTINT ISC_LWRES_GETHOSTBYADDRVOID ISC_LWRES_NEEDHERRNO ISC_LWRES_GETIPNODEPROTO ISC_LWRES_GETADDRINFOPROTO ISC_LWRES_GETNAMEINFOPROTO ISC_PLATFORM_NEEDSTRSEP ISC_PLATFORM_NEEDMEMMOVE ISC_PLATFORM_NEEDSTRTOUL LWRES_PLATFORM_NEEDSTRTOUL GENRANDOMLIB ISC_PLATFORM_NEEDSTRLCPY ISC_PLATFORM_NEEDSTRLCAT ISC_PLATFORM_NEEDSPRINTF LWRES_PLATFORM_NEEDSPRINTF ISC_PLATFORM_NEEDVSNPRINTF LWRES_PLATFORM_NEEDVSNPRINTF ISC_EXTRA_OBJS ISC_EXTRA_SRCS ISC_PLATFORM_QUADFORMAT LWRES_PLATFORM_QUADFORMAT ISC_PLATFORM_RLIMITTYPE ISC_PLATFORM_USEDECLSPEC LWRES_PLATFORM_USEDECLSPEC ISC_PLATFORM_BRACEPTHREADONCEINIT ISC_PLATFORM_HAVEIFNAMETOINDEX LATEX PDFLATEX XSLTPROC XMLLINT XSLT_DOCBOOK_STYLE_HTML XSLT_DOCBOOK_STYLE_XHTML XSLT_DOCBOOK_STYLE_MAN XSLT_DOCBOOK_CHUNK_HTML XSLT_DOCBOOK_CHUNK_XHTML XSLT_DB2LATEX_STYLE XSLT_DB2LATEX_ADMONITIONS BIND9_TOP_BUILDDIR BIND9_ISC_BUILDINCLUDE BIND9_ISCCC_BUILDINCLUDE BIND9_ISCCFG_BUILDINCLUDE BIND9_DNS_BUILDINCLUDE BIND9_LWRES_BUILDINCLUDE BIND9_BIND9_BUILDINCLUDE BIND9_VERSION LIBOBJS LTLIBOBJS' ac_subst_files='BIND9_MAKE_INCLUDES BIND9_MAKE_RULES LIBISC_API LIBISCCC_API LIBISCCFG_API LIBDNS_API LIBBIND9_API LIBLWRES_API' # Initialize some variables set by options. @@ -1068,6 +1068,20 @@ Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-dlz-postgres=PATH Build with Postgres DLZ driver yes|no|path. + (Required to use Postgres with DLZ) + --with-dlz-mysql=PATH Build with MySQL DLZ driver yes|no|path. + (Required to use MySQL with DLZ) + --with-dlz-bdb=PATH Build with Berkeley DB DLZ driver yes|no|path. + (Required to use Berkeley DB with DLZ) + --with-dlz-filesystem=PATH Build with filesystem DLZ driver yes|no. + (Required to use file system driver with DLZ) + --with-dlz-ldap=PATH Build with LDAP DLZ driver yes|no|path. + (Required to use LDAP with DLZ) + --with-dlz-odbc=PATH Build with ODBC DLZ driver yes|no|path. + (Required to use ODBC with DLZ) + --with-dlz-stub=PATH Build with stub DLZ driver yes|no. + (Required to use stub driver with DLZ) --with-openssl=PATH Build with OpenSSL yes|no|path. (Required for DNSSEC) --with-randomdev=PATH Specify path for random device @@ -1186,7 +1200,7 @@ else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi - cd $ac_popdir + cd "$ac_popdir" done fi @@ -2669,8 +2683,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2728,8 +2741,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2845,8 +2857,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2900,8 +2911,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2946,8 +2956,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2991,8 +3000,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -3350,8 +3358,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -3523,8 +3530,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -3632,8 +3638,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -3693,8 +3698,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -3800,8 +3804,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -3873,8 +3876,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -3939,8 +3941,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4005,8 +4006,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4071,8 +4071,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4137,8 +4136,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4207,8 +4205,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4266,8 +4263,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4330,8 +4326,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4388,8 +4383,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4452,8 +4446,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4513,8 +4506,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4606,8 +4598,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4649,8 +4640,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4707,8 +4697,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4803,6 +4792,480 @@ # +# Build DLZ support? +# +# The use_dlz flag is set to no. If one of the DLZ drivers is selected +# for build the use_dlz flag will be set to yes +# + +use_dlz=no + +# +# was --with-dlz-postgres specified? +# + +echo "$as_me:$LINENO: checking for Postgres DLZ driver" >&5 +echo $ECHO_N "checking for Postgres DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_postgres or --without-dlz_postgres was given. +if test "${with_dlz_postgres+set}" = set; then + withval="$with_dlz_postgres" + use_dlz_postgres="$withval" +else + use_dlz_postgres="no" +fi; + +case "$use_dlz_postgres" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + if test "$use_dlz_postgres" = "yes" + then + # User did not specify a path - guess it + pgdirs="/usr /usr/local /usr/local/pgsql /usr/pkg" + for d in $pgdirs + do + if test -f $d/include/libpq-fe.h + then + use_dlz_postgres=$d/include + use_dlz_postgres_lib=$d/lib + break + fi + done + + # If config is not found then try to use pg_config + # debian hack + PG_CONFIG="no" + PG_CONFIG=`which pg_config` + + if test "$PG_CONFIG" != "no"; then + use_dlz_postgres=`pg_config --includedir` + use_dlz_postgres_lib=`pg_config --libdir` + fi + + + if test "$use_dlz_postgres" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path otherwise put pg_config directory in your path" >&5 +echo "$as_me: error: PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path otherwise put pg_config directory in your path" >&2;} + { (exit 1); exit 1; }; } + fi + fi + USE_DLZ_POSTGRES='-DDLZ_POSTGRES' + DLZ_POSTGRES_INC="-I$use_dlz_postgres" + DLZ_POSTGRES_LIBS="-L$use_dlz_postgres_lib -lpq" + echo "$as_me:$LINENO: result: using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres" >&5 +echo "${ECHO_T}using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres" >&6 + ;; +esac + + + + + + + +# +# was --with-dlz-mysql specified? +# + +echo "$as_me:$LINENO: checking for MySQL DLZ driver" >&5 +echo $ECHO_N "checking for MySQL DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_mysql or --without-dlz_mysql was given. +if test "${with_dlz_mysql+set}" = set; then + withval="$with_dlz_mysql" + use_dlz_mysql="$withval" +else + use_dlz_mysql="no" +fi; + +case "$use_dlz_mysql" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + if test "$use_dlz_mysql" = "yes" + then + # User did not specify a path - guess it + mysqldirs="/usr /usr/local /usr/local/mysql /usr/pkg" + for d in $mysqldirs + do + if test -f $d/include/mysql/mysql.h + then + use_dlz_mysql=$d + break + fi + done + if test "$use_dlz_mysql" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path" >&5 +echo "$as_me: error: MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path" >&2;} + { (exit 1); exit 1; }; } + fi + fi + USE_DLZ_MYSQL='-DDLZ_MYSQL' + DLZ_MYSQL_INC="-I$use_dlz_mysql/include/mysql" + DLZ_MYSQL_LIBS="-L$use_dlz_mysql/lib/mysql -lmysqlclient -lz -lcrypt -lnsl -lm" + echo "$as_me:$LINENO: result: using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql" >&5 +echo "${ECHO_T}using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql" >&6 + ;; +esac + + + + + + +# +# was --with-dlz-bdb specified? +# + +echo "$as_me:$LINENO: checking for Berkeley DB DLZ driver" >&5 +echo $ECHO_N "checking for Berkeley DB DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_bdb or --without-dlz_bdb was given. +if test "${with_dlz_bdb+set}" = set; then + withval="$with_dlz_bdb" + use_dlz_bdb="$withval" +else + use_dlz_bdb="no" +fi; + +case "$use_dlz_bdb" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + #set DLZ_BDB_UTIL to "dlzbdb" so that it is built too + DLZ_BDB_UTIL="dlzbdb" + if test "$use_dlz_bdb" = "yes" + then + # User did not specify a path - guess it + bdbdirs="/usr /usr/local /usr/pkg" + for d in $bdbdirs + do + if test -f $d/include/db.h + then + use_dlz_bdb=$d + DLZ_BDB_INCDIR=$use_dlz_bdb/include + DLZ_BDB_LIB="-ldb" + break + fi + done + if test "$use_dlz_bdb" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: Berkeley DB was not found in any of $bdbdirs; use --with-dlz-bdb=/path" >&5 +echo "$as_me: error: Berkeley DB was not found in any of $bdbdirs; use --with-dlz-bdb=/path" >&2;} + { (exit 1); exit 1; }; } + fi + # if the user specified a path try a few variations of the path + # for the include and library dirs + elif test -d "$use_dlz_bdb" + then + # set both to yes, so we can check them later + DLZ_BDB_INCDIR="yes" + DLZ_BDB_LIB="yes" + + # check other locations for includes. + bdb_incdirs="/ /db41/ /db4/ /db/" + for d in $bdb_incdirs + do + if test -f $use_dlz_bdb/include${d}db.h + then + DLZ_BDB_INCDIR=$use_dlz_bdb/include${d} + break + fi + done + + if test "$DLZ_BDB_INCDIR" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: Berkeley DB header was not found in $use_dlz_bdb/include, \ +$use_dlz_bdb/include/db41, $use_dlz_bdb/include/db4 or $use_dlz_bdb/include/db" >&5 +echo "$as_me: error: Berkeley DB header was not found in $use_dlz_bdb/include, \ +$use_dlz_bdb/include/db41, $use_dlz_bdb/include/db4 or $use_dlz_bdb/include/db" >&2;} + { (exit 1); exit 1; }; } + fi + + # look for libname other than libdb.so + bdb_libnames="db41 db-4.1 db" + for d in $bdb_libnames + do + if test -f $use_dlz_bdb/lib/lib${d}.so + then + DLZ_BDB_LIB="-l${d}" + fi + done + + if test "$DLZ_BDB_LIB" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: Berkeley DB library libdb41.so, libdb-4.1.so or libdb.so could not be found \ +in $use_dlz_bdb/lib" >&5 +echo "$as_me: error: Berkeley DB library libdb41.so, libdb-4.1.so or libdb.so could not be found \ +in $use_dlz_bdb/lib" >&2;} + { (exit 1); exit 1; }; } + fi + + + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: Path $use_dlz_bdb does not exist" >&5 +echo "$as_me: error: Path $use_dlz_bdb does not exist" >&2;} + { (exit 1); exit 1; }; } + + fi + + USE_DLZ_BDB='-DDLZ_BDB' + DLZ_BDB_INC="-I$DLZ_BDB_INCDIR" + DLZ_BDB_LIBS="-L${use_dlz_bdb}/lib ${DLZ_BDB_LIB}" + echo "$as_me:$LINENO: result: using Berkeley DB from $use_dlz_bdb/lib and $DLZ_BDB_INCDIR" >&5 +echo "${ECHO_T}using Berkeley DB from $use_dlz_bdb/lib and $DLZ_BDB_INCDIR" >&6 + ;; +esac + + + + + + + +# +# was --with-dlz-filesystem specified? +# + +echo "$as_me:$LINENO: checking for file system DLZ driver" >&5 +echo $ECHO_N "checking for file system DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_filesystem or --without-dlz_filesystem was given. +if test "${with_dlz_filesystem+set}" = set; then + withval="$with_dlz_filesystem" + use_dlz_filesystem="$withval" +else + use_dlz_filesystem="no" +fi; + +case "$use_dlz_filesystem" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + USE_DLZ_FILESYSTEM='-DDLZ_FILESYSTEM' + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; +esac + + + + +# +# was --with-dlz-ldap specified? +# + +echo "$as_me:$LINENO: checking for LDAP DLZ driver" >&5 +echo $ECHO_N "checking for LDAP DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_ldap or --without-dlz_ldap was given. +if test "${with_dlz_ldap+set}" = set; then + withval="$with_dlz_ldap" + use_dlz_ldap="$withval" +else + use_dlz_ldap="no" +fi; + +case "$use_dlz_ldap" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + if test "$use_dlz_ldap" = "yes" + then + # User did not specify a path - guess it + ldapdirs="/usr /usr/local /usr/pkg" + for d in $ldapdirs + do + if test -f $d/include/ldap.h + then + use_dlz_ldap=$d + break + fi + done + if test "$use_dlz_ldap" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path" >&5 +echo "$as_me: error: LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path" >&2;} + { (exit 1); exit 1; }; } + fi + fi + USE_DLZ_LDAP='-DDLZ_LDAP' + DLZ_LDAP_INC="-I$use_dlz_ldap/include" + DLZ_LDAP_LIBS="-L$use_dlz_ldap/lib -lldap -llber" + echo "$as_me:$LINENO: result: using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include" >&5 +echo "${ECHO_T}using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include" >&6 + ;; +esac + + + + + + + +# +# was --with-dlz-odbc specified? +# + +echo "$as_me:$LINENO: checking for ODBC DLZ driver" >&5 +echo $ECHO_N "checking for ODBC DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_odbc or --without-dlz_odbc was given. +if test "${with_dlz_odbc+set}" = set; then + withval="$with_dlz_odbc" + use_dlz_odbc="$withval" +else + use_dlz_odbc="no" +fi; + +case "$use_dlz_odbc" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + if test "$use_dlz_odbc" = "yes" + then + # User did not specify a path - guess it + odbcdirs="/usr /usr/local /usr/pkg" + for d in $odbcdirs + do + if test -f $d/include/sql.h + then + use_dlz_odbc=$d + break + fi + done + + if test -z "`${CC} contrib/sdb/test//test.c -lodbc -o contrib/sdb/test/test.ok 2>&1`"; then + DLZ_ODBC_LIB="-lodbc" + elif test -z "`${CC} contrib/sdb/test//test.c -liodbc -o contrib/sdb/test/test.ok 2>&1`"; then + DLZ_ODBC_LIB="-liodbc" + else + DLZ_ODBC_LIB="" + use_dlz_odbc="no" + echo "$as_me:$LINENO: result: \"no ODBC libraries found\"" >&5 +echo "${ECHO_T}\"no ODBC libraries found\"" >&6 + fi + rm -f contrib/sdb/test/test.ok + + + if test "$use_dlz_odbc" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path" >&5 +echo "$as_me: error: ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path" >&2;} + { (exit 1); exit 1; }; } + fi + fi + USE_DLZ_ODBC='-DDLZ_ODBC' + DLZ_ODBC_INC="-I$use_dlz_odbc/include" + DLZ_ODBC_LIBS="-L$use_dlz_odbc/lib $DLZ_ODBC_LIB" + echo "$as_me:$LINENO: result: using ODBC from $use_dlz_odbc/lib and $use_dlz_odbc/include" >&5 +echo "${ECHO_T}using ODBC from $use_dlz_odbc/lib and $use_dlz_odbc/include" >&6 + ;; +esac + + + + + + + +# +# was --with-dlz-stub specified? +# + +echo "$as_me:$LINENO: checking for stub DLZ driver" >&5 +echo $ECHO_N "checking for stub DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_stub or --without-dlz_stub was given. +if test "${with_dlz_stub+set}" = set; then + withval="$with_dlz_stub" + use_dlz_stub="$withval" +else + use_dlz_stub="no" +fi; + +case "$use_dlz_stub" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + USE_DLZ_STUB='-DDLZ_STUB' + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; +esac + + + + +# +# If any DLZ driver is built, the DLZ core is built too. +# + +echo "$as_me:$LINENO: checking for DLZ" >&5 +echo $ECHO_N "checking for DLZ... $ECHO_C" >&6 + +# check if any DLZ driver was built +if test "$use_dlz" = "yes" +then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + USE_DLZ="-DDLZ" +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + + +# # was --with-openssl specified? # echo "$as_me:$LINENO: checking for OpenSSL library" >&5 @@ -4966,8 +5429,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5012,8 +5474,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5116,8 +5577,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5462,8 +5922,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5697,8 +6156,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5770,8 +6228,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5843,8 +6300,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5916,8 +6372,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5989,8 +6444,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6083,8 +6537,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6153,8 +6606,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6223,8 +6675,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6327,8 +6778,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6425,8 +6875,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6504,8 +6953,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6648,8 +7096,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6760,8 +7207,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6890,8 +7336,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6996,8 +7441,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7093,8 +7537,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7295,8 +7738,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7381,8 +7823,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7455,8 +7896,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8139,7 +8579,7 @@ ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 8142 "configure"' > conftest.$ac_ext + echo '#line 8582 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -8259,8 +8699,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8335,8 +8774,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8596,8 +9034,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8655,8 +9092,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8727,8 +9163,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8772,8 +9207,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9136,7 +9570,7 @@ # Provide some information about the compiler. -echo "$as_me:9139:" \ +echo "$as_me:9573:" \ "checking for Fortran 77 compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 @@ -9182,8 +9616,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_f77_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_f77_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9232,8 +9665,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_f77_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_f77_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10197,11 +10629,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10200: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10632: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:10204: \$? = $ac_status" >&5 + echo "$as_me:10636: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -10440,11 +10872,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10443: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10875: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:10447: \$? = $ac_status" >&5 + echo "$as_me:10879: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -10500,11 +10932,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10503: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10935: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:10507: \$? = $ac_status" >&5 + echo "$as_me:10939: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -10891,8 +11323,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10952,8 +11383,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12145,8 +12575,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12249,8 +12678,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12315,8 +12743,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12409,8 +12836,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12475,8 +12901,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12542,8 +12967,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12609,8 +13033,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12685,7 +13108,7 @@ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -13882,8 +14304,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14980,11 +15401,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14983: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15404: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:14987: \$? = $ac_status" >&5 + echo "$as_me:15408: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -15040,11 +15461,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15043: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15464: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:15047: \$? = $ac_status" >&5 + echo "$as_me:15468: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -15861,8 +16282,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15965,8 +16385,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16031,8 +16450,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16125,8 +16543,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16191,8 +16608,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16258,8 +16674,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16325,8 +16740,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16401,7 +16815,7 @@ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:17753: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:17343: \$? = $ac_status" >&5 + echo "$as_me:17757: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -17396,11 +17810,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:17399: $lt_compile\"" >&5) + (eval echo "\"\$as_me:17813: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:17403: \$? = $ac_status" >&5 + echo "$as_me:17817: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -17777,8 +18191,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_f77_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_f77_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17828,8 +18241,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_f77_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_f77_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19435,11 +19847,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:19438: $lt_compile\"" >&5) + (eval echo "\"\$as_me:19850: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:19442: \$? = $ac_status" >&5 + echo "$as_me:19854: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -19678,11 +20090,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:19681: $lt_compile\"" >&5) + (eval echo "\"\$as_me:20093: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:19685: \$? = $ac_status" >&5 + echo "$as_me:20097: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -19738,11 +20150,11 @@ -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:19741: $lt_compile\"" >&5) + (eval echo "\"\$as_me:20153: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:19745: \$? = $ac_status" >&5 + echo "$as_me:20157: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -20129,8 +20541,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20190,8 +20601,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21383,8 +21793,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21487,8 +21896,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21553,8 +21961,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21647,8 +22054,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21713,8 +22119,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21780,8 +22185,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21847,8 +22251,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21923,7 +22326,7 @@ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23317,8 +23719,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23380,8 +23781,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23442,8 +23842,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23503,8 +23902,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23564,8 +23962,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23655,8 +24052,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23712,8 +24108,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23884,8 +24279,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -23958,8 +24352,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24019,8 +24412,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24077,8 +24469,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24134,8 +24525,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24195,8 +24585,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24249,8 +24638,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24303,8 +24691,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24358,8 +24745,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24412,8 +24798,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24466,8 +24851,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24521,8 +24905,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24575,8 +24958,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24667,8 +25049,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24763,8 +25144,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24859,8 +25239,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -24959,8 +25338,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25082,8 +25460,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25155,8 +25532,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25259,8 +25635,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25373,8 +25748,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25464,8 +25838,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25562,8 +25935,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25666,8 +26038,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25764,8 +26135,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25827,8 +26197,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -25923,8 +26292,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -26028,8 +26396,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -26202,8 +26569,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -26275,8 +26641,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -26429,8 +26794,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -26614,8 +26978,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -26675,8 +27038,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -26736,8 +27098,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -26938,8 +27299,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -27033,8 +27393,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -27219,8 +27578,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -27292,8 +27650,7 @@ cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -27825,7 +28182,7 @@ LIBLWRES_API=$srcdir/lib/lwres/api - ac_config_files="$ac_config_files make/rules make/includes Makefile make/Makefile make/mkdep lib/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/nls/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile bin/Makefile bin/check/Makefile bin/named/Makefile bin/named/unix/Makefile bin/rndc/Makefile bin/rndc/unix/Makefile bin/dig/Makefile bin/nsupdate/Makefile bin/tests/Makefile bin/tests/names/Makefile bin/tests/master/Makefile bin/tests/rbt/Makefile bin/tests/db/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/dst/Makefile bin/tests/mem/Makefile bin/tests/net/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/lwresd/Makefile bin/tests/system/tkey/Makefile bin/tests/headerdep_test.sh bin/dnssec/Makefile doc/Makefile doc/arm/Makefile doc/misc/Makefile doc/xsl/Makefile isc-config.sh doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl" + ac_config_files="$ac_config_files make/rules make/includes Makefile make/Makefile make/mkdep lib/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/nls/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile bin/Makefile bin/dlzbdb/Makefile bin/check/Makefile bin/named/Makefile bin/named/unix/Makefile bin/rndc/Makefile bin/rndc/unix/Makefile bin/dig/Makefile bin/nsupdate/Makefile bin/tests/Makefile bin/tests/names/Makefile bin/tests/master/Makefile bin/tests/rbt/Makefile bin/tests/db/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/dst/Makefile bin/tests/mem/Makefile bin/tests/net/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/lwresd/Makefile bin/tests/system/tkey/Makefile bin/tests/headerdep_test.sh bin/dnssec/Makefile doc/Makefile doc/arm/Makefile doc/misc/Makefile doc/xsl/Makefile isc-config.sh doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -28394,6 +28751,7 @@ "lib/tests/include/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/tests/include/Makefile" ;; "lib/tests/include/tests/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/tests/include/tests/Makefile" ;; "bin/Makefile" ) CONFIG_FILES="$CONFIG_FILES bin/Makefile" ;; + "bin/dlzbdb/Makefile" ) CONFIG_FILES="$CONFIG_FILES bin/dlzbdb/Makefile" ;; "bin/check/Makefile" ) CONFIG_FILES="$CONFIG_FILES bin/check/Makefile" ;; "bin/named/Makefile" ) CONFIG_FILES="$CONFIG_FILES bin/named/Makefile" ;; "bin/named/unix/Makefile" ) CONFIG_FILES="$CONFIG_FILES bin/named/unix/Makefile" ;; @@ -28550,6 +28908,25 @@ s,@ISC_PLATFORM_HAVELIFCONF@,$ISC_PLATFORM_HAVELIFCONF,;t t s,@ISC_PLATFORM_NEEDSYSSELECTH@,$ISC_PLATFORM_NEEDSYSSELECTH,;t t s,@LWRES_PLATFORM_NEEDSYSSELECTH@,$LWRES_PLATFORM_NEEDSYSSELECTH,;t t +s,@DLZ_POSTGRES_INC@,$DLZ_POSTGRES_INC,;t t +s,@DLZ_POSTGRES_LIBS@,$DLZ_POSTGRES_LIBS,;t t +s,@USE_DLZ_POSTGRES@,$USE_DLZ_POSTGRES,;t t +s,@DLZ_MYSQL_INC@,$DLZ_MYSQL_INC,;t t +s,@DLZ_MYSQL_LIBS@,$DLZ_MYSQL_LIBS,;t t +s,@USE_DLZ_MYSQL@,$USE_DLZ_MYSQL,;t t +s,@DLZ_BDB_UTIL@,$DLZ_BDB_UTIL,;t t +s,@DLZ_BDB_INC@,$DLZ_BDB_INC,;t t +s,@DLZ_BDB_LIBS@,$DLZ_BDB_LIBS,;t t +s,@USE_DLZ_BDB@,$USE_DLZ_BDB,;t t +s,@USE_DLZ_FILESYSTEM@,$USE_DLZ_FILESYSTEM,;t t +s,@DLZ_LDAP_INC@,$DLZ_LDAP_INC,;t t +s,@DLZ_LDAP_LIBS@,$DLZ_LDAP_LIBS,;t t +s,@USE_DLZ_LDAP@,$USE_DLZ_LDAP,;t t +s,@DLZ_ODBC_INC@,$DLZ_ODBC_INC,;t t +s,@DLZ_ODBC_LIBS@,$DLZ_ODBC_LIBS,;t t +s,@USE_DLZ_ODBC@,$USE_DLZ_ODBC,;t t +s,@USE_DLZ_STUB@,$USE_DLZ_STUB,;t t +s,@USE_DLZ@,$USE_DLZ,;t t s,@USE_OPENSSL@,$USE_OPENSSL,;t t s,@DST_OPENSSL_INC@,$DST_OPENSSL_INC,;t t s,@USE_GSSAPI@,$USE_GSSAPI,;t t @@ -28851,11 +29228,6 @@ *) ac_INSTALL=$ac_top_builddir$INSTALL ;; esac - if test x"$ac_file" != x-; then - { echo "$as_me:$LINENO: creating $ac_file" >&5 -echo "$as_me: creating $ac_file" >&6;} - rm -f "$ac_file" - fi # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ @@ -28894,6 +29266,12 @@ fi;; esac done` || { (exit 1); exit 1; } + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub @@ -29349,7 +29727,7 @@ { (exit 1); exit 1; }; } fi - cd $ac_popdir + cd "$ac_popdir" done fi diff -Nuar bind-9.3.2-orig/configure.in bind-9.3.2-mod/configure.in --- bind-9.3.2-orig/configure.in 2005-10-21 01:47:25.000000000 +0200 +++ bind-9.3.2-mod/configure.in 2006-02-20 19:13:55.000000000 +0100 @@ -355,6 +355,414 @@ AC_C_BIGENDIAN # +# Build DLZ support? +# +# The use_dlz flag is set to no. If one of the DLZ drivers is selected +# for build the use_dlz flag will be set to yes +# + +use_dlz=no + +# +# was --with-dlz-postgres specified? +# + +AC_MSG_CHECKING(for Postgres DLZ driver) +AC_ARG_WITH(dlz_postgres, +[ --with-dlz-postgres[=PATH] Build with Postgres DLZ driver [yes|no|path]. + (Required to use Postgres with DLZ)], + use_dlz_postgres="$withval", use_dlz_postgres="no") + +case "$use_dlz_postgres" in + no) + AC_MSG_RESULT(no) + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + if test "$use_dlz_postgres" = "yes" + then + # User did not specify a path - guess it + pgdirs="/usr /usr/local /usr/local/pgsql /usr/pkg" + for d in $pgdirs + do + if test -f $d/include/libpq-fe.h + then + use_dlz_postgres=$d/include + use_dlz_postgres_lib=$d/lib + break + fi + done + + # If config is not found then try to use pg_config + # debian hack + PG_CONFIG="no" + PG_CONFIG=`which pg_config` + + if test "$PG_CONFIG" != "no"; then + use_dlz_postgres=`pg_config --includedir` + use_dlz_postgres_lib=`pg_config --libdir` + fi + + + if test "$use_dlz_postgres" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path otherwise put pg_config directory in your path]) + fi + fi + USE_DLZ_POSTGRES='-DDLZ_POSTGRES' + DLZ_POSTGRES_INC="-I$use_dlz_postgres" + DLZ_POSTGRES_LIBS="-L$use_dlz_postgres_lib -lpq" + AC_MSG_RESULT( +[using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres]) + ;; +esac + + +AC_SUBST(DLZ_POSTGRES_INC) +AC_SUBST(DLZ_POSTGRES_LIBS) +AC_SUBST(USE_DLZ_POSTGRES) + + +# +# was --with-dlz-mysql specified? +# + +AC_MSG_CHECKING(for MySQL DLZ driver) +AC_ARG_WITH(dlz_mysql, +[ --with-dlz-mysql[=PATH] Build with MySQL DLZ driver [yes|no|path]. + (Required to use MySQL with DLZ)], + use_dlz_mysql="$withval", use_dlz_mysql="no") + +case "$use_dlz_mysql" in + no) + AC_MSG_RESULT(no) + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + if test "$use_dlz_mysql" = "yes" + then + # User did not specify a path - guess it + mysqldirs="/usr /usr/local /usr/local/mysql /usr/pkg" + for d in $mysqldirs + do + if test -f $d/include/mysql/mysql.h + then + use_dlz_mysql=$d + break + fi + done + if test "$use_dlz_mysql" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path]) + fi + fi + USE_DLZ_MYSQL='-DDLZ_MYSQL' + DLZ_MYSQL_INC="-I$use_dlz_mysql/include/mysql" + DLZ_MYSQL_LIBS="-L$use_dlz_mysql/lib/mysql -lmysqlclient -lz -lcrypt -lnsl -lm" + AC_MSG_RESULT( +[using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql]) + ;; +esac + +AC_SUBST(DLZ_MYSQL_INC) +AC_SUBST(DLZ_MYSQL_LIBS) +AC_SUBST(USE_DLZ_MYSQL) + + +# +# was --with-dlz-bdb specified? +# + +AC_MSG_CHECKING(for Berkeley DB DLZ driver) +AC_ARG_WITH(dlz_bdb, +[ --with-dlz-bdb[=PATH] Build with Berkeley DB DLZ driver [yes|no|path]. + (Required to use Berkeley DB with DLZ)], + use_dlz_bdb="$withval", use_dlz_bdb="no") + +case "$use_dlz_bdb" in + no) + AC_MSG_RESULT(no) + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + #set DLZ_BDB_UTIL to "dlzbdb" so that it is built too + DLZ_BDB_UTIL="dlzbdb" + if test "$use_dlz_bdb" = "yes" + then + # User did not specify a path - guess it + bdbdirs="/usr /usr/local /usr/pkg" + for d in $bdbdirs + do + if test -f $d/include/db.h + then + use_dlz_bdb=$d + DLZ_BDB_INCDIR=$use_dlz_bdb/include + DLZ_BDB_LIB="-ldb" + break + fi + done + if test "$use_dlz_bdb" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[Berkeley DB was not found in any of $bdbdirs; use --with-dlz-bdb=/path]) + fi + # if the user specified a path try a few variations of the path + # for the include and library dirs + elif test -d "$use_dlz_bdb" + then + # set both to yes, so we can check them later + DLZ_BDB_INCDIR="yes" + DLZ_BDB_LIB="yes" + + # check other locations for includes. + bdb_incdirs="/ /db41/ /db4/ /db/" + for d in $bdb_incdirs + do + if test -f $use_dlz_bdb/include${d}db.h + then + DLZ_BDB_INCDIR=$use_dlz_bdb/include${d} + break + fi + done + + if test "$DLZ_BDB_INCDIR" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[Berkeley DB header was not found in $use_dlz_bdb/include, \ +$use_dlz_bdb/include/db41, $use_dlz_bdb/include/db4 or $use_dlz_bdb/include/db]) + fi + + # look for libname other than libdb.so + bdb_libnames="db41 db-4.1 db" + for d in $bdb_libnames + do + if test -f $use_dlz_bdb/lib/lib${d}.so + then + DLZ_BDB_LIB="-l${d}" + fi + done + + if test "$DLZ_BDB_LIB" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[Berkeley DB library libdb41.so, libdb-4.1.so or libdb.so could not be found \ +in $use_dlz_bdb/lib]) + fi + + + else + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[Path $use_dlz_bdb does not exist]) + + fi + + USE_DLZ_BDB='-DDLZ_BDB' + DLZ_BDB_INC="-I$DLZ_BDB_INCDIR" + DLZ_BDB_LIBS="-L${use_dlz_bdb}/lib ${DLZ_BDB_LIB}" + AC_MSG_RESULT( +[using Berkeley DB from $use_dlz_bdb/lib and $DLZ_BDB_INCDIR]) + ;; +esac + +AC_SUBST(DLZ_BDB_UTIL) +AC_SUBST(DLZ_BDB_INC) +AC_SUBST(DLZ_BDB_LIBS) +AC_SUBST(USE_DLZ_BDB) + + +# +# was --with-dlz-filesystem specified? +# + +AC_MSG_CHECKING(for file system DLZ driver) +AC_ARG_WITH(dlz_filesystem, +[ --with-dlz-filesystem[=PATH] Build with filesystem DLZ driver [yes|no]. + (Required to use file system driver with DLZ)], + use_dlz_filesystem="$withval", use_dlz_filesystem="no") + +case "$use_dlz_filesystem" in + no) + AC_MSG_RESULT(no) + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + USE_DLZ_FILESYSTEM='-DDLZ_FILESYSTEM' + AC_MSG_RESULT(yes) + ;; +esac + +AC_SUBST(USE_DLZ_FILESYSTEM) + + +# +# was --with-dlz-ldap specified? +# + +AC_MSG_CHECKING(for LDAP DLZ driver) +AC_ARG_WITH(dlz_ldap, +[ --with-dlz-ldap[=PATH] Build with LDAP DLZ driver [yes|no|path]. + (Required to use LDAP with DLZ)], + use_dlz_ldap="$withval", use_dlz_ldap="no") + +case "$use_dlz_ldap" in + no) + AC_MSG_RESULT(no) + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + if test "$use_dlz_ldap" = "yes" + then + # User did not specify a path - guess it + ldapdirs="/usr /usr/local /usr/pkg" + for d in $ldapdirs + do + if test -f $d/include/ldap.h + then + use_dlz_ldap=$d + break + fi + done + if test "$use_dlz_ldap" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path]) + fi + fi + USE_DLZ_LDAP='-DDLZ_LDAP' + DLZ_LDAP_INC="-I$use_dlz_ldap/include" + DLZ_LDAP_LIBS="-L$use_dlz_ldap/lib -lldap -llber" + AC_MSG_RESULT( +[using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include]) + ;; +esac + + +AC_SUBST(DLZ_LDAP_INC) +AC_SUBST(DLZ_LDAP_LIBS) +AC_SUBST(USE_DLZ_LDAP) + + +# +# was --with-dlz-odbc specified? +# + +AC_MSG_CHECKING(for ODBC DLZ driver) +AC_ARG_WITH(dlz_odbc, +[ --with-dlz-odbc[=PATH] Build with ODBC DLZ driver [yes|no|path]. + (Required to use ODBC with DLZ)], + use_dlz_odbc="$withval", use_dlz_odbc="no") + +case "$use_dlz_odbc" in + no) + AC_MSG_RESULT(no) + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + if test "$use_dlz_odbc" = "yes" + then + # User did not specify a path - guess it + odbcdirs="/usr /usr/local /usr/pkg" + for d in $odbcdirs + do + if test -f $d/include/sql.h + then + use_dlz_odbc=$d + break + fi + done + + if test -z "`${CC} contrib/sdb/test//test.c -lodbc -o contrib/sdb/test/test.ok 2>&1`"; then + DLZ_ODBC_LIB="-lodbc" + elif test -z "`${CC} contrib/sdb/test//test.c -liodbc -o contrib/sdb/test/test.ok 2>&1`"; then + DLZ_ODBC_LIB="-liodbc" + else + DLZ_ODBC_LIB="" + use_dlz_odbc="no" + AC_MSG_RESULT("no ODBC libraries found") + fi + rm -f contrib/sdb/test/test.ok + + + if test "$use_dlz_odbc" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path]) + fi + fi + USE_DLZ_ODBC='-DDLZ_ODBC' + DLZ_ODBC_INC="-I$use_dlz_odbc/include" + DLZ_ODBC_LIBS="-L$use_dlz_odbc/lib $DLZ_ODBC_LIB" + AC_MSG_RESULT( +[using ODBC from $use_dlz_odbc/lib and $use_dlz_odbc/include]) + ;; +esac + + +AC_SUBST(DLZ_ODBC_INC) +AC_SUBST(DLZ_ODBC_LIBS) +AC_SUBST(USE_DLZ_ODBC) + + +# +# was --with-dlz-stub specified? +# + +AC_MSG_CHECKING(for stub DLZ driver) +AC_ARG_WITH(dlz_stub, +[ --with-dlz-stub[=PATH] Build with stub DLZ driver [yes|no]. + (Required to use stub driver with DLZ)], + use_dlz_stub="$withval", use_dlz_stub="no") + +case "$use_dlz_stub" in + no) + AC_MSG_RESULT(no) + ;; + *) + #set use_dlz = "yes" so the DLZ core will be built + use_dlz=yes + USE_DLZ_STUB='-DDLZ_STUB' + AC_MSG_RESULT(yes) + ;; +esac + +AC_SUBST(USE_DLZ_STUB) + + +# +# If any DLZ driver is built, the DLZ core is built too. +# + +AC_MSG_CHECKING(for DLZ) + +# check if any DLZ driver was built +if test "$use_dlz" = "yes" +then + AC_MSG_RESULT(yes) + USE_DLZ="-DDLZ" +else + AC_MSG_RESULT(no) +fi + +AC_SUBST(USE_DLZ) + + +# # was --with-openssl specified? # AC_MSG_CHECKING(for OpenSSL library) @@ -2038,7 +2446,7 @@ AC_OUTPUT( make/rules - make/includes + make/includes Makefile make/Makefile make/mkdep @@ -2080,6 +2488,7 @@ lib/tests/include/Makefile lib/tests/include/tests/Makefile bin/Makefile + bin/dlzbdb/Makefile bin/check/Makefile bin/named/Makefile bin/named/unix/Makefile diff -Nuar bind-9.3.2-orig/contrib/sdb/test/test.c bind-9.3.2-mod/contrib/sdb/test/test.c --- bind-9.3.2-orig/contrib/sdb/test/test.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/contrib/sdb/test/test.c 2006-02-20 19:13:55.000000000 +0100 @@ -0,0 +1,5 @@ + +int main(void) { + +} + diff -Nuar bind-9.3.2-orig/do-conf bind-9.3.2-mod/do-conf --- bind-9.3.2-orig/do-conf 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/do-conf 2006-02-20 19:13:55.000000000 +0100 @@ -0,0 +1,21 @@ +./configure --prefix=/usr/local \ + --mandir=\$${prefix}/share/man \ + --infodir=\$${prefix}/share/info \ + --sysconfdir=/etc/bind \ + --localstatedir=/var \ + --enable-threads \ + --with-libtool \ + --enable-shared \ + --enable-static \ + --with-openssl=/usr \ + --with-gnu-ld \ + --enable-ipv6 \ + --with-dlz-mysql \ + --with-dlz-postgres \ + --with-dlz-bdb \ + --with-dlz-ldap \ + --with-dlz-odbc \ + --with-dlz-filesystem \ + --with-dlz-stub + + \ No newline at end of file diff -Nuar bind-9.3.2-orig/lib/dns/Makefile.in bind-9.3.2-mod/lib/dns/Makefile.in --- bind-9.3.2-orig/lib/dns/Makefile.in 2004-12-09 05:07:15.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/Makefile.in 2006-02-20 19:13:55.000000000 +0100 @@ -32,7 +32,7 @@ CINCLUDES = -I. -Iinclude ${DNS_INCLUDES} \ ${ISC_INCLUDES} @DST_OPENSSL_INC@ @DST_GSSAPI_INC@ -CDEFINES = -DUSE_MD5 @USE_OPENSSL@ @USE_GSSAPI@ +CDEFINES = -DUSE_MD5 @USE_OPENSSL@ @USE_GSSAPI@ @USE_DLZ@ CWARNINGS = ISCLIBS = ../../lib/isc/libisc.@A@ @@ -52,14 +52,14 @@ DNSOBJS = acl.@O@ adb.@O@ byaddr.@O@ \ cache.@O@ callbacks.@O@ compress.@O@ \ db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \ - dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \ + dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \ lib.@O@ log.@O@ lookup.@O@ \ master.@O@ masterdump.@O@ message.@O@ \ name.@O@ ncache.@O@ nsec.@O@ order.@O@ peer.@O@ portlist.@O@ \ rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \ rdatalist.@O@ \ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ request.@O@ \ - resolver.@O@ result.@O@ rootns.@O@ sdb.@O@ soa.@O@ ssu.@O@ \ + resolver.@O@ result.@O@ rootns.@O@ sdb.@O@ sdlz.@O@ soa.@O@ ssu.@O@ \ stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \ tsig.@O@ ttl.@O@ validator.@O@ \ version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ zt.@O@ @@ -76,14 +76,14 @@ SRCS = acl.c adb.c byaddr.c \ cache.c callbacks.c compress.c \ db.c dbiterator.c dbtable.c diff.c dispatch.c \ - dnssec.c ds.c forward.c journal.c keytable.c \ + dlz.c dnssec.c ds.c forward.c journal.c keytable.c \ lib.c log.c lookup.c \ master.c masterdump.c message.c \ name.c ncache.c nsec.c order.c peer.c portlist.c \ rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \ rdatalist.c \ rdataset.c rdatasetiter.c rdataslab.c request.c \ - resolver.c result.c rootns.c sdb.c soa.c ssu.c \ + resolver.c result.c rootns.c sdb.c sdlz.c soa.c ssu.c \ stats.c tcpmsg.c time.c timer.c tkey.c \ tsig.c ttl.c validator.c \ version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS} diff -Nuar bind-9.3.2-orig/lib/dns/dlz.c bind-9.3.2-mod/lib/dns/dlz.c --- bind-9.3.2-orig/lib/dns/dlz.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/dlz.c 2006-02-20 19:13:55.000000000 +0100 @@ -0,0 +1,480 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + + +/*** + *** Imports + ***/ + +#ifdef DLZ + +#include + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include + +/*** + *** Supported DLZ DB Implementations Registry + ***/ + +static ISC_LIST(dns_dlzimplementation_t) dlz_implementations; +static isc_rwlock_t dlz_implock; +static isc_once_t once = ISC_ONCE_INIT; + +static void +dlz_initialize(void) { + RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS); + ISC_LIST_INIT(dlz_implementations); +} + +/* Searches the dlz_implementations list for a driver matching name */ +static inline dns_dlzimplementation_t * +dlz_impfind(const char *name) { + dns_dlzimplementation_t *imp; + + for (imp = ISC_LIST_HEAD(dlz_implementations); + imp != NULL; + imp = ISC_LIST_NEXT(imp, link)) + if (strcasecmp(name, imp->name) == 0) + return (imp); + return (NULL); +} + +/*** + *** Basic DLZ Methods + ***/ +/**/ +isc_result_t +dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name, + isc_sockaddr_t *clientaddr, dns_db_t **dbp) +{ + isc_result_t result; + + /* Performs checks to make sure data is as we expect / require it to be. */ + REQUIRE(DNS_DLZ_VALID(view->dlzdatabase)); + REQUIRE(name != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + /* ask driver if the zone is supported */ + result = view->dlzdatabase->implementation->methods->allowzonexfr( + view->dlzdatabase->implementation->driverarg, + view->dlzdatabase->dbdata, view->dlzdatabase->mctx, + view->rdclass, name, clientaddr, dbp); + + if(result == ISC_R_NOTIMPLEMENTED) + return (ISC_R_NOTFOUND); + return result; +} + + +isc_result_t +dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername, + unsigned int argc, char *argv[], dns_dlzdb_t **dbp) +{ + + dns_dlzimplementation_t *impinfo; + isc_result_t result; + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + /* Performs checks to make sure data is as we expect / require it to be. */ + REQUIRE(dbp != NULL && *dbp == NULL); + REQUIRE(dlzname != NULL); + REQUIRE(drivername != NULL); + REQUIRE(mctx != NULL); + + /* write log message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Loading '%s' using driver %s", dlzname, drivername); + + /* lock the dlz_implementations list so we can search it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_read); + + /* search for the driver implementation */ + impinfo = dlz_impfind(drivername); + if (impinfo == NULL) { + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "unsupported DLZ database driver '%s'. %s not loaded.", + drivername, dlzname); + + return (ISC_R_NOTFOUND); + } + + /* Allocate memory to hold the DLZ database driver */ + (*dbp) = isc_mem_get(mctx, sizeof(dns_dlzdb_t)); + if((*dbp) == NULL){ + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + return (ISC_R_NOMEMORY); + } + + /* Make sure memory region is set to all 0's */ + memset((*dbp), 0, sizeof(dns_dlzdb_t)); + + (*dbp)->implementation = impinfo; + + /* Create a new database using implementation 'drivername'. */ + result = ((impinfo->methods->create)(mctx, dlzname, argc, argv, + impinfo->driverarg, &(*dbp)->dbdata)); + + /* mark the DLZ driver as valid */ + if(result == ISC_R_SUCCESS){ + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + (*dbp)->magic = DNS_DLZ_MAGIC; + isc_mem_attach(mctx, &(*dbp)->mctx); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "DLZ driver loaded successfully."); + return (ISC_R_SUCCESS); + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "DLZ driver failed to load."); + } + + /* impinfo->methods->create failed. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t)); + return (result); +} + +void +dns_dlzdestroy(dns_dlzdb_t **dbp) +{ + isc_mem_t *mctx; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unloading DLZ driver."); + + /* Perform checks to make sure data is as we expect / require it to be. */ + REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp)); + + /* call the drivers destroy method */ + if((*dbp) != NULL){ + mctx = (*dbp)->mctx; + (*dbp)->implementation->methods->destroy((*dbp)->implementation->driverarg, + (*dbp)->dbdata); + /* return memory */ + isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t)); + isc_mem_detach(&mctx); + } + + *dbp = NULL; +} + + +isc_result_t +dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels, + dns_db_t **dbp) +{ + dns_fixedname_t fname; + dns_name_t *zonename; + unsigned int namelabels; + unsigned int i; + isc_result_t result; + + /* Performs checks to make sure data is as we expect / require it to be. */ + REQUIRE(DNS_DLZ_VALID(view->dlzdatabase)); + REQUIRE(name != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + /* setup a "fixed" dns name */ + dns_fixedname_init(&fname); + zonename = dns_fixedname_name(&fname); + + /* count the number of labels in the name */ + namelabels = dns_name_countlabels(name); + + /* + * loop through starting with the longest domain name and trying shorter names + * portions of the name until we find a match, have an error, or are below the + * 'minlabels' threshold. minlabels is 0, if the standard database didn't have + * a zone name match. Otherwise minlables is the number of labels in that name. + * We need to beat that for a "better" match for the DLZ database to be + * authoritative instead of the standard database. + */ + for(i = namelabels; i > minlabels && i > 1; i--){ + if(i == namelabels){ + result = dns_name_copy(name, zonename, NULL); + if(result != ISC_R_SUCCESS) + return result; + } else{ + result = dns_name_splitatdepth(name, i, NULL, zonename); + if(result != ISC_R_SUCCESS) + return result; + } + + /* ask SDLZ driver if the zone is supported */ + result = view->dlzdatabase->implementation->methods->findzone( + view->dlzdatabase->implementation->driverarg, + view->dlzdatabase->dbdata, view->dlzdatabase->mctx, + view->rdclass, zonename, dbp); + if(result != ISC_R_NOTFOUND) + return result; + } + return (ISC_R_NOTFOUND); +} + +/* + * Registers a DLZ driver. This basically just adds the dlz + * driver to the list of available drivers in the dlz_implementations list. + */ +isc_result_t +dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods, + void *driverarg, isc_mem_t *mctx, dns_dlzimplementation_t **dlzimp) +{ + + dns_dlzimplementation_t *dlz_imp; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ driver '%s'", drivername); + + /* Performs checks to make sure data is as we expect / require it to be. */ + REQUIRE(drivername != NULL); + REQUIRE(methods != NULL); + REQUIRE(methods->create != NULL); + REQUIRE(methods->destroy != NULL); + REQUIRE(methods->findzone != NULL); + REQUIRE(mctx != NULL); + REQUIRE(dlzimp != NULL && *dlzimp == NULL); + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + /* lock the dlz_implementations list so we can modify it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_write); + + /* check that another already registered driver isn't using the same name */ + dlz_imp = dlz_impfind(drivername); + if (dlz_imp != NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "DLZ Driver '%s' already registered", drivername); + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + return (ISC_R_EXISTS); + } + + /* Allocate memory for a dlz_implementation object. Error if we cannot. */ + dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t)); + if (dlz_imp == NULL) { + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + return (ISC_R_NOMEMORY); + } + + /* Make sure memory region is set to all 0's */ + memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t)); + + /* Store the data passed into this method */ + dlz_imp->name = drivername; + dlz_imp->methods = methods; + dlz_imp->mctx = NULL; + dlz_imp->driverarg = driverarg; + + /* attach the new dlz_implementation object to a memory context */ + isc_mem_attach(mctx, &dlz_imp->mctx); + + /* + * prepare the dlz_implementation object to be put in a list, + * and append it to the list + */ + ISC_LINK_INIT(dlz_imp, link); + ISC_LIST_APPEND(dlz_implementations, dlz_imp, link); + + /* Unlock the dlz_implementations list. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + + /* Pass back the dlz_implementation that we created. */ + *dlzimp = dlz_imp; + + return (ISC_R_SUCCESS); +} + +/* Helper function for dns_dlzstrtoargv(). Pardon the gratuitous recursion. */ +static isc_result_t +dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp, + char ***argvp, unsigned int n) +{ + isc_result_t result; + +restart: + /* Discard leading whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + + if (*s == '\0') { + /* We have reached the end of the string. */ + *argcp = n; + *argvp = isc_mem_get(mctx, n * sizeof(char *)); + if (*argvp == NULL) + return (ISC_R_NOMEMORY); + } else { + char *p = s; + while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{'){ + if(*p == '\n'){ + *p = ' '; + goto restart; + } + p++; + } + + /* do "grouping", items between { and } are one arg */ + if(*p == '{'){ + char *t = p; + /* shift all characters to left by 1 to get rid of '{' */ + while (*t != '\0'){ + t++; + *(t-1) = *t; + } + while (*p != '\0' && *p != '}'){ + p++; + } + /* get rid of '}' character */ + if (*p == '}'){ + *p = '\0'; + p++; + } + /* normal case, no "grouping" */ + } else if (*p != '\0') + *p++ = '\0'; + + result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1); + if (result != ISC_R_SUCCESS) + return (result); + (*argvp)[n] = s; + } + return (ISC_R_SUCCESS); +} + +/* + * Tokenize the string "s" into whitespace-separated words, + * return the number of words in '*argcp' and an array + * of pointers to the words in '*argvp'. The caller + * must free the array using isc_mem_put(). The string + * is modified in-place. + */ +isc_result_t +dns_dlzstrtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) { + return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0)); +} + +/* + * Unregisters a DLZ driver. This basically just removes the dlz + * driver from the list of available drivers in the dlz_implementations list. + */ +void +dns_dlzunregister(dns_dlzimplementation_t **dlzimp) +{ + dns_dlzimplementation_t *dlz_imp; + isc_mem_t *mctx; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ driver."); + + /* Performs checks to make sure data is as we expect / require it to be. */ + REQUIRE(dlzimp != NULL && *dlzimp != NULL); + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + dlz_imp = *dlzimp; + + /* lock the dlz_implementations list so we can modify it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_write); + + /* remove the dlz_implementation object from the list */ + ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link); + mctx = dlz_imp->mctx; + + /* + * return the memory back to the available memory pool and remove it from + * the memory context. + */ + isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t)); + isc_mem_detach(&mctx); + + /* Unlock the dlz_implementations list. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + +} + +#endif + diff -Nuar bind-9.3.2-orig/lib/dns/include/dns/dlz.h bind-9.3.2-mod/lib/dns/include/dns/dlz.h --- bind-9.3.2-orig/lib/dns/include/dns/dlz.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/include/dns/dlz.h 2006-02-20 19:13:55.000000000 +0100 @@ -0,0 +1,264 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + + + +#ifndef DLZ_H +#define DLZ_H 1 + +/***** + ***** Module Info + *****/ + +/* + * DLZ Interface + * + * The DLZ interface allows zones to be looked up using a driver instead of + * Bind's default in memory zone table. + * + * + * Reliability: + * No anticipated impact. + * + * Resources: + * + * Security: + * No anticipated impact. + * + * Standards: + * None. + */ + +/***** + ***** Imports + *****/ + +#include +#include +#include + +#include + +ISC_LANG_BEGINDECLS + +/*** + *** Types + ***/ + +#define DNS_DLZ_MAGIC ISC_MAGIC('D','L','Z','D') +#define DNS_DLZ_VALID(dlz) ISC_MAGIC_VALID(dlz, DNS_DLZ_MAGIC) + +typedef isc_result_t +(*dns_dlzallowzonexfr_t)(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, isc_sockaddr_t *clientaddr, + dns_db_t **dbp); + +/* + * Method prototype. Drivers implementing the DLZ interface MUST supply an + * allow zone transfer method. This method is called when the DNS server is performing + * a zone transfer query. + * The driver's method should return ISC_R_SUCCESS and a database pointer + * to the name server if the zone is supported by the database, and zone transfer is allowed. + * Otherwise it will return ISC_R_NOTFOUND if the zone is not supported by the database, or + * ISC_R_NOPERM if zone transfers are not allowed. + * If an error occurs it should return a result code indicating the type of error. + */ + +typedef isc_result_t +(*dns_dlzcreate_t)(isc_mem_t *mctx, const char *dlzname, unsigned int argc, + char *argv[], void *driverarg, void **dbdata); + +/* + * Method prototype. Drivers implementing the DLZ interface MUST supply a + * create method. This method is called when the DNS server is starting up + * and creating drivers for use later. + */ + +typedef void +(*dns_dlzdestroy_t)(void *driverarg, void **dbdata); + +/* + * Method prototype. Drivers implementing the DLZ interface MUST supply a + * destroy method. This method is called when the DNS server is shuting down + * and no longer needs the driver. + */ + +typedef isc_result_t +(*dns_dlzfindzone_t)(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, dns_db_t **dbp); + +/* + * Method prototype. Drivers implementing the DLZ interface MUST supply a + * find zone method. This method is called when the DNS server is performing a query. + * The find zone method will be called with the longest possible name first, and continue + * to be called with successively shorter domain names, until any of the following occur: + * 1) a match is found, and the function returns (ISC_R_SUCCESS) + * 2) a problem occurs, and the functions returns anything other than (ISC_R_NOTFOUND) + * 3) we run out of domain name labels. I.E. we have tried the shortest domain name + * 4) the number of labels in the domain name is less than min_lables for dns_dlzfindzone + * + * The driver's find zone method should return ISC_R_SUCCESS and a database pointer + * to the name server if the zone is supported by the database. Otherwise + * it will return ISC_R_NOTFOUND, and a null pointer if the zone is not supported. + * If an error occurs it should return a result code indicating the type of error. + */ + + /* the methods supplied by a DLZ driver */ +typedef struct dns_dlzmethods { + dns_dlzcreate_t create; + dns_dlzdestroy_t destroy; + dns_dlzfindzone_t findzone; + dns_dlzallowzonexfr_t allowzonexfr; +} dns_dlzmethods_t; + + /* information about a DLZ driver */ +struct dns_dlzimplementation { + const char *name; + const dns_dlzmethods_t *methods; + isc_mem_t *mctx; + void *driverarg; + ISC_LINK(dns_dlzimplementation_t) link; +}; + + /* an instance of a DLZ driver */ +struct dns_dlzdb{ + unsigned int magic; + isc_mem_t *mctx; + dns_dlzimplementation_t *implementation; + void *dbdata; +}; + + +/*** + *** Method declarations + ***/ + +isc_result_t +dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name, isc_sockaddr_t *clientaddr, dns_db_t **dbp); + +/* + * This method is called when the DNS server is performing a zone transfer query. It will call + * the DLZ driver's allow zone tranfer method. + */ + +isc_result_t +dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername, unsigned int argc, + char *argv[], dns_dlzdb_t **dbp); + +/* + * This method is called when the DNS server is starting up and creating drivers for use later. + * It will search the DLZ driver list for 'drivername' and return a DLZ driver via dbp if a match + * is found. If the DLZ driver supplies a create method, this function will call it. + */ + +void +dns_dlzdestroy(dns_dlzdb_t **dbp); + +/* + * This method is called when the DNS server is shuting down and no longer needs the driver. + * If the DLZ driver supplies a destroy methods, this function will call it. + */ + +isc_result_t +dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels, dns_db_t **dbp); + +/* + * This method is called when the DNS server is performing a query. It will call the DLZ + * driver's find zone method. + */ + +isc_result_t +dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods, + void *driverarg, isc_mem_t *mctx, dns_dlzimplementation_t **dlzimp); + +/* + * Register a dynamically loadable zones (DLZ) driver for the database type 'drivername', + * implemented by the functions in '*methods'. + * + * dlzimp must point to a NULL dlz_implementation_t pointer. That is, + * dlzimp != NULL && *dlzimp == NULL. It will be assigned a value that + * will later be used to identify the driver when deregistering it. + */ + +isc_result_t +dns_dlzstrtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp); + +/* + * This method is called when the name server is starting up to parse the DLZ driver + * command line from named.conf. Basically it splits up a string into and argc / argv. + * The primary difference of this method is items between braces { } are considered + * only 1 word. for example the command line "this is { one grouped phrase } and this isn't" + * would be parsed into: + * argv[0]: "this" + * argv[1]: "is" + * argv{2]: " one grouped phrase " + * argv[3]: "and" + * argv[4]: "this" + * argv{5}: "isn't" + * braces should NOT be nested, more than one grouping in the command line is allowed. + * Notice, argv[2] has an extra space at the beginning and end. Extra spaces are not stripped + * between a grouping. You can do so in your driver if needed, or be sure not to put extra + * spaces before / after the braces. + */ + +void +dns_dlzunregister(dns_dlzimplementation_t **dlzimp); + +/* + * Removes the dlz driver from the list of registered dlz drivers. + * There must be no active dlz drivers of this type when this function + * is called. + */ + +ISC_LANG_ENDDECLS + +#endif /* DLZ_H */ diff -Nuar bind-9.3.2-orig/lib/dns/include/dns/log.h bind-9.3.2-mod/lib/dns/include/dns/log.h --- bind-9.3.2-orig/lib/dns/include/dns/log.h 2004-03-06 09:13:57.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/include/dns/log.h 2006-02-20 19:13:55.000000000 +0100 @@ -69,6 +69,9 @@ #define DNS_LOGMODULE_SDB (&dns_modules[22]) #define DNS_LOGMODULE_DIFF (&dns_modules[23]) #define DNS_LOGMODULE_HINTS (&dns_modules[24]) +#ifdef DLZ +#define DNS_LOGMODULE_DLZ (&dns_modules[25]) +#endif ISC_LANG_BEGINDECLS diff -Nuar bind-9.3.2-orig/lib/dns/include/dns/name.h bind-9.3.2-mod/lib/dns/include/dns/name.h --- bind-9.3.2-orig/lib/dns/include/dns/name.h 2004-09-08 02:29:34.000000000 +0200 +++ bind-9.3.2-mod/lib/dns/include/dns/name.h 2006-02-20 19:13:55.000000000 +0100 @@ -263,6 +263,11 @@ * ISC_FALSE 'name' does not have a dedicated buffer. */ +/*** + *** Extended Label Types + ***/ + +#define DNS_LABELTYPE_BITSTRING 0x41 /*** *** Properties @@ -523,6 +528,26 @@ * FALSE 'name' does not match the wildcard specified in 'wname' */ +unsigned int +dns_name_depth(const dns_name_t *name); +/* + * The depth of 'name'. + * + * Notes: + * The "depth" of a name represents how far down the DNS tree of trees + * the name is. For each wire-encoding label in name, the depth is + * increased by 1 for an ordinary label, and by the number of bits in + * a bitstring label. + * + * Depth is used when creating or validating DNSSEC signatures. + * + * Requires: + * 'name' is a valid name + * + * Returns: + * The depth of 'name'. + */ + /*** *** Labels ***/ @@ -983,6 +1008,40 @@ */ isc_result_t +dns_name_splitatdepth(dns_name_t *name, unsigned int depth, + dns_name_t *prefix, dns_name_t *suffix); +/* + * Split 'name' into two pieces at a certain depth. + * + * Requires: + * 'name' is a valid non-empty name. + * + * depth > 0 + * + * depth <= dns_name_depth(name) + * + * The preconditions of dns_name_split() apply to 'prefix' and 'suffix'. + * + * Ensures: + * + * On success: + * If 'prefix' is not NULL it will contain the least significant + * labels and bits. + * + * If 'suffix' is not NULL it will contain the most significant + * labels and bits. dns_name_countlabels(suffix) will be + * equal to suffixlabels. + * + * On failure: + * Either 'prefix' or 'suffix' is invalidated (depending + * on which one the problem was encountered with). + * + * Returns: + * The possible result codes are the same as those of dns_name_split(). + */ + + +isc_result_t dns_name_dup(dns_name_t *source, isc_mem_t *mctx, dns_name_t *target); /* * Make 'target' a dynamically allocated copy of 'source'. diff -Nuar bind-9.3.2-orig/lib/dns/include/dns/sdlz.h bind-9.3.2-mod/lib/dns/include/dns/sdlz.h --- bind-9.3.2-orig/lib/dns/include/dns/sdlz.h 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/include/dns/sdlz.h 2006-02-20 19:13:55.000000000 +0100 @@ -0,0 +1,244 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifndef SDLZ_H +#define SDLZ_H 1 + +#include + +ISC_LANG_BEGINDECLS + +#define DNS_SDLZFLAG_THREADSAFE 0x00000001U +#define DNS_SDLZFLAG_RELATIVEOWNER 0x00000002U +#define DNS_SDLZFLAG_RELATIVERDATA 0x00000004U + + /* A simple DLZ database. */ +typedef struct dns_sdlz_db dns_sdlz_db_t; + + /* A simple DLZ database lookup in progress. */ +typedef struct dns_sdlzlookup dns_sdlzlookup_t; + + /* A simple DLZ database traversal in progress. */ +typedef struct dns_sdlzallnodes dns_sdlzallnodes_t; + + +typedef isc_result_t +(*dns_sdlzallnodesfunc_t)(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +/* + * Method prototype. Drivers implementing the SDLZ interface may supply an + * all nodes method. This method is called when the DNS server is performing a + * zone transfer query, after the allow zone transfer method has been called. + * This method is only called if the allow zone transfer method returned + * ISC_R_SUCCESS. This method and the allow zone transfer method are both + * required for zone transfers to be supported. If the driver generates data + * dynamically (instead of searching in a database for it) it should not implement + * this function as a zone transfer would be meaningless. A SDLZ driver does not + * have to implement an all nodes method. + */ + +typedef isc_result_t +(*dns_sdlzallowzonexfr_t)(void *driverarg, void *dbdata, const char *name, + const char *client); + +/* + * Method prototype. Drivers implementing the SDLZ interface may supply an + * allow zone transfer method. This method is called when the DNS server is + * performing a zone transfer query, before the all nodes method can be called. + * This method and the all node method are both required for zone transfers to be + * supported. If the driver generates data dynamically (instead of searching in + * a database for it) it should not implement this function as a zone transfer + * would be meaningless. A SDLZ driver does not have to implement an allow + * zone transfer method. + * This method should return ISC_R_SUCCESS if the zone is supported + * by the database and a zone transfer is allowed for the specified client. + * If the zone is supported by the database, but zone transfers are not allowed + * for the specified client this method should return ISC_R_NOPERM.. + * Lastly the method should return ISC_R_NOTFOUND if the zone is not supported + * by the database. If an error occurs it should return a result code + * indicating the type of error. + */ + + +typedef isc_result_t +(*dns_sdlzauthorityfunc_t)(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup); + +/* + * Method prototype. Drivers implementing the SDLZ interface may supply an + * authority method. This method is called when the DNS server is performing a query, + * after both the find zone and lookup methods have been called. This method + * is required if the lookup function does not supply authority information for + * the dns record. A SDLZ driver does not have to implement an authority method. + */ + +typedef isc_result_t +(*dns_sdlzcreate_t)(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +/* + * Method prototype. Drivers implementing the SDLZ interface may supply a + * create method. This method is called when the DNS server is starting up + * and creating drivers for use later. A SDLZ driver does not have to implement + * a create method. + */ + +typedef void +(*dns_sdlzdestroy_t)(void *driverarg, void *dbdata); + +/* + * Method prototype. Drivers implementing the SDLZ interface may supply a + * destroy method. This method is called when the DNS server is shuting down + * and no longer needs the driver. A SDLZ driver does not have to implement + * a destroy method. + */ + +typedef isc_result_t +(*dns_sdlzfindzone_t)(void *driverarg, void *dbdata, const char *name); + +/* + * Method prototype. Drivers implementing the SDLZ interface MUST supply a + * find zone method. This method is called when the DNS server is performing a + * query to to determine if 'name' is a supported dns zone. + * The find zone method will be called with the longest possible name first, + * and continue to be called with successively shorter domain names, until + * any of the following occur: + * 1) the function returns (ISC_R_SUCCESS) indicating a zone name match. + * 2) a problem occurs, and the functions returns anything other than (ISC_R_NOTFOUND) + * 3) we run out of domain name labels. I.E. we have tried the shortest domain name + * 4) the number of labels in the domain name is less than min_lables for dns_dlzfindzone + * + * The driver's find zone method should return ISC_R_SUCCESS if the zone is + * supported by the database. Otherwise it should return ISC_R_NOTFOUND, if + * the zone is not supported. If an error occurs it should return a result + * code indicating the type of error. + */ + +typedef isc_result_t +(*dns_sdlzlookupfunc_t)(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +/* + * Method prototype. Drivers implementing the SDLZ interface MUST supply a + * lookup method. This method is called when the DNS server is performing a query, + * after the find zone and before any other methods have been called. This function + * returns record DNS record information using the dns_sdlz_putrr and dns_sdlz_putsoa + * functions. If this function supplies authority information for the DNS record + * the authority method is not required. If it does not, the authority function is + * required. A SDLZ driver must implement a lookup method. + */ + +typedef struct dns_sdlzmethods { + dns_sdlzcreate_t create; + dns_sdlzdestroy_t destroy; + dns_sdlzfindzone_t findzone; + dns_sdlzlookupfunc_t lookup; + dns_sdlzauthorityfunc_t authority; + dns_sdlzallnodesfunc_t allnodes; + dns_sdlzallowzonexfr_t allowzonexfr; +} dns_sdlzmethods_t; + +isc_result_t +dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods, + void *driverarg, unsigned int flags, isc_mem_t *mctx, + dns_sdlzimplementation_t **sdlzimp); +/* + * Register a dynamically loadable zones (dlz) driver for the database type + * 'drivername', implemented by the functions in '*methods'. + * + * sdlzimp must point to a NULL dns_sdlzimplementation_t pointer. That is, + * sdlzimp != NULL && *sdlzimp == NULL. It will be assigned a value that + * will later be used to identify the driver when deregistering it. + */ + +void +dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp); + +/* + * Removes the sdlz driver from the list of registered sdlz drivers. + * There must be no active sdlz drivers of this type when this function + * is called. + */ + +isc_result_t +dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name, + const char *type, dns_ttl_t ttl, const char *data); +/* + * Add a single resource record to the allnodes structure to be later + * parsed into a zone transfer response. + */ + +isc_result_t +dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl, + const char *data); +/* + * Add a single resource record to the lookup structure to be later + * parsed into a query response. + */ + +isc_result_t +dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname, + isc_uint32_t serial); +/* + * This function may optionally be called from the 'authority' callback + * to simplify construction of the SOA record for 'zone'. It will + * provide a SOA listing 'mname' as as the master server and 'rname' as + * the responsible person mailbox. It is the responsibility of the + * driver to increment the serial number between responses if necessary. + * All other SOA fields will have reasonable default values. + */ + + +ISC_LANG_ENDDECLS + +#endif /* SDLZ_H */ diff -Nuar bind-9.3.2-orig/lib/dns/include/dns/types.h bind-9.3.2-mod/lib/dns/include/dns/types.h --- bind-9.3.2-orig/lib/dns/include/dns/types.h 2004-03-08 10:04:39.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/include/dns/types.h 2006-02-20 19:13:55.000000000 +0100 @@ -50,6 +50,11 @@ typedef void dns_dbnode_t; typedef struct dns_dbtable dns_dbtable_t; typedef void dns_dbversion_t; +#ifdef DLZ +typedef struct dns_dlzimplementation dns_dlzimplementation_t; +typedef struct dns_dlzdb dns_dlzdb_t; +typedef struct dns_sdlzimplementation dns_sdlzimplementation_t; +#endif typedef struct dns_decompress dns_decompress_t; typedef struct dns_dispatch dns_dispatch_t; typedef struct dns_dispatchevent dns_dispatchevent_t; diff -Nuar bind-9.3.2-orig/lib/dns/include/dns/view.h bind-9.3.2-mod/lib/dns/include/dns/view.h --- bind-9.3.2-orig/lib/dns/include/dns/view.h 2004-03-10 03:55:58.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/include/dns/view.h 2006-02-20 19:13:55.000000000 +0100 @@ -83,6 +83,9 @@ dns_rdataclass_t rdclass; char * name; dns_zt_t * zonetable; +#ifdef DLZ + dns_dlzdb_t * dlzdatabase; +#endif dns_resolver_t * resolver; dns_adb_t * adb; dns_requestmgr_t * requestmgr; diff -Nuar bind-9.3.2-orig/lib/dns/log.c bind-9.3.2-mod/lib/dns/log.c --- bind-9.3.2-orig/lib/dns/log.c 2004-03-06 09:13:39.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/log.c 2006-02-20 19:13:55.000000000 +0100 @@ -74,6 +74,9 @@ { "dns/sdb", 0 }, { "dns/diff", 0 }, { "dns/hints", 0 }, +#ifdef DLZ + { "dns/dlz", 0 }, +#endif { NULL, 0 } }; diff -Nuar bind-9.3.2-orig/lib/dns/name.c bind-9.3.2-mod/lib/dns/name.c --- bind-9.3.2-orig/lib/dns/name.c 2005-10-14 03:38:48.000000000 +0200 +++ bind-9.3.2-mod/lib/dns/name.c 2006-02-20 19:13:55.000000000 +0100 @@ -767,6 +767,50 @@ } unsigned int +dns_name_depth(const dns_name_t *name) { + unsigned int depth, count, nrem, n; + unsigned char *ndata; + + /* + * The depth of 'name'. + */ + + REQUIRE(VALID_NAME(name)); + + if (name->labels == 0) + return (0); + + depth = 0; + ndata = name->ndata; + nrem = name->length; + while (nrem > 0) { + count = *ndata++; + nrem--; + if (count > 63) { + INSIST(count == DNS_LABELTYPE_BITSTRING); + INSIST(nrem != 0); + n = *ndata++; + nrem--; + if (n == 0) + n = 256; + depth += n; + count = n / 8; + if (n % 8 != 0) + count++; + } else { + depth++; + if (count == 0) + break; + } + INSIST(nrem >= count); + nrem -= count; + ndata += count; + } + + return (depth); +} + +unsigned int dns_name_countlabels(const dns_name_t *name) { /* * How many labels does 'name' have? @@ -1962,6 +2006,79 @@ } isc_result_t +dns_name_splitatdepth(dns_name_t *name, unsigned int depth, + dns_name_t *prefix, dns_name_t *suffix) +{ + unsigned int suffixlabels, nbits, label, count, n; + unsigned char *offsets, *ndata; + dns_offsets_t odata; + + /* + * Split 'name' into two pieces at a certain depth. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(name->labels > 0); + REQUIRE(depth > 0); + + SETUP_OFFSETS(name, offsets, odata); + + suffixlabels = 0; + nbits = 0; + label = name->labels; + do { + label--; + ndata = &name->ndata[offsets[label]]; + count = *ndata++; + if (count > 63) { + INSIST(count == DNS_LABELTYPE_BITSTRING); + /* + * Get the number of bits in the bitstring label. + */ + n = *ndata++; + if (n == 0) + n = 256; + suffixlabels++; + if (n <= depth) { + /* + * This entire bitstring is in the suffix. + */ + depth -= n; + } else { + /* + * Only the first 'depth' bits of this + * bitstring are in the suffix. + */ + nbits = depth; + depth = 0; + } + } else { + suffixlabels++; + depth--; + } + } while (depth != 0 && label != 0); + + /* + * If depth is not zero, then the caller violated the requirement + * that depth <= dns_name_depth(name). + */ + if (depth != 0) { + REQUIRE(depth <= dns_name_depth(name)); + /* + * We should never get here! + */ + INSIST(0); + } +/* ASD */ + dns_name_split(name, suffixlabels, prefix, suffix); + if (suffix==NULL) + return ISC_R_NOSPACE; + else + return ISC_R_SUCCESS; +} + + +isc_result_t dns_name_dup(dns_name_t *source, isc_mem_t *mctx, dns_name_t *target) { /* * Make 'target' a dynamically allocated copy of 'source'. diff -Nuar bind-9.3.2-orig/lib/dns/name.c.orig bind-9.3.2-mod/lib/dns/name.c.orig --- bind-9.3.2-orig/lib/dns/name.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/name.c.orig 2005-10-14 03:38:48.000000000 +0200 @@ -0,0 +1,2196 @@ +/* + * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1998-2003 Internet Software Consortium. + * + * 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: name.c,v 1.127.2.7.2.14 2005/10/14 01:38:48 marka Exp $ */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define VALID_NAME(n) ISC_MAGIC_VALID(n, DNS_NAME_MAGIC) + +typedef enum { + ft_init = 0, + ft_start, + ft_ordinary, + ft_initialescape, + ft_escape, + ft_escdecimal, + ft_at +} ft_state; + +typedef enum { + fw_start = 0, + fw_ordinary, + fw_copy, + fw_newcurrent +} fw_state; + +static char digitvalue[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ +}; + +static unsigned char maptolower[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +#define CONVERTTOASCII(c) +#define CONVERTFROMASCII(c) + +#define INIT_OFFSETS(name, var, default) \ + if (name->offsets != NULL) \ + var = name->offsets; \ + else \ + var = default; + +#define SETUP_OFFSETS(name, var, default) \ + if (name->offsets != NULL) \ + var = name->offsets; \ + else { \ + var = default; \ + set_offsets(name, var, NULL); \ + } + +/* + * Note: If additional attributes are added that should not be set for + * empty names, MAKE_EMPTY() must be changed so it clears them. + */ +#define MAKE_EMPTY(name) \ +do { \ + name->ndata = NULL; \ + name->length = 0; \ + name->labels = 0; \ + name->attributes &= ~DNS_NAMEATTR_ABSOLUTE; \ +} while (0); + +/* + * A name is "bindable" if it can be set to point to a new value, i.e. + * name->ndata and name->length may be changed. + */ +#define BINDABLE(name) \ + ((name->attributes & (DNS_NAMEATTR_READONLY|DNS_NAMEATTR_DYNAMIC)) \ + == 0) + +/* + * Note that the name data must be a char array, not a string + * literal, to avoid compiler warnings about discarding + * the const attribute of a string. + */ +static unsigned char root_ndata[] = { '\0' }; +static unsigned char root_offsets[] = { 0 }; + +static dns_name_t root = +{ + DNS_NAME_MAGIC, + root_ndata, 1, 1, + DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, + root_offsets, NULL, + {(void *)-1, (void *)-1}, + {NULL, NULL} +}; + +/* XXXDCL make const? */ +LIBDNS_EXTERNAL_DATA dns_name_t *dns_rootname = &root; + +static unsigned char wild_ndata[] = { '\001', '*' }; +static unsigned char wild_offsets[] = { 0 }; + +static dns_name_t wild = +{ + DNS_NAME_MAGIC, + wild_ndata, 2, 1, + DNS_NAMEATTR_READONLY, + wild_offsets, NULL, + {(void *)-1, (void *)-1}, + {NULL, NULL} +}; + +/* XXXDCL make const? */ +LIBDNS_EXTERNAL_DATA dns_name_t *dns_wildcardname = &wild; + +unsigned int +dns_fullname_hash(dns_name_t *name, isc_boolean_t case_sensitive); + +static void +set_offsets(const dns_name_t *name, unsigned char *offsets, + dns_name_t *set_name); + +void +dns_name_init(dns_name_t *name, unsigned char *offsets) { + /* + * Initialize 'name'. + */ + DNS_NAME_INIT(name, offsets); +} + +void +dns_name_reset(dns_name_t *name) { + REQUIRE(VALID_NAME(name)); + REQUIRE(BINDABLE(name)); + + DNS_NAME_RESET(name); +} + +void +dns_name_invalidate(dns_name_t *name) { + /* + * Make 'name' invalid. + */ + + REQUIRE(VALID_NAME(name)); + + name->magic = 0; + name->ndata = NULL; + name->length = 0; + name->labels = 0; + name->attributes = 0; + name->offsets = NULL; + name->buffer = NULL; + ISC_LINK_INIT(name, link); +} + +void +dns_name_setbuffer(dns_name_t *name, isc_buffer_t *buffer) { + /* + * Dedicate a buffer for use with 'name'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE((buffer != NULL && name->buffer == NULL) || + (buffer == NULL)); + + name->buffer = buffer; +} + +isc_boolean_t +dns_name_hasbuffer(const dns_name_t *name) { + /* + * Does 'name' have a dedicated buffer? + */ + + REQUIRE(VALID_NAME(name)); + + if (name->buffer != NULL) + return (ISC_TRUE); + + return (ISC_FALSE); +} + +isc_boolean_t +dns_name_isabsolute(const dns_name_t *name) { + + /* + * Does 'name' end in the root label? + */ + + REQUIRE(VALID_NAME(name)); + + if ((name->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) + return (ISC_TRUE); + return (ISC_FALSE); +} + +#define hyphenchar(c) ((c) == 0x2d) +#define asterchar(c) ((c) == 0x2a) +#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ + || ((c) >= 0x61 && (c) <= 0x7a)) +#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) +#define borderchar(c) (alphachar(c) || digitchar(c)) +#define middlechar(c) (borderchar(c) || hyphenchar(c)) +#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) + +isc_boolean_t +dns_name_ismailbox(const dns_name_t *name) { + unsigned char *ndata, ch; + unsigned int n; + isc_boolean_t first; + + REQUIRE(VALID_NAME(name)); + REQUIRE(name->labels > 0); + REQUIRE(name->attributes & DNS_NAMEATTR_ABSOLUTE); + + /* + * Root label. + */ + if (name->length == 1) + return (ISC_TRUE); + + ndata = name->ndata; + n = *ndata++; + INSIST(n <= 63); + while (n--) { + ch = *ndata++; + if (!domainchar(ch)) + return (ISC_FALSE); + } + + if (ndata == name->ndata + name->length) + return (ISC_FALSE); + + /* + * RFC292/RFC1123 hostname. + */ + while (ndata < (name->ndata + name->length)) { + n = *ndata++; + INSIST(n <= 63); + first = ISC_TRUE; + while (n--) { + ch = *ndata++; + if (first || n == 0) { + if (!borderchar(ch)) + return (ISC_FALSE); + } else { + if (!middlechar(ch)) + return (ISC_FALSE); + } + first = ISC_FALSE; + } + } + return (ISC_TRUE); +} + +isc_boolean_t +dns_name_ishostname(const dns_name_t *name, isc_boolean_t wildcard) { + unsigned char *ndata, ch; + unsigned int n; + isc_boolean_t first; + + REQUIRE(VALID_NAME(name)); + REQUIRE(name->labels > 0); + REQUIRE(name->attributes & DNS_NAMEATTR_ABSOLUTE); + + /* + * Root label. + */ + if (name->length == 1) + return (ISC_TRUE); + + /* + * Skip wildcard if this is a ownername. + */ + ndata = name->ndata; + if (wildcard && ndata[0] == 1 && ndata[1] == '*') + ndata += 2; + + /* + * RFC292/RFC1123 hostname. + */ + while (ndata < (name->ndata + name->length)) { + n = *ndata++; + INSIST(n <= 63); + first = ISC_TRUE; + while (n--) { + ch = *ndata++; + if (first || n == 0) { + if (!borderchar(ch)) + return (ISC_FALSE); + } else { + if (!middlechar(ch)) + return (ISC_FALSE); + } + first = ISC_FALSE; + } + } + return (ISC_TRUE); +} + +isc_boolean_t +dns_name_iswildcard(const dns_name_t *name) { + unsigned char *ndata; + + /* + * Is 'name' a wildcard name? + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(name->labels > 0); + + if (name->length >= 2) { + ndata = name->ndata; + if (ndata[0] == 1 && ndata[1] == '*') + return (ISC_TRUE); + } + + return (ISC_FALSE); +} + +static inline unsigned int +name_hash(dns_name_t *name, isc_boolean_t case_sensitive) { + unsigned int length; + const unsigned char *s; + unsigned int h = 0; + unsigned char c; + + length = name->length; + if (length > 16) + length = 16; + + /* + * This hash function is similar to the one Ousterhout + * uses in Tcl. + */ + s = name->ndata; + if (case_sensitive) { + while (length > 0) { + h += ( h << 3 ) + *s; + s++; + length--; + } + } else { + while (length > 0) { + c = maptolower[*s]; + h += ( h << 3 ) + c; + s++; + length--; + } + } + + return (h); +} + +unsigned int +dns_name_hash(dns_name_t *name, isc_boolean_t case_sensitive) { + /* + * Provide a hash value for 'name'. + */ + REQUIRE(VALID_NAME(name)); + + if (name->labels == 0) + return (0); + + return (name_hash(name, case_sensitive)); +} + +unsigned int +dns_name_fullhash(dns_name_t *name, isc_boolean_t case_sensitive) { + /* + * Provide a hash value for 'name'. + */ + REQUIRE(VALID_NAME(name)); + + if (name->labels == 0) + return (0); + + return (isc_hash_calc((const unsigned char *)name->ndata, + name->length, case_sensitive)); +} + +unsigned int +dns_fullname_hash(dns_name_t *name, isc_boolean_t case_sensitive) { + /* + * This function was deprecated due to the breakage of the name space + * convention. We only keep this internally to provide binary backward + * compatibility. + */ + REQUIRE(VALID_NAME(name)); + + return (dns_name_fullhash(name, case_sensitive)); +} + +unsigned int +dns_name_hashbylabel(dns_name_t *name, isc_boolean_t case_sensitive) { + unsigned char *offsets; + dns_offsets_t odata; + dns_name_t tname; + unsigned int h = 0; + unsigned int i; + + /* + * Provide a hash value for 'name'. + */ + REQUIRE(VALID_NAME(name)); + + if (name->labels == 0) + return (0); + else if (name->labels == 1) + return (name_hash(name, case_sensitive)); + + SETUP_OFFSETS(name, offsets, odata); + DNS_NAME_INIT(&tname, NULL); + tname.labels = 1; + h = 0; + for (i = 0; i < name->labels; i++) { + tname.ndata = name->ndata + offsets[i]; + if (i == name->labels - 1) + tname.length = name->length - offsets[i]; + else + tname.length = offsets[i + 1] - offsets[i]; + h += name_hash(&tname, case_sensitive); + } + + return (h); +} + +dns_namereln_t +dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2, + int *orderp, unsigned int *nlabelsp) +{ + unsigned int l1, l2, l, count1, count2, count, nlabels; + int cdiff, ldiff, chdiff; + unsigned char *label1, *label2; + unsigned char *offsets1, *offsets2; + dns_offsets_t odata1, odata2; + dns_namereln_t namereln = dns_namereln_none; + + /* + * Determine the relative ordering under the DNSSEC order relation of + * 'name1' and 'name2', and also determine the hierarchical + * relationship of the names. + * + * Note: It makes no sense for one of the names to be relative and the + * other absolute. If both names are relative, then to be meaningfully + * compared the caller must ensure that they are both relative to the + * same domain. + */ + + REQUIRE(VALID_NAME(name1)); + REQUIRE(VALID_NAME(name2)); + REQUIRE(orderp != NULL); + REQUIRE(nlabelsp != NULL); + /* + * Either name1 is absolute and name2 is absolute, or neither is. + */ + REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) == + (name2->attributes & DNS_NAMEATTR_ABSOLUTE)); + + SETUP_OFFSETS(name1, offsets1, odata1); + SETUP_OFFSETS(name2, offsets2, odata2); + + nlabels = 0; + l1 = name1->labels; + l2 = name2->labels; + ldiff = (int)l1 - (int)l2; + if (ldiff < 0) + l = l1; + else + l = l2; + + while (l > 0) { + l--; + l1--; + l2--; + label1 = &name1->ndata[offsets1[l1]]; + label2 = &name2->ndata[offsets2[l2]]; + count1 = *label1++; + count2 = *label2++; + + /* + * We dropped bitstring labels, and we don't support any + * other extended label types. + */ + INSIST(count1 <= 63 && count2 <= 63); + + cdiff = (int)count1 - (int)count2; + if (cdiff < 0) + count = count1; + else + count = count2; + + while (count > 0) { + chdiff = (int)maptolower[*label1] - + (int)maptolower[*label2]; + if (chdiff != 0) { + *orderp = chdiff; + goto done; + } + count--; + label1++; + label2++; + } + if (cdiff != 0) { + *orderp = cdiff; + goto done; + } + nlabels++; + } + + *orderp = ldiff; + if (ldiff < 0) + namereln = dns_namereln_contains; + else if (ldiff > 0) + namereln = dns_namereln_subdomain; + else + namereln = dns_namereln_equal; + + done: + *nlabelsp = nlabels; + + if (nlabels > 0 && namereln == dns_namereln_none) + namereln = dns_namereln_commonancestor; + + return (namereln); +} + +int +dns_name_compare(const dns_name_t *name1, const dns_name_t *name2) { + int order; + unsigned int nlabels; + + /* + * Determine the relative ordering under the DNSSEC order relation of + * 'name1' and 'name2'. + * + * Note: It makes no sense for one of the names to be relative and the + * other absolute. If both names are relative, then to be meaningfully + * compared the caller must ensure that they are both relative to the + * same domain. + */ + + (void)dns_name_fullcompare(name1, name2, &order, &nlabels); + + return (order); +} + +isc_boolean_t +dns_name_equal(const dns_name_t *name1, const dns_name_t *name2) { + unsigned int l, count; + unsigned char c; + unsigned char *label1, *label2; + + /* + * Are 'name1' and 'name2' equal? + * + * Note: It makes no sense for one of the names to be relative and the + * other absolute. If both names are relative, then to be meaningfully + * compared the caller must ensure that they are both relative to the + * same domain. + */ + + REQUIRE(VALID_NAME(name1)); + REQUIRE(VALID_NAME(name2)); + /* + * Either name1 is absolute and name2 is absolute, or neither is. + */ + REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) == + (name2->attributes & DNS_NAMEATTR_ABSOLUTE)); + + if (name1->length != name2->length) + return (ISC_FALSE); + + l = name1->labels; + + if (l != name2->labels) + return (ISC_FALSE); + + label1 = name1->ndata; + label2 = name2->ndata; + while (l > 0) { + l--; + count = *label1++; + if (count != *label2++) + return (ISC_FALSE); + + INSIST(count <= 63); /* no bitstring support */ + + while (count > 0) { + count--; + c = maptolower[*label1++]; + if (c != maptolower[*label2++]) + return (ISC_FALSE); + } + } + + return (ISC_TRUE); +} + +int +dns_name_rdatacompare(const dns_name_t *name1, const dns_name_t *name2) { + unsigned int l1, l2, l, count1, count2, count; + unsigned char c1, c2; + unsigned char *label1, *label2; + + /* + * Compare two absolute names as rdata. + */ + + REQUIRE(VALID_NAME(name1)); + REQUIRE(name1->labels > 0); + REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) != 0); + REQUIRE(VALID_NAME(name2)); + REQUIRE(name2->labels > 0); + REQUIRE((name2->attributes & DNS_NAMEATTR_ABSOLUTE) != 0); + + l1 = name1->labels; + l2 = name2->labels; + + l = (l1 < l2) ? l1 : l2; + + label1 = name1->ndata; + label2 = name2->ndata; + while (l > 0) { + l--; + count1 = *label1++; + count2 = *label2++; + + /* no bitstring support */ + INSIST(count1 <= 63 && count2 <= 63); + + if (count1 != count2) + return ((count1 < count2) ? -1 : 1); + count = count1; + while (count > 0) { + count--; + c1 = maptolower[*label1++]; + c2 = maptolower[*label2++]; + if (c1 < c2) + return (-1); + else if (c1 > c2) + return (1); + } + } + + /* + * If one name had more labels than the other, their common + * prefix must have been different because the shorter name + * ended with the root label and the longer one can't have + * a root label in the middle of it. Therefore, if we get + * to this point, the lengths must be equal. + */ + INSIST(l1 == l2); + + return (0); +} + +isc_boolean_t +dns_name_issubdomain(const dns_name_t *name1, const dns_name_t *name2) { + int order; + unsigned int nlabels; + dns_namereln_t namereln; + + /* + * Is 'name1' a subdomain of 'name2'? + * + * Note: It makes no sense for one of the names to be relative and the + * other absolute. If both names are relative, then to be meaningfully + * compared the caller must ensure that they are both relative to the + * same domain. + */ + + namereln = dns_name_fullcompare(name1, name2, &order, &nlabels); + if (namereln == dns_namereln_subdomain || + namereln == dns_namereln_equal) + return (ISC_TRUE); + + return (ISC_FALSE); +} + +isc_boolean_t +dns_name_matcheswildcard(const dns_name_t *name, const dns_name_t *wname) { + int order; + unsigned int nlabels, labels; + dns_name_t tname; + + REQUIRE(VALID_NAME(name)); + REQUIRE(name->labels > 0); + REQUIRE(VALID_NAME(wname)); + labels = wname->labels; + REQUIRE(labels > 0); + REQUIRE(dns_name_iswildcard(wname)); + + DNS_NAME_INIT(&tname, NULL); + dns_name_getlabelsequence(wname, 1, labels - 1, &tname); + if (dns_name_fullcompare(name, &tname, &order, &nlabels) == + dns_namereln_subdomain) + return (ISC_TRUE); + return (ISC_FALSE); +} + +unsigned int +dns_name_countlabels(const dns_name_t *name) { + /* + * How many labels does 'name' have? + */ + + REQUIRE(VALID_NAME(name)); + + ENSURE(name->labels <= 128); + + return (name->labels); +} + +void +dns_name_getlabel(const dns_name_t *name, unsigned int n, dns_label_t *label) { + unsigned char *offsets; + dns_offsets_t odata; + + /* + * Make 'label' refer to the 'n'th least significant label of 'name'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(name->labels > 0); + REQUIRE(n < name->labels); + REQUIRE(label != NULL); + + SETUP_OFFSETS(name, offsets, odata); + + label->base = &name->ndata[offsets[n]]; + if (n == name->labels - 1) + label->length = name->length - offsets[n]; + else + label->length = offsets[n + 1] - offsets[n]; +} + +void +dns_name_getlabelsequence(const dns_name_t *source, + unsigned int first, unsigned int n, + dns_name_t *target) +{ + unsigned char *offsets; + dns_offsets_t odata; + unsigned int firstoffset, endoffset; + + /* + * Make 'target' refer to the 'n' labels including and following + * 'first' in 'source'. + */ + + REQUIRE(VALID_NAME(source)); + REQUIRE(VALID_NAME(target)); + REQUIRE(first <= source->labels); + REQUIRE(first + n <= source->labels); + REQUIRE(BINDABLE(target)); + + SETUP_OFFSETS(source, offsets, odata); + + if (first == source->labels) + firstoffset = source->length; + else + firstoffset = offsets[first]; + + if (first + n == source->labels) + endoffset = source->length; + else + endoffset = offsets[first + n]; + + target->ndata = &source->ndata[firstoffset]; + target->length = endoffset - firstoffset; + + if (first + n == source->labels && n > 0 && + (source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) + target->attributes |= DNS_NAMEATTR_ABSOLUTE; + else + target->attributes &= ~DNS_NAMEATTR_ABSOLUTE; + + target->labels = n; + + /* + * If source and target are the same, and we're making target + * a prefix of source, the offsets table is correct already + * so we don't need to call set_offsets(). + */ + if (target->offsets != NULL && + (target != source || first != 0)) + set_offsets(target, target->offsets, NULL); +} + +void +dns_name_clone(dns_name_t *source, dns_name_t *target) { + + /* + * Make 'target' refer to the same name as 'source'. + */ + + REQUIRE(VALID_NAME(source)); + REQUIRE(VALID_NAME(target)); + REQUIRE(BINDABLE(target)); + + target->ndata = source->ndata; + target->length = source->length; + target->labels = source->labels; + target->attributes = source->attributes & + (unsigned int)~(DNS_NAMEATTR_READONLY | DNS_NAMEATTR_DYNAMIC | + DNS_NAMEATTR_DYNOFFSETS); + if (target->offsets != NULL && source->labels > 0) { + if (source->offsets != NULL) + memcpy(target->offsets, source->offsets, + source->labels); + else + set_offsets(target, target->offsets, NULL); + } +} + +void +dns_name_fromregion(dns_name_t *name, const isc_region_t *r) { + unsigned char *offsets; + dns_offsets_t odata; + unsigned int len; + isc_region_t r2; + + /* + * Make 'name' refer to region 'r'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(r != NULL); + REQUIRE(BINDABLE(name)); + + INIT_OFFSETS(name, offsets, odata); + + if (name->buffer != NULL) { + isc_buffer_clear(name->buffer); + isc_buffer_availableregion(name->buffer, &r2); + len = (r->length < r2.length) ? r->length : r2.length; + if (len > DNS_NAME_MAXWIRE) + len = DNS_NAME_MAXWIRE; + memcpy(r2.base, r->base, len); + name->ndata = r2.base; + name->length = len; + } else { + name->ndata = r->base; + name->length = (r->length <= DNS_NAME_MAXWIRE) ? + r->length : DNS_NAME_MAXWIRE; + } + + if (r->length > 0) + set_offsets(name, offsets, name); + else { + name->labels = 0; + name->attributes &= ~DNS_NAMEATTR_ABSOLUTE; + } + + if (name->buffer != NULL) + isc_buffer_add(name->buffer, name->length); +} + +void +dns_name_toregion(dns_name_t *name, isc_region_t *r) { + /* + * Make 'r' refer to 'name'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(r != NULL); + + DNS_NAME_TOREGION(name, r); +} + + +isc_result_t +dns_name_fromtext(dns_name_t *name, isc_buffer_t *source, + dns_name_t *origin, unsigned int options, + isc_buffer_t *target) +{ + unsigned char *ndata, *label; + char *tdata; + char c; + ft_state state; + unsigned int value, count; + unsigned int n1, n2, tlen, nrem, nused, digits, labels, tused; + isc_boolean_t done; + unsigned char *offsets; + dns_offsets_t odata; + isc_boolean_t downcase; + + /* + * Convert the textual representation of a DNS name at source + * into uncompressed wire form stored in target. + * + * Notes: + * Relative domain names will have 'origin' appended to them + * unless 'origin' is NULL, in which case relative domain names + * will remain relative. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(ISC_BUFFER_VALID(source)); + REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) || + (target == NULL && ISC_BUFFER_VALID(name->buffer))); + + downcase = ISC_TF((options & DNS_NAME_DOWNCASE) != 0); + + if (target == NULL && name->buffer != NULL) { + target = name->buffer; + isc_buffer_clear(target); + } + + REQUIRE(BINDABLE(name)); + + INIT_OFFSETS(name, offsets, odata); + offsets[0] = 0; + + /* + * Initialize things to make the compiler happy; they're not required. + */ + n1 = 0; + n2 = 0; + label = NULL; + digits = 0; + value = 0; + count = 0; + + /* + * Make 'name' empty in case of failure. + */ + MAKE_EMPTY(name); + + /* + * Set up the state machine. + */ + tdata = (char *)source->base + source->current; + tlen = isc_buffer_remaininglength(source); + tused = 0; + ndata = isc_buffer_used(target); + nrem = isc_buffer_availablelength(target); + if (nrem > 255) + nrem = 255; + nused = 0; + labels = 0; + done = ISC_FALSE; + state = ft_init; + + while (nrem > 0 && tlen > 0 && !done) { + c = *tdata++; + tlen--; + tused++; + + switch (state) { + case ft_init: + /* + * Is this the root name? + */ + if (c == '.') { + if (tlen != 0) + return (DNS_R_EMPTYLABEL); + labels++; + *ndata++ = 0; + nrem--; + nused++; + done = ISC_TRUE; + break; + } + if (c == '@' && tlen == 0) { + state = ft_at; + break; + } + + /* FALLTHROUGH */ + case ft_start: + label = ndata; + ndata++; + nrem--; + nused++; + count = 0; + if (c == '\\') { + state = ft_initialescape; + break; + } + state = ft_ordinary; + if (nrem == 0) + return (ISC_R_NOSPACE); + /* FALLTHROUGH */ + case ft_ordinary: + if (c == '.') { + if (count == 0) + return (DNS_R_EMPTYLABEL); + *label = count; + labels++; + INSIST(labels <= 127); + offsets[labels] = nused; + if (tlen == 0) { + labels++; + *ndata++ = 0; + nrem--; + nused++; + done = ISC_TRUE; + } + state = ft_start; + } else if (c == '\\') { + state = ft_escape; + } else { + if (count >= 63) + return (DNS_R_LABELTOOLONG); + count++; + CONVERTTOASCII(c); + if (downcase) + c = maptolower[(int)c]; + *ndata++ = c; + nrem--; + nused++; + } + break; + case ft_initialescape: + if (c == '[') { + /* + * This looks like a bitstring label, which + * was deprecated. Intentionally drop it. + */ + return (DNS_R_BADLABELTYPE); + } + state = ft_escape; + /* FALLTHROUGH */ + case ft_escape: + if (!isdigit(c & 0xff)) { + if (count >= 63) + return (DNS_R_LABELTOOLONG); + count++; + CONVERTTOASCII(c); + if (downcase) + c = maptolower[(int)c]; + *ndata++ = c; + nrem--; + nused++; + state = ft_ordinary; + break; + } + digits = 0; + value = 0; + state = ft_escdecimal; + /* FALLTHROUGH */ + case ft_escdecimal: + if (!isdigit(c & 0xff)) + return (DNS_R_BADESCAPE); + value *= 10; + value += digitvalue[(int)c]; + digits++; + if (digits == 3) { + if (value > 255) + return (DNS_R_BADESCAPE); + if (count >= 63) + return (DNS_R_LABELTOOLONG); + count++; + if (downcase) + value = maptolower[value]; + *ndata++ = value; + nrem--; + nused++; + state = ft_ordinary; + } + break; + default: + FATAL_ERROR(__FILE__, __LINE__, + "Unexpected state %d", state); + /* Does not return. */ + } + } + + if (!done) { + if (nrem == 0) + return (ISC_R_NOSPACE); + INSIST(tlen == 0); + if (state != ft_ordinary && state != ft_at) + return (ISC_R_UNEXPECTEDEND); + if (state == ft_ordinary) { + INSIST(count != 0); + *label = count; + labels++; + INSIST(labels <= 127); + offsets[labels] = nused; + } + if (origin != NULL) { + if (nrem < origin->length) + return (ISC_R_NOSPACE); + label = origin->ndata; + n1 = origin->length; + nrem -= n1; + while (n1 > 0) { + n2 = *label++; + INSIST(n2 <= 63); /* no bitstring support */ + *ndata++ = n2; + n1 -= n2 + 1; + nused += n2 + 1; + while (n2 > 0) { + c = *label++; + if (downcase) + c = maptolower[(int)c]; + *ndata++ = c; + n2--; + } + labels++; + if (n1 > 0) { + INSIST(labels <= 127); + offsets[labels] = nused; + } + } + if ((origin->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) + name->attributes |= DNS_NAMEATTR_ABSOLUTE; + } + } else + name->attributes |= DNS_NAMEATTR_ABSOLUTE; + + name->ndata = (unsigned char *)target->base + target->used; + name->labels = labels; + name->length = nused; + + isc_buffer_forward(source, tused); + isc_buffer_add(target, name->length); + + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_name_totext(dns_name_t *name, isc_boolean_t omit_final_dot, + isc_buffer_t *target) +{ + unsigned char *ndata; + char *tdata; + unsigned int nlen, tlen; + unsigned char c; + unsigned int trem, count; + unsigned int labels; + isc_boolean_t saw_root = ISC_FALSE; + + /* + * This function assumes the name is in proper uncompressed + * wire format. + */ + REQUIRE(VALID_NAME(name)); + REQUIRE(ISC_BUFFER_VALID(target)); + + ndata = name->ndata; + nlen = name->length; + labels = name->labels; + tdata = isc_buffer_used(target); + tlen = isc_buffer_availablelength(target); + + trem = tlen; + + if (labels == 0 && nlen == 0) { + /* + * Special handling for an empty name. + */ + if (trem == 0) + return (ISC_R_NOSPACE); + + /* + * The names of these booleans are misleading in this case. + * This empty name is not necessarily from the root node of + * the DNS root zone, nor is a final dot going to be included. + * They need to be set this way, though, to keep the "@" + * from being trounced. + */ + saw_root = ISC_TRUE; + omit_final_dot = ISC_FALSE; + *tdata++ = '@'; + trem--; + + /* + * Skip the while() loop. + */ + nlen = 0; + } else if (nlen == 1 && labels == 1 && *ndata == '\0') { + /* + * Special handling for the root label. + */ + if (trem == 0) + return (ISC_R_NOSPACE); + + saw_root = ISC_TRUE; + omit_final_dot = ISC_FALSE; + *tdata++ = '.'; + trem--; + + /* + * Skip the while() loop. + */ + nlen = 0; + } + + while (labels > 0 && nlen > 0 && trem > 0) { + labels--; + count = *ndata++; + nlen--; + if (count == 0) { + saw_root = ISC_TRUE; + break; + } + if (count < 64) { + INSIST(nlen >= count); + while (count > 0) { + c = *ndata; + switch (c) { + case 0x22: /* '"' */ + case 0x28: /* '(' */ + case 0x29: /* ')' */ + case 0x2E: /* '.' */ + case 0x3B: /* ';' */ + case 0x5C: /* '\\' */ + /* Special modifiers in zone files. */ + case 0x40: /* '@' */ + case 0x24: /* '$' */ + if (trem < 2) + return (ISC_R_NOSPACE); + *tdata++ = '\\'; + CONVERTFROMASCII(c); + *tdata++ = c; + ndata++; + trem -= 2; + nlen--; + break; + default: + if (c > 0x20 && c < 0x7f) { + if (trem == 0) + return (ISC_R_NOSPACE); + CONVERTFROMASCII(c); + *tdata++ = c; + ndata++; + trem--; + nlen--; + } else { + if (trem < 4) + return (ISC_R_NOSPACE); + *tdata++ = 0x5c; + *tdata++ = 0x30 + + ((c / 100) % 10); + *tdata++ = 0x30 + + ((c / 10) % 10); + *tdata++ = 0x30 + (c % 10); + trem -= 4; + ndata++; + nlen--; + } + } + count--; + } + } else { + FATAL_ERROR(__FILE__, __LINE__, + "Unexpected label type %02x", count); + /* NOTREACHED */ + } + + /* + * The following assumes names are absolute. If not, we + * fix things up later. Note that this means that in some + * cases one more byte of text buffer is required than is + * needed in the final output. + */ + if (trem == 0) + return (ISC_R_NOSPACE); + *tdata++ = '.'; + trem--; + } + + if (nlen != 0 && trem == 0) + return (ISC_R_NOSPACE); + + if (!saw_root || omit_final_dot) + trem++; + + isc_buffer_add(target, tlen - trem); + + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_name_tofilenametext(dns_name_t *name, isc_boolean_t omit_final_dot, + isc_buffer_t *target) +{ + unsigned char *ndata; + char *tdata; + unsigned int nlen, tlen; + unsigned char c; + unsigned int trem, count; + unsigned int labels; + + /* + * This function assumes the name is in proper uncompressed + * wire format. + */ + REQUIRE(VALID_NAME(name)); + REQUIRE((name->attributes & DNS_NAMEATTR_ABSOLUTE) != 0); + REQUIRE(ISC_BUFFER_VALID(target)); + + ndata = name->ndata; + nlen = name->length; + labels = name->labels; + tdata = isc_buffer_used(target); + tlen = isc_buffer_availablelength(target); + + trem = tlen; + + if (nlen == 1 && labels == 1 && *ndata == '\0') { + /* + * Special handling for the root label. + */ + if (trem == 0) + return (ISC_R_NOSPACE); + + omit_final_dot = ISC_FALSE; + *tdata++ = '.'; + trem--; + + /* + * Skip the while() loop. + */ + nlen = 0; + } + + while (labels > 0 && nlen > 0 && trem > 0) { + labels--; + count = *ndata++; + nlen--; + if (count == 0) + break; + if (count < 64) { + INSIST(nlen >= count); + while (count > 0) { + c = *ndata; + if ((c >= 0x30 && c <= 0x39) || /* digit */ + (c >= 0x41 && c <= 0x5A) || /* uppercase */ + (c >= 0x61 && c <= 0x7A) || /* lowercase */ + c == 0x2D || /* hyphen */ + c == 0x5F) /* underscore */ + { + if (trem == 0) + return (ISC_R_NOSPACE); + /* downcase */ + if (c >= 0x41 && c <= 0x5A) + c += 0x20; + CONVERTFROMASCII(c); + *tdata++ = c; + ndata++; + trem--; + nlen--; + } else { + if (trem < 3) + return (ISC_R_NOSPACE); + sprintf(tdata, "%%%02X", c); + tdata += 3; + trem -= 3; + ndata++; + nlen--; + } + count--; + } + } else { + FATAL_ERROR(__FILE__, __LINE__, + "Unexpected label type %02x", count); + /* NOTREACHED */ + } + + /* + * The following assumes names are absolute. If not, we + * fix things up later. Note that this means that in some + * cases one more byte of text buffer is required than is + * needed in the final output. + */ + if (trem == 0) + return (ISC_R_NOSPACE); + *tdata++ = '.'; + trem--; + } + + if (nlen != 0 && trem == 0) + return (ISC_R_NOSPACE); + + if (omit_final_dot) + trem++; + + isc_buffer_add(target, tlen - trem); + + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_name_downcase(dns_name_t *source, dns_name_t *name, isc_buffer_t *target) { + unsigned char *sndata, *ndata; + unsigned int nlen, count, labels; + isc_buffer_t buffer; + + /* + * Downcase 'source'. + */ + + REQUIRE(VALID_NAME(source)); + REQUIRE(VALID_NAME(name)); + if (source == name) { + REQUIRE((name->attributes & DNS_NAMEATTR_READONLY) == 0); + isc_buffer_init(&buffer, source->ndata, source->length); + target = &buffer; + ndata = source->ndata; + } else { + REQUIRE(BINDABLE(name)); + REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) || + (target == NULL && ISC_BUFFER_VALID(name->buffer))); + if (target == NULL) { + target = name->buffer; + isc_buffer_clear(name->buffer); + } + ndata = (unsigned char *)target->base + target->used; + name->ndata = ndata; + } + + sndata = source->ndata; + nlen = source->length; + labels = source->labels; + + if (nlen > (target->length - target->used)) { + MAKE_EMPTY(name); + return (ISC_R_NOSPACE); + } + + while (labels > 0 && nlen > 0) { + labels--; + count = *sndata++; + *ndata++ = count; + nlen--; + if (count < 64) { + INSIST(nlen >= count); + while (count > 0) { + *ndata++ = maptolower[(*sndata++)]; + nlen--; + count--; + } + } else { + FATAL_ERROR(__FILE__, __LINE__, + "Unexpected label type %02x", count); + /* Does not return. */ + } + } + + if (source != name) { + name->labels = source->labels; + name->length = source->length; + if ((source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) + name->attributes = DNS_NAMEATTR_ABSOLUTE; + else + name->attributes = 0; + if (name->labels > 0 && name->offsets != NULL) + set_offsets(name, name->offsets, NULL); + } + + isc_buffer_add(target, name->length); + + return (ISC_R_SUCCESS); +} + +static void +set_offsets(const dns_name_t *name, unsigned char *offsets, + dns_name_t *set_name) +{ + unsigned int offset, count, length, nlabels; + unsigned char *ndata; + isc_boolean_t absolute; + + ndata = name->ndata; + length = name->length; + offset = 0; + nlabels = 0; + absolute = ISC_FALSE; + while (offset != length) { + INSIST(nlabels < 128); + offsets[nlabels++] = offset; + count = *ndata++; + offset++; + INSIST(count <= 63); + offset += count; + ndata += count; + INSIST(offset <= length); + if (count == 0) { + absolute = ISC_TRUE; + break; + } + } + if (set_name != NULL) { + INSIST(set_name == name); + + set_name->labels = nlabels; + set_name->length = offset; + if (absolute) + set_name->attributes |= DNS_NAMEATTR_ABSOLUTE; + else + set_name->attributes &= ~DNS_NAMEATTR_ABSOLUTE; + } + INSIST(nlabels == name->labels); + INSIST(offset == name->length); +} + +isc_result_t +dns_name_fromwire(dns_name_t *name, isc_buffer_t *source, + dns_decompress_t *dctx, unsigned int options, + isc_buffer_t *target) +{ + unsigned char *cdata, *ndata; + unsigned int cused; /* Bytes of compressed name data used */ + unsigned int hops, nused, labels, n, nmax; + unsigned int current, new_current, biggest_pointer; + isc_boolean_t done; + fw_state state = fw_start; + unsigned int c; + unsigned char *offsets; + dns_offsets_t odata; + isc_boolean_t downcase; + + /* + * Copy the possibly-compressed name at source into target, + * decompressing it. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) || + (target == NULL && ISC_BUFFER_VALID(name->buffer))); + + downcase = ISC_TF((options & DNS_NAME_DOWNCASE) != 0); + + if (target == NULL && name->buffer != NULL) { + target = name->buffer; + isc_buffer_clear(target); + } + + REQUIRE(dctx != NULL); + REQUIRE(BINDABLE(name)); + + INIT_OFFSETS(name, offsets, odata); + + /* + * Make 'name' empty in case of failure. + */ + MAKE_EMPTY(name); + + /* + * Initialize things to make the compiler happy; they're not required. + */ + n = 0; + new_current = 0; + + /* + * Set up. + */ + labels = 0; + hops = 0; + done = ISC_FALSE; + + ndata = isc_buffer_used(target); + nused = 0; + + /* + * Find the maximum number of uncompressed target name + * bytes we are willing to generate. This is the smaller + * of the available target buffer length and the + * maximum legal domain name length (255). + */ + nmax = isc_buffer_availablelength(target); + if (nmax > DNS_NAME_MAXWIRE) + nmax = DNS_NAME_MAXWIRE; + + cdata = isc_buffer_current(source); + cused = 0; + + current = source->current; + biggest_pointer = current; + + /* + * Note: The following code is not optimized for speed, but + * rather for correctness. Speed will be addressed in the future. + */ + + while (current < source->active && !done) { + c = *cdata++; + current++; + if (hops == 0) + cused++; + + switch (state) { + case fw_start: + if (c < 64) { + offsets[labels] = nused; + labels++; + if (nused + c + 1 > nmax) + goto full; + nused += c + 1; + *ndata++ = c; + if (c == 0) + done = ISC_TRUE; + n = c; + state = fw_ordinary; + } else if (c >= 128 && c < 192) { + /* + * 14 bit local compression pointer. + * Local compression is no longer an + * IETF draft. + */ + return (DNS_R_BADLABELTYPE); + } else if (c >= 192) { + /* + * Ordinary 14-bit pointer. + */ + if ((dctx->allowed & DNS_COMPRESS_GLOBAL14) == + 0) + return (DNS_R_DISALLOWED); + new_current = c & 0x3F; + n = 1; + state = fw_newcurrent; + } else + return (DNS_R_BADLABELTYPE); + break; + case fw_ordinary: + if (downcase) + c = maptolower[c]; + /* FALLTHROUGH */ + case fw_copy: + *ndata++ = c; + n--; + if (n == 0) + state = fw_start; + break; + case fw_newcurrent: + new_current *= 256; + new_current += c; + n--; + if (n != 0) + break; + if (new_current >= biggest_pointer) + return (DNS_R_BADPOINTER); + biggest_pointer = new_current; + current = new_current; + cdata = (unsigned char *)source->base + + current; + hops++; + if (hops > DNS_POINTER_MAXHOPS) + return (DNS_R_TOOMANYHOPS); + state = fw_start; + break; + default: + FATAL_ERROR(__FILE__, __LINE__, + "Unknown state %d", state); + /* Does not return. */ + } + } + + if (!done) + return (ISC_R_UNEXPECTEDEND); + + name->ndata = (unsigned char *)target->base + target->used; + name->labels = labels; + name->length = nused; + name->attributes |= DNS_NAMEATTR_ABSOLUTE; + + isc_buffer_forward(source, cused); + isc_buffer_add(target, name->length); + + return (ISC_R_SUCCESS); + + full: + if (nmax == DNS_NAME_MAXWIRE) + /* + * The name did not fit even though we had a buffer + * big enough to fit a maximum-length name. + */ + return (DNS_R_NAMETOOLONG); + else + /* + * The name might fit if only the caller could give us a + * big enough buffer. + */ + return (ISC_R_NOSPACE); + +} + +isc_result_t +dns_name_towire(dns_name_t *name, dns_compress_t *cctx, isc_buffer_t *target) { + unsigned int methods; + isc_uint16_t offset; + dns_name_t gp; /* Global compression prefix */ + isc_boolean_t gf; /* Global compression target found */ + isc_uint16_t go; /* Global compression offset */ + dns_offsets_t clo; + dns_name_t clname; + + /* + * Convert 'name' into wire format, compressing it as specified by the + * compression context 'cctx', and storing the result in 'target'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(cctx != NULL); + REQUIRE(ISC_BUFFER_VALID(target)); + + /* + * If 'name' doesn't have an offsets table, make a clone which + * has one. + */ + if (name->offsets == NULL) { + DNS_NAME_INIT(&clname, clo); + dns_name_clone(name, &clname); + name = &clname; + } + DNS_NAME_INIT(&gp, NULL); + + offset = target->used; /*XXX*/ + + methods = dns_compress_getmethods(cctx); + + if ((methods & DNS_COMPRESS_GLOBAL14) != 0) + gf = dns_compress_findglobal(cctx, name, &gp, &go); + else + gf = ISC_FALSE; + + /* + * If the offset is too high for 14 bit global compression, we're + * out of luck. + */ + if (gf && go >= 0x4000) + gf = ISC_FALSE; + + /* + * Will the compression pointer reduce the message size? + */ + if (gf && (gp.length + 2) >= name->length) + gf = ISC_FALSE; + + if (gf) { + if (target->length - target->used < gp.length) + return (ISC_R_NOSPACE); + (void)memcpy((unsigned char *)target->base + target->used, + gp.ndata, (size_t)gp.length); + isc_buffer_add(target, gp.length); + go |= 0xc000; + if (target->length - target->used < 2) + return (ISC_R_NOSPACE); + isc_buffer_putuint16(target, go); + if (gp.length != 0) + dns_compress_add(cctx, name, &gp, offset); + } else { + if (target->length - target->used < name->length) + return (ISC_R_NOSPACE); + (void)memcpy((unsigned char *)target->base + target->used, + name->ndata, (size_t)name->length); + isc_buffer_add(target, name->length); + dns_compress_add(cctx, name, name, offset); + } + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_name_concatenate(dns_name_t *prefix, dns_name_t *suffix, dns_name_t *name, + isc_buffer_t *target) +{ + unsigned char *ndata, *offsets; + unsigned int nrem, labels, prefix_length, length; + isc_boolean_t copy_prefix = ISC_TRUE; + isc_boolean_t copy_suffix = ISC_TRUE; + isc_boolean_t absolute = ISC_FALSE; + dns_name_t tmp_name; + dns_offsets_t odata; + + /* + * Concatenate 'prefix' and 'suffix'. + */ + + REQUIRE(prefix == NULL || VALID_NAME(prefix)); + REQUIRE(suffix == NULL || VALID_NAME(suffix)); + REQUIRE(name == NULL || VALID_NAME(name)); + REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) || + (target == NULL && name != NULL && ISC_BUFFER_VALID(name->buffer))); + if (prefix == NULL || prefix->labels == 0) + copy_prefix = ISC_FALSE; + if (suffix == NULL || suffix->labels == 0) + copy_suffix = ISC_FALSE; + if (copy_prefix && + (prefix->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) { + absolute = ISC_TRUE; + REQUIRE(!copy_suffix); + } + if (name == NULL) { + DNS_NAME_INIT(&tmp_name, odata); + name = &tmp_name; + } + if (target == NULL) { + INSIST(name->buffer != NULL); + target = name->buffer; + isc_buffer_clear(name->buffer); + } + + REQUIRE(BINDABLE(name)); + + /* + * Set up. + */ + nrem = target->length - target->used; + ndata = (unsigned char *)target->base + target->used; + if (nrem > DNS_NAME_MAXWIRE) + nrem = DNS_NAME_MAXWIRE; + length = 0; + prefix_length = 0; + labels = 0; + if (copy_prefix) { + prefix_length = prefix->length; + length += prefix_length; + labels += prefix->labels; + } + if (copy_suffix) { + length += suffix->length; + labels += suffix->labels; + } + if (length > DNS_NAME_MAXWIRE) { + MAKE_EMPTY(name); + return (DNS_R_NAMETOOLONG); + } + if (length > nrem) { + MAKE_EMPTY(name); + return (ISC_R_NOSPACE); + } + + if (copy_suffix) { + if ((suffix->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) + absolute = ISC_TRUE; + if (suffix == name && suffix->buffer == target) + memmove(ndata + prefix_length, suffix->ndata, + suffix->length); + else + memcpy(ndata + prefix_length, suffix->ndata, + suffix->length); + } + + /* + * If 'prefix' and 'name' are the same object, and the object has + * a dedicated buffer, and we're using it, then we don't have to + * copy anything. + */ + if (copy_prefix && (prefix != name || prefix->buffer != target)) + memcpy(ndata, prefix->ndata, prefix_length); + + name->ndata = ndata; + name->labels = labels; + name->length = length; + if (absolute) + name->attributes = DNS_NAMEATTR_ABSOLUTE; + else + name->attributes = 0; + + if (name->labels > 0 && name->offsets != NULL) { + INIT_OFFSETS(name, offsets, odata); + set_offsets(name, offsets, NULL); + } + + isc_buffer_add(target, name->length); + + return (ISC_R_SUCCESS); +} + +void +dns_name_split(dns_name_t *name, unsigned int suffixlabels, + dns_name_t *prefix, dns_name_t *suffix) + +{ + unsigned int splitlabel; + + REQUIRE(VALID_NAME(name)); + REQUIRE(suffixlabels > 0); + REQUIRE(suffixlabels < name->labels); + REQUIRE(prefix != NULL || suffix != NULL); + REQUIRE(prefix == NULL || + (VALID_NAME(prefix) && + prefix->buffer != NULL && + BINDABLE(prefix))); + REQUIRE(suffix == NULL || + (VALID_NAME(suffix) && + suffix->buffer != NULL && + BINDABLE(suffix))); + + splitlabel = name->labels - suffixlabels; + + if (prefix != NULL) + dns_name_getlabelsequence(name, 0, splitlabel, prefix); + + if (suffix != NULL) + dns_name_getlabelsequence(name, splitlabel, + suffixlabels, suffix); + + return; +} + +isc_result_t +dns_name_dup(dns_name_t *source, isc_mem_t *mctx, dns_name_t *target) { + /* + * Make 'target' a dynamically allocated copy of 'source'. + */ + + REQUIRE(VALID_NAME(source)); + REQUIRE(source->length > 0); + REQUIRE(VALID_NAME(target)); + REQUIRE(BINDABLE(target)); + + /* + * Make 'target' empty in case of failure. + */ + MAKE_EMPTY(target); + + target->ndata = isc_mem_get(mctx, source->length); + if (target->ndata == NULL) + return (ISC_R_NOMEMORY); + + memcpy(target->ndata, source->ndata, source->length); + + target->length = source->length; + target->labels = source->labels; + target->attributes = DNS_NAMEATTR_DYNAMIC; + if ((source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) + target->attributes |= DNS_NAMEATTR_ABSOLUTE; + if (target->offsets != NULL) { + if (source->offsets != NULL) + memcpy(target->offsets, source->offsets, + source->labels); + else + set_offsets(target, target->offsets, NULL); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_name_dupwithoffsets(dns_name_t *source, isc_mem_t *mctx, + dns_name_t *target) +{ + /* + * Make 'target' a read-only dynamically allocated copy of 'source'. + * 'target' will also have a dynamically allocated offsets table. + */ + + REQUIRE(VALID_NAME(source)); + REQUIRE(source->length > 0); + REQUIRE(VALID_NAME(target)); + REQUIRE(BINDABLE(target)); + REQUIRE(target->offsets == NULL); + + /* + * Make 'target' empty in case of failure. + */ + MAKE_EMPTY(target); + + target->ndata = isc_mem_get(mctx, source->length + source->labels); + if (target->ndata == NULL) + return (ISC_R_NOMEMORY); + + memcpy(target->ndata, source->ndata, source->length); + + target->length = source->length; + target->labels = source->labels; + target->attributes = DNS_NAMEATTR_DYNAMIC | DNS_NAMEATTR_DYNOFFSETS | + DNS_NAMEATTR_READONLY; + if ((source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) + target->attributes |= DNS_NAMEATTR_ABSOLUTE; + target->offsets = target->ndata + source->length; + if (source->offsets != NULL) + memcpy(target->offsets, source->offsets, source->labels); + else + set_offsets(target, target->offsets, NULL); + + return (ISC_R_SUCCESS); +} + +void +dns_name_free(dns_name_t *name, isc_mem_t *mctx) { + size_t size; + + /* + * Free 'name'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE((name->attributes & DNS_NAMEATTR_DYNAMIC) != 0); + + size = name->length; + if ((name->attributes & DNS_NAMEATTR_DYNOFFSETS) != 0) + size += name->labels; + isc_mem_put(mctx, name->ndata, size); + dns_name_invalidate(name); +} + +isc_result_t +dns_name_digest(dns_name_t *name, dns_digestfunc_t digest, void *arg) { + dns_name_t downname; + unsigned char data[256]; + isc_buffer_t buffer; + isc_result_t result; + isc_region_t r; + + /* + * Send 'name' in DNSSEC canonical form to 'digest'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(digest != NULL); + + DNS_NAME_INIT(&downname, NULL); + isc_buffer_init(&buffer, data, sizeof(data)); + + result = dns_name_downcase(name, &downname, &buffer); + if (result != ISC_R_SUCCESS) + return (result); + + isc_buffer_usedregion(&buffer, &r); + + return ((digest)(arg, &r)); +} + +isc_boolean_t +dns_name_dynamic(dns_name_t *name) { + REQUIRE(VALID_NAME(name)); + + /* + * Returns whether there is dynamic memory associated with this name. + */ + + return ((name->attributes & DNS_NAMEATTR_DYNAMIC) != 0 ? + ISC_TRUE : ISC_FALSE); +} + +isc_result_t +dns_name_print(dns_name_t *name, FILE *stream) { + isc_result_t result; + isc_buffer_t b; + isc_region_t r; + char t[1024]; + + /* + * Print 'name' on 'stream'. + */ + + REQUIRE(VALID_NAME(name)); + + isc_buffer_init(&b, t, sizeof(t)); + result = dns_name_totext(name, ISC_FALSE, &b); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_usedregion(&b, &r); + fprintf(stream, "%.*s", (int)r.length, (char *)r.base); + + return (ISC_R_SUCCESS); +} + +void +dns_name_format(dns_name_t *name, char *cp, unsigned int size) { + isc_result_t result; + isc_buffer_t buf; + + REQUIRE(size > 0); + + /* + * Leave room for null termination after buffer. + */ + isc_buffer_init(&buf, cp, size - 1); + result = dns_name_totext(name, ISC_TRUE, &buf); + if (result == ISC_R_SUCCESS) { + /* + * Null terminate. + */ + isc_region_t r; + isc_buffer_usedregion(&buf, &r); + ((char *) r.base)[r.length] = '\0'; + + } else + snprintf(cp, size, ""); +} + +isc_result_t +dns_name_copy(dns_name_t *source, dns_name_t *dest, isc_buffer_t *target) { + unsigned char *ndata; + + /* + * Make dest a copy of source. + */ + + REQUIRE(VALID_NAME(source)); + REQUIRE(VALID_NAME(dest)); + REQUIRE(target != NULL || dest->buffer != NULL); + + if (target == NULL) { + target = dest->buffer; + isc_buffer_clear(dest->buffer); + } + + REQUIRE(BINDABLE(dest)); + + /* + * Set up. + */ + if (target->length - target->used < source->length) + return (ISC_R_NOSPACE); + + ndata = (unsigned char *)target->base + target->used; + dest->ndata = target->base; + + memcpy(ndata, source->ndata, source->length); + + dest->ndata = ndata; + dest->labels = source->labels; + dest->length = source->length; + if ((source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) + dest->attributes = DNS_NAMEATTR_ABSOLUTE; + else + dest->attributes = 0; + + if (dest->labels > 0 && dest->offsets != NULL) { + if (source->offsets != NULL) + memcpy(dest->offsets, source->offsets, source->labels); + else + set_offsets(dest, dest->offsets, NULL); + } + + isc_buffer_add(target, dest->length); + + return (ISC_R_SUCCESS); +} + diff -Nuar bind-9.3.2-orig/lib/dns/sdlz.c bind-9.3.2-mod/lib/dns/sdlz.c --- bind-9.3.2-orig/lib/dns/sdlz.c 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/sdlz.c 2006-02-20 19:38:59.000000000 +0100 @@ -0,0 +1,1799 @@ +/* + * 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 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#ifdef DLZ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rdatalist_p.h" + +/*** + *** Private Types + ***/ + +struct dns_sdlzimplementation { + const dns_sdlzmethods_t *methods; + isc_mem_t *mctx; + void *driverarg; + unsigned int flags; + isc_mutex_t driverlock; + dns_dlzimplementation_t *dlz_imp; +}; + +struct dns_sdlz_db { + /* Unlocked */ + dns_db_t common; + void *dbdata; + dns_sdlzimplementation_t *dlzimp; + isc_mutex_t refcnt_lock; + /* Locked */ + unsigned int references; +}; + +struct dns_sdlzlookup { + /* Unlocked */ + unsigned int magic; + dns_sdlz_db_t *sdlz; + ISC_LIST(dns_rdatalist_t) lists; + ISC_LIST(isc_buffer_t) buffers; + dns_name_t *name; + ISC_LINK(dns_sdlzlookup_t) link; + isc_mutex_t lock; + dns_rdatacallbacks_t callbacks; + /* Locked */ + unsigned int references; +}; + +typedef struct dns_sdlzlookup dns_sdlznode_t; + +struct dns_sdlzallnodes { + dns_dbiterator_t common; + ISC_LIST(dns_sdlznode_t) nodelist; + dns_sdlznode_t *current; + dns_sdlznode_t *origin; +}; + +typedef dns_sdlzallnodes_t sdlz_dbiterator_t; + +typedef struct sdlz_rdatasetiter { + dns_rdatasetiter_t common; + dns_rdatalist_t *current; +} sdlz_rdatasetiter_t; + + +#define SDLZDB_MAGIC ISC_MAGIC('D', 'L', 'Z', 'S') + +/* + * Note that "impmagic" is not the first four bytes of the struct, so + * ISC_MAGIC_VALID cannot be used. + */ + +#define VALID_SDLZDB(sdlzdb) ((sdlzdb) != NULL && \ + (sdlzdb)->common.impmagic == SDLZDB_MAGIC) + +#define SDLZLOOKUP_MAGIC ISC_MAGIC('D','L','Z','L') +#define VALID_SDLZLOOKUP(sdlzl) ISC_MAGIC_VALID(sdlzl, SDLZLOOKUP_MAGIC) +#define VALID_SDLZNODE(sdlzn) VALID_SDLZLOOKUP(sdlzn) + +/* These values are taken from RFC 1537 */ +#define SDLZ_DEFAULT_REFRESH (60 * 60 * 8) +#define SDLZ_DEFAULT_RETRY (60 * 60 * 2) +#define SDLZ_DEFAULT_EXPIRE (60 * 60 * 24 * 7) +#define SDLZ_DEFAULT_MINIMUM (60 * 60 * 24) + +/* This is a reasonable value */ +#define SDLZ_DEFAULT_TTL (60 * 60 * 24) + +void dns_sdlz_tolower(char *str); + +isc_result_t +dns_sdlzcreateDBP(isc_mem_t *mctx, void *driverarg, void *dbdata, + dns_name_t *name, dns_rdataclass_t rdclass, dns_db_t **dbp); + +isc_result_t +dns_sdlzallowzonexfr(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, + isc_sockaddr_t *clientaddr, dns_db_t **dbp); + +isc_result_t +dns_sdlzcreate(isc_mem_t *mctx, const char *dlzname, unsigned int argc, + char *argv[], void *driverarg, void **dbdata); + +void +dns_sdlzdestroy(void *driverdata, void **dbdata); + +isc_result_t +dns_sdlzfindzone(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, dns_db_t **dbp); + +static int dummy; + +static isc_result_t findrdataset(dns_db_t *db, dns_dbnode_t *node, + dns_dbversion_t *version, + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_stdtime_t now, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset); + +static isc_result_t createnode(dns_sdlz_db_t *sdlz, dns_sdlznode_t **nodep); + +static void destroynode(dns_sdlznode_t *node); + +static void detachnode(dns_db_t *db, dns_dbnode_t **targetp); + + +static void list_tordataset(dns_rdatalist_t *rdatalist, + dns_db_t *db, dns_dbnode_t *node, + dns_rdataset_t *rdataset); + +static void dbiterator_destroy(dns_dbiterator_t **iteratorp); +static isc_result_t dbiterator_first(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_last(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, + dns_name_t *name); +static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_next(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_current(dns_dbiterator_t *iterator, + dns_dbnode_t **nodep, + dns_name_t *name); +static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, + dns_name_t *name); + +static dns_dbiteratormethods_t dbiterator_methods = { + dbiterator_destroy, + dbiterator_first, + dbiterator_last, + dbiterator_seek, + dbiterator_prev, + dbiterator_next, + dbiterator_current, + dbiterator_pause, + dbiterator_origin +}; + +static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp); +static isc_result_t rdatasetiter_first(dns_rdatasetiter_t *iterator); +static isc_result_t rdatasetiter_next(dns_rdatasetiter_t *iterator); +static void rdatasetiter_current(dns_rdatasetiter_t *iterator, + dns_rdataset_t *rdataset); + +static dns_rdatasetitermethods_t rdatasetiter_methods = { + rdatasetiter_destroy, + rdatasetiter_first, + rdatasetiter_next, + rdatasetiter_current +}; + +#define MAYBE_LOCK(imp) \ + do { \ + unsigned int flags = imp->flags; \ + if((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \ + LOCK(&imp->driverlock); \ + } while(0) + +#define MAYBE_UNLOCK(imp) \ + do { \ + unsigned int flags = imp->flags; \ + if((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \ + UNLOCK(&imp->driverlock); \ + } while(0) + +/*** + *** Functions + ***/ + + /* Converts the input string to lowercase, in place. */ + +void +dns_sdlz_tolower(char *str) { + + unsigned int len = strlen(str); + unsigned int i; + + for(i = 0; i < len; i++){ + if(str[i] >= 'A' && str[i] <= 'Z') + str[i] += 32; + } + +} + +static inline unsigned int +initial_size(const char *data) { +/* unsigned int len = strlen(data); + unsigned int size; + for (size = 64; size < (64 * 1024); size *= 2) + if (len < size) + return (size); + return (64 * 1024); +*/ + unsigned int len = (strlen(data) / 64) + 1; + return len * 64 + 64; +} + +isc_result_t +dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl, + const char *data) +{ + dns_rdatalist_t *rdatalist; + dns_rdata_t *rdata; + dns_rdatatype_t typeval; + isc_consttextregion_t r; + isc_buffer_t b; + isc_buffer_t *rdatabuf; + isc_lex_t *lex; + isc_result_t result; + unsigned int size; + isc_mem_t *mctx; + dns_name_t *origin; + + REQUIRE(VALID_SDLZLOOKUP(lookup)); + REQUIRE(type != NULL); + REQUIRE(data != NULL); + + mctx = lookup->sdlz->common.mctx; + + r.base = type; + r.length = strlen(type); + result = dns_rdatatype_fromtext(&typeval, (isc_textregion_t *)&r); + if (result != ISC_R_SUCCESS) + return (result); + + rdatalist = ISC_LIST_HEAD(lookup->lists); + while (rdatalist != NULL) { + if (rdatalist->type == typeval) + break; + rdatalist = ISC_LIST_NEXT(rdatalist, link); + } + + if (rdatalist == NULL) { + rdatalist = isc_mem_get(mctx, sizeof(dns_rdatalist_t)); + if (rdatalist == NULL) + return (ISC_R_NOMEMORY); + rdatalist->rdclass = lookup->sdlz->common.rdclass; + rdatalist->type = typeval; + rdatalist->covers = 0; + rdatalist->ttl = ttl; + ISC_LIST_INIT(rdatalist->rdata); + ISC_LINK_INIT(rdatalist, link); + ISC_LIST_APPEND(lookup->lists, rdatalist, link); + } else + if (rdatalist->ttl != ttl) + return (DNS_R_BADTTL); + + rdata = isc_mem_get(mctx, sizeof(dns_rdata_t)); + if (rdata == NULL) + return (ISC_R_NOMEMORY); + dns_rdata_init(rdata); + + if ((lookup->sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0) + origin = &lookup->sdlz->common.origin; + else + origin = dns_rootname; + + lex = NULL; + result = isc_lex_create(mctx, 64, &lex); + if (result != ISC_R_SUCCESS) + goto failure; + + size = initial_size(data); + do { + isc_buffer_init(&b, data, strlen(data)); + isc_buffer_add(&b, strlen(data)); + + result = isc_lex_openbuffer(lex, &b); + if (result != ISC_R_SUCCESS) + goto failure; + + rdatabuf = NULL; + result = isc_buffer_allocate(mctx, &rdatabuf, size); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_rdata_fromtext(rdata, rdatalist->rdclass, + rdatalist->type, lex, + origin, ISC_FALSE, + mctx, rdatabuf, + &lookup->callbacks); + if (result != ISC_R_SUCCESS) + isc_buffer_free(&rdatabuf); + size *= 2; + } while (result == ISC_R_NOSPACE); + + if (result != ISC_R_SUCCESS) + goto failure; + + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + ISC_LIST_APPEND(lookup->buffers, rdatabuf, link); + + if (lex != NULL) + isc_lex_destroy(&lex); + + return (ISC_R_SUCCESS); + + failure: + + if (rdatabuf != NULL) + isc_buffer_free(&rdatabuf); + if (lex != NULL) + isc_lex_destroy(&lex); + isc_mem_put(mctx, rdata, sizeof(dns_rdata_t)); + + return (result); +} + +isc_result_t +dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name, + const char *type, dns_ttl_t ttl, const char *data) +{ + dns_name_t *newname, *origin; + dns_fixedname_t fnewname; + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)allnodes->common.db; + dns_sdlznode_t *sdlznode; + isc_mem_t *mctx = sdlz->common.mctx; + isc_buffer_t b; + isc_result_t result; + + dns_fixedname_init(&fnewname); + newname = dns_fixedname_name(&fnewname); + + if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0) + origin = &sdlz->common.origin; + else + origin = dns_rootname; + isc_buffer_init(&b, name, strlen(name)); + isc_buffer_add(&b, strlen(name)); + + result = dns_name_fromtext(newname, &b, origin, ISC_FALSE, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + if (allnodes->common.relative_names) { + /* All names are relative to the root */ + unsigned int nlabels = dns_name_countlabels(newname); + dns_name_getlabelsequence(newname, 0, nlabels - 1, newname); + } + + sdlznode = ISC_LIST_HEAD(allnodes->nodelist); + if (sdlznode == NULL || !dns_name_equal(sdlznode->name, newname)) { + sdlznode = NULL; + result = createnode(sdlz, &sdlznode); + if (result != ISC_R_SUCCESS) + return (result); + sdlznode->name = isc_mem_get(mctx, sizeof(dns_name_t)); + if (sdlznode->name == NULL) { + destroynode(sdlznode); + return (ISC_R_NOMEMORY); + } + dns_name_init(sdlznode->name, NULL); + result = dns_name_dup(newname, mctx, sdlznode->name); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, sdlznode->name, sizeof(dns_name_t)); + destroynode(sdlznode); + return (result); + } + ISC_LIST_PREPEND(allnodes->nodelist, sdlznode, link); + if (allnodes->origin == NULL && + dns_name_equal(newname, &sdlz->common.origin)) + allnodes->origin = sdlznode; + } + return (dns_sdlz_putrr(sdlznode, type, ttl, data)); + +} + +isc_result_t +dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname, + isc_uint32_t serial) +{ + char str[2 * DNS_NAME_MAXTEXT + 5 * (sizeof("2147483647")) + 7]; + int n; + + REQUIRE(mname != NULL); + REQUIRE(rname != NULL); + + n = snprintf(str, sizeof str, "%s %s %u %u %u %u %u", + mname, rname, serial, + SDLZ_DEFAULT_REFRESH, SDLZ_DEFAULT_RETRY, + SDLZ_DEFAULT_EXPIRE, SDLZ_DEFAULT_MINIMUM); + if (n >= (int)sizeof(str) || n < 0) + return (ISC_R_NOSPACE); + return (dns_sdlz_putrr(lookup, "SOA", SDLZ_DEFAULT_TTL, str)); +} + +/* + * DB routines. These methods were "borrowed" from the SDB driver interface. + * See the SDB driver interface documentation for more info. + */ + +static void +attach(dns_db_t *source, dns_db_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *) source; + + REQUIRE(VALID_SDLZDB(sdlz)); + + LOCK(&sdlz->refcnt_lock); + REQUIRE(sdlz->references > 0); + sdlz->references++; + UNLOCK(&sdlz->refcnt_lock); + + *targetp = source; +} + +static void +destroy(dns_sdlz_db_t *sdlz) { + isc_mem_t *mctx; + mctx = sdlz->common.mctx; + + sdlz->common.magic = 0; + sdlz->common.impmagic = 0; + + isc_mutex_destroy(&sdlz->refcnt_lock); + + dns_name_free(&sdlz->common.origin, mctx); + + isc_mem_put(mctx, sdlz, sizeof(dns_sdlz_db_t)); + isc_mem_detach(&mctx); +} + +static void +detach(dns_db_t **dbp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)(*dbp); + isc_boolean_t need_destroy = ISC_FALSE; + + REQUIRE(VALID_SDLZDB(sdlz)); + LOCK(&sdlz->refcnt_lock); + REQUIRE(sdlz->references > 0); + sdlz->references--; + if (sdlz->references == 0) + need_destroy = ISC_TRUE; + UNLOCK(&sdlz->refcnt_lock); + + if (need_destroy) + destroy(sdlz); + + *dbp = NULL; +} + +static isc_result_t +beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp, dns_dbload_t **dbloadp) { + UNUSED(db); + UNUSED(addp); + UNUSED(dbloadp); + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +endload(dns_db_t *db, dns_dbload_t **dbloadp) { + UNUSED(db); + UNUSED(dbloadp); + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +dump(dns_db_t *db, dns_dbversion_t *version, const char *filename) { + UNUSED(db); + UNUSED(version); + UNUSED(filename); + return (ISC_R_NOTIMPLEMENTED); +} + +static void +currentversion(dns_db_t *db, dns_dbversion_t **versionp) { + REQUIRE(versionp != NULL && *versionp == NULL); + + UNUSED(db); + + *versionp = (void *) &dummy; + return; +} + +static isc_result_t +newversion(dns_db_t *db, dns_dbversion_t **versionp) { + UNUSED(db); + UNUSED(versionp); + + return (ISC_R_NOTIMPLEMENTED); +} + +static void +attachversion(dns_db_t *db, dns_dbversion_t *source, + dns_dbversion_t **targetp) +{ + REQUIRE(source != NULL && source == (void *) &dummy); + + UNUSED(db); + UNUSED(source); + UNUSED(targetp); + *targetp = source; + return; +} + +static void +closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { + REQUIRE(versionp != NULL && *versionp == (void *) &dummy); + REQUIRE(commit == ISC_FALSE); + + UNUSED(db); + UNUSED(commit); + + *versionp = NULL; +} + +static isc_result_t +createnode(dns_sdlz_db_t *sdlz, dns_sdlznode_t **nodep) { + dns_sdlznode_t *node; + isc_result_t result; + + node = isc_mem_get(sdlz->common.mctx, sizeof(dns_sdlznode_t)); + if (node == NULL) + return (ISC_R_NOMEMORY); + + node->sdlz = NULL; + attach((dns_db_t *)sdlz, (dns_db_t **)&node->sdlz); + ISC_LIST_INIT(node->lists); + ISC_LIST_INIT(node->buffers); + ISC_LINK_INIT(node, link); + node->name = NULL; + result = isc_mutex_init(&node->lock); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + isc_mem_put(sdlz->common.mctx, node, sizeof(dns_sdlznode_t)); + return (ISC_R_UNEXPECTED); + } + dns_rdatacallbacks_init(&node->callbacks); + node->references = 1; + node->magic = SDLZLOOKUP_MAGIC; + + *nodep = node; + return (ISC_R_SUCCESS); +} + +static void +destroynode(dns_sdlznode_t *node) { + dns_rdatalist_t *list; + dns_rdata_t *rdata; + isc_buffer_t *b; + dns_sdlz_db_t *sdlz; + isc_mem_t *mctx; + + sdlz = node->sdlz; + mctx = sdlz->common.mctx; + + while (!ISC_LIST_EMPTY(node->lists)) { + list = ISC_LIST_HEAD(node->lists); + while (!ISC_LIST_EMPTY(list->rdata)) { + rdata = ISC_LIST_HEAD(list->rdata); + ISC_LIST_UNLINK(list->rdata, rdata, link); + isc_mem_put(mctx, rdata, sizeof(dns_rdata_t)); + } + ISC_LIST_UNLINK(node->lists, list, link); + isc_mem_put(mctx, list, sizeof(dns_rdatalist_t)); + } + + while (!ISC_LIST_EMPTY(node->buffers)) { + b = ISC_LIST_HEAD(node->buffers); + ISC_LIST_UNLINK(node->buffers, b, link); + isc_buffer_free(&b); + } + + if (node->name != NULL) { + dns_name_free(node->name, mctx); + isc_mem_put(mctx, node->name, sizeof(dns_name_t)); + } + DESTROYLOCK(&node->lock); + node->magic = 0; + isc_mem_put(mctx, node, sizeof(dns_sdlznode_t)); + detach((dns_db_t **)&sdlz); +} + +static isc_result_t +findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, + dns_dbnode_t **nodep) +{ + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node = NULL; + isc_result_t result; + isc_buffer_t b; + char namestr[DNS_NAME_MAXTEXT + 3]; + isc_buffer_t b2; + char zonestr[DNS_NAME_MAXTEXT + 1]; + isc_boolean_t isorigin; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(create == ISC_FALSE); + REQUIRE(nodep != NULL && *nodep == NULL); + + UNUSED(name); + UNUSED(create); + + isc_buffer_init(&b, namestr, sizeof(namestr)); + if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVEOWNER) != 0) { + dns_name_t relname; + unsigned int labels; + + labels = dns_name_countlabels(name) - + dns_name_countlabels(&db->origin); + dns_name_init(&relname, NULL); + dns_name_getlabelsequence(name, 0, labels, &relname); + result = dns_name_totext(&relname, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return (result); + } else { + result = dns_name_totext(name, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return (result); + } + isc_buffer_putuint8(&b, 0); + + isc_buffer_init(&b2, zonestr, sizeof(zonestr)); + result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b2); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_putuint8(&b2, 0); + + result = createnode(sdlz, &node); + if (result != ISC_R_SUCCESS) + return (result); + + isorigin = dns_name_equal(name, &sdlz->common.origin); + + // make sure strings are always lowercase + dns_sdlz_tolower(zonestr); + dns_sdlz_tolower(namestr); + + MAYBE_LOCK(sdlz->dlzimp); + + // try to lookup the host (namestr) + result = sdlz->dlzimp->methods->lookup(zonestr, namestr, + sdlz->dlzimp->driverarg, sdlz->dbdata, node); + + // if the host (namestr) was not found, and there are multiple labels, try finding + // a best-fit wildcard + if((result != ISC_R_SUCCESS) && ((dns_name_countlabels(name) > (dns_name_countlabels(&db->origin)+1)))) { + unsigned int i; + unsigned int labels, endlabel; + + if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVEOWNER) != 0) { + labels = dns_name_countlabels(name) - dns_name_countlabels(&db->origin); + endlabel = labels; + } else { + labels = dns_name_countlabels(name); + endlabel = dns_name_countlabels(name) - dns_name_countlabels(&db->origin); + } + + // XXX: this isn't a full search + for(i = (endlabel > 5) ? (endlabel - 5) : 1; + (i < endlabel) && (result != ISC_R_SUCCESS); + i++) + { + dns_name_t tname; + + dns_name_init(&tname, NULL); + dns_name_getlabelsequence(name, i, labels - i, &tname); + isc_buffer_init(&b, namestr, sizeof(namestr)); + isc_buffer_putstr(&b,"*."); + result = dns_name_totext(&tname, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) { + MAYBE_UNLOCK(sdlz->dlzimp); + destroynode(node); + return (result); + } + isc_buffer_putuint8(&b, 0); + result = sdlz->dlzimp->methods->lookup(zonestr, namestr, + sdlz->dlzimp->driverarg, sdlz->dbdata, node); + } + } + + // if the host (namestr) was not found, try to lookup the default "wildcard" host. + if(result != ISC_R_SUCCESS){ + result = sdlz->dlzimp->methods->lookup(zonestr, "*", + sdlz->dlzimp->driverarg, sdlz->dbdata, node); + } + + MAYBE_UNLOCK(sdlz->dlzimp); + + if (result != ISC_R_SUCCESS && !isorigin) { + destroynode(node); + return (result); + } + + if (isorigin && sdlz->dlzimp->methods->authority != NULL) { + MAYBE_LOCK(sdlz->dlzimp); + result = sdlz->dlzimp->methods->authority(zonestr, + sdlz->dlzimp->driverarg, sdlz->dbdata, node); + MAYBE_UNLOCK(sdlz->dlzimp); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) { + destroynode(node); + return (result); + } + } + + *nodep = node; + return (ISC_R_SUCCESS); +} + +static isc_result_t +find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_dbnode_t *node = NULL; + dns_fixedname_t fname; + dns_rdataset_t xrdataset; + dns_name_t *xname; + unsigned int nlabels, olabels; + isc_result_t result; + unsigned int i; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(nodep == NULL || *nodep == NULL); + REQUIRE(version == NULL || version == (void *) &dummy); + + UNUSED(options); + UNUSED(sdlz); + + if (!dns_name_issubdomain(name, &db->origin)) + return (DNS_R_NXDOMAIN); + + olabels = dns_name_countlabels(&db->origin); + nlabels = dns_name_countlabels(name); + + dns_fixedname_init(&fname); + xname = dns_fixedname_name(&fname); + + if (rdataset == NULL) + rdataset = &xrdataset; + + result = DNS_R_NXDOMAIN; + + for (i = olabels; i <= nlabels; i++) { + /* + * Unless this is an explicit lookup at the origin, don't + * look at the origin. + */ + if (i == olabels && i != nlabels) + continue; + + /* + * Look up the next label. + */ + dns_name_getlabelsequence(name, nlabels - i, i, xname); + result = findnode(db, xname, ISC_FALSE, &node); + if (result != ISC_R_SUCCESS) { + result = DNS_R_NXDOMAIN; + continue; + } + + /* + * Look for a DNAME at the current label, unless this is + * the qname. + */ + if (i < nlabels) { + result = findrdataset(db, node, version, + dns_rdatatype_dname, + 0, now, rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + result = DNS_R_DNAME; + break; + } + } + + /* + * Look for an NS at the current label, unless this is the + * origin or glue is ok. + */ + if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0) { + result = findrdataset(db, node, version, + dns_rdatatype_ns, + 0, now, rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + if (i == nlabels && type == dns_rdatatype_any) + { + result = DNS_R_ZONECUT; + dns_rdataset_disassociate(rdataset); + if (sigrdataset != NULL) + dns_rdataset_disassociate + (sigrdataset); + } else + result = DNS_R_DELEGATION; + break; + } + } + + /* + * If the current name is not the qname, add another label + * and try again. + */ + if (i < nlabels) { + destroynode(node); + node = NULL; + continue; + } + + /* + * If we're looking for ANY, we're done. + */ + if (type == dns_rdatatype_any) { + result = ISC_R_SUCCESS; + break; + } + + /* + * Look for the qtype. + */ + result = findrdataset(db, node, version, type, + 0, now, rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) + break; + + /* + * Look for a CNAME + */ + if (type != dns_rdatatype_cname) { + result = findrdataset(db, node, version, + dns_rdatatype_cname, + 0, now, rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + result = DNS_R_CNAME; + break; + } + } + + result = DNS_R_NXRRSET; + break; + } + + if (rdataset == &xrdataset && dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + + if (foundname != NULL) { + isc_result_t xresult; + + xresult = dns_name_copy(xname, foundname, NULL); + if (xresult != ISC_R_SUCCESS) { + destroynode(node); + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + return (DNS_R_BADDB); + } + } + + if (nodep != NULL) + *nodep = node; + else if (node != NULL) + detachnode(db, &node); + + return (result); +} + +static isc_result_t +findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, + isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + UNUSED(db); + UNUSED(name); + UNUSED(options); + UNUSED(now); + UNUSED(nodep); + UNUSED(foundname); + UNUSED(rdataset); + UNUSED(sigrdataset); + + return (ISC_R_NOTIMPLEMENTED); +} + +static void +attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node = (dns_sdlznode_t *)source; + + REQUIRE(VALID_SDLZDB(sdlz)); + + UNUSED(sdlz); + + LOCK(&node->lock); + INSIST(node->references > 0); + node->references++; + INSIST(node->references != 0); /* Catch overflow. */ + UNLOCK(&node->lock); + + *targetp = source; +} + +static void +detachnode(dns_db_t *db, dns_dbnode_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node; + isc_boolean_t need_destroy = ISC_FALSE; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(targetp != NULL && *targetp != NULL); + + UNUSED(sdlz); + + node = (dns_sdlznode_t *)(*targetp); + + LOCK(&node->lock); + INSIST(node->references > 0); + node->references--; + if (node->references == 0) + need_destroy = ISC_TRUE; + UNLOCK(&node->lock); + + if (need_destroy) + destroynode(node); + + *targetp = NULL; +} + +static isc_result_t +expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) { + UNUSED(db); + UNUSED(node); + UNUSED(now); + INSIST(0); + return (ISC_R_UNEXPECTED); +} + +static void +printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) { + UNUSED(db); + UNUSED(node); + UNUSED(out); + return; +} + +static isc_result_t +createiterator(dns_db_t *db, isc_boolean_t relative_names, + dns_dbiterator_t **iteratorp) +{ + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + sdlz_dbiterator_t *sdlziter; + isc_result_t result; + isc_buffer_t b; + char zonestr[DNS_NAME_MAXTEXT + 1]; + + REQUIRE(VALID_SDLZDB(sdlz)); + + if (sdlz->dlzimp->methods->allnodes == NULL) + return (ISC_R_NOTIMPLEMENTED); + + isc_buffer_init(&b, zonestr, sizeof(zonestr)); + result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_putuint8(&b, 0); + + sdlziter = isc_mem_get(sdlz->common.mctx, sizeof(sdlz_dbiterator_t)); + if (sdlziter == NULL) + return (ISC_R_NOMEMORY); + + sdlziter->common.methods = &dbiterator_methods; + sdlziter->common.db = NULL; + dns_db_attach(db, &sdlziter->common.db); + sdlziter->common.relative_names = relative_names; + sdlziter->common.magic = DNS_DBITERATOR_MAGIC; + ISC_LIST_INIT(sdlziter->nodelist); + sdlziter->current = NULL; + sdlziter->origin = NULL; + + // make sure strings are always lowercase + dns_sdlz_tolower(zonestr); + + MAYBE_LOCK(sdlz->dlzimp); + result = sdlz->dlzimp->methods->allnodes(zonestr, sdlz->dlzimp->driverarg, + sdlz->dbdata, sdlziter); + MAYBE_UNLOCK(sdlz->dlzimp); + if (result != ISC_R_SUCCESS) { + dbiterator_destroy((dns_dbiterator_t **)&sdlziter); + return (result); + } + + if (sdlziter->origin != NULL) { + ISC_LIST_UNLINK(sdlziter->nodelist, sdlziter->origin, link); + ISC_LIST_PREPEND(sdlziter->nodelist, sdlziter->origin, link); + } + + *iteratorp = (dns_dbiterator_t *)sdlziter; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_stdtime_t now, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset) +{ + dns_rdatalist_t *list; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)node; + + REQUIRE(VALID_SDLZNODE(node)); + + UNUSED(db); + UNUSED(version); + UNUSED(covers); + UNUSED(now); + UNUSED(sigrdataset); + + if (type == dns_rdatatype_sig) + return (ISC_R_NOTIMPLEMENTED); + + list = ISC_LIST_HEAD(sdlznode->lists); + while (list != NULL) { + if (list->type == type) + break; + list = ISC_LIST_NEXT(list, link); + } + if (list == NULL) + return (ISC_R_NOTFOUND); + + list_tordataset(list, db, node, rdataset); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + isc_stdtime_t now, dns_rdatasetiter_t **iteratorp) +{ + sdlz_rdatasetiter_t *iterator; + + REQUIRE(version == NULL || version == &dummy); + + UNUSED(version); + UNUSED(now); + + iterator = isc_mem_get(db->mctx, sizeof(sdlz_rdatasetiter_t)); + if (iterator == NULL) + return (ISC_R_NOMEMORY); + + iterator->common.magic = DNS_RDATASETITER_MAGIC; + iterator->common.methods = &rdatasetiter_methods; + iterator->common.db = db; + iterator->common.node = NULL; + attachnode(db, node, &iterator->common.node); + iterator->common.version = version; + iterator->common.now = now; + + *iteratorp = (dns_rdatasetiter_t *)iterator; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options, + dns_rdataset_t *addedrdataset) +{ + UNUSED(db); + UNUSED(node); + UNUSED(version); + UNUSED(now); + UNUSED(rdataset); + UNUSED(options); + UNUSED(addedrdataset); + + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdataset_t *rdataset, unsigned int options, + dns_rdataset_t *newrdataset) +{ + UNUSED(db); + UNUSED(node); + UNUSED(version); + UNUSED(rdataset); + UNUSED(options); + UNUSED(newrdataset); + + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdatatype_t type, dns_rdatatype_t covers) +{ + UNUSED(db); + UNUSED(node); + UNUSED(version); + UNUSED(type); + UNUSED(covers); + + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_boolean_t +issecure(dns_db_t *db) { + UNUSED(db); + + return (ISC_FALSE); +} + +static unsigned int +nodecount(dns_db_t *db) { + UNUSED(db); + + return (0); +} + +static isc_boolean_t +ispersistent(dns_db_t *db) { + UNUSED(db); + return (ISC_TRUE); +} + +static void +overmem(dns_db_t *db, isc_boolean_t overmem) { + UNUSED(db); + UNUSED(overmem); +} + + +static dns_dbmethods_t sdlzdb_methods = { + attach, + detach, + beginload, + endload, + dump, + currentversion, + newversion, + attachversion, + closeversion, + findnode, + find, + findzonecut, + attachnode, + detachnode, + expirenode, + printnode, + createiterator, + findrdataset, + allrdatasets, + addrdataset, + subtractrdataset, + deleterdataset, + issecure, + nodecount, + ispersistent, + overmem +}; + + +/* + * Rdataset Methods. These methods were "borrowed" from the SDB driver + * interface. See the SDB driver interface documentation for more info. + */ + +static void +disassociate(dns_rdataset_t *rdataset) { + dns_dbnode_t *node = rdataset->private5; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *) node; + dns_db_t *db = (dns_db_t *) sdlznode->sdlz; + + detachnode(db, &node); + isc__rdatalist_disassociate(rdataset); +} + +static void +rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { + dns_dbnode_t *node = source->private5; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *) node; + dns_db_t *db = (dns_db_t *) sdlznode->sdlz; + dns_dbnode_t *tempdb = NULL; + + isc__rdatalist_clone(source, target); + attachnode(db, node, &tempdb); + source->private5 = tempdb; +} + +static dns_rdatasetmethods_t methods = { + disassociate, + isc__rdatalist_first, + isc__rdatalist_next, + isc__rdatalist_current, + rdataset_clone, + isc__rdatalist_count +}; + +static void +list_tordataset(dns_rdatalist_t *rdatalist, + dns_db_t *db, dns_dbnode_t *node, + dns_rdataset_t *rdataset) +{ + /* + * The sdlz rdataset is an rdatalist with some additions. + * - private1 & private2 are used by the rdatalist. + * - private3 & private 4 are unused. + * - private5 is the node. + */ + + /* This should never fail. */ + RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == + ISC_R_SUCCESS); + + rdataset->methods = &methods; + dns_db_attachnode(db, node, &rdataset->private5); +} + +/* + * Database Iterator Methods. These methods were "borrowed" from the SDB + * driver interface. See the SDB driver interface documentation for more info. + */ +static void +dbiterator_destroy(dns_dbiterator_t **iteratorp) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)(*iteratorp); + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)sdlziter->common.db; + + while (!ISC_LIST_EMPTY(sdlziter->nodelist)) { + dns_sdlznode_t *node; + node = ISC_LIST_HEAD(sdlziter->nodelist); + ISC_LIST_UNLINK(sdlziter->nodelist, node, link); + destroynode(node); + } + + dns_db_detach(&sdlziter->common.db); + isc_mem_put(sdlz->common.mctx, sdlziter, sizeof(sdlz_dbiterator_t)); + + *iteratorp = NULL; +} + +static isc_result_t +dbiterator_first(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist); + if (sdlziter->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_last(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_TAIL(sdlziter->nodelist); + if (sdlziter->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist); + while (sdlziter->current != NULL) + if (dns_name_equal(sdlziter->current->name, name)) + return (ISC_R_SUCCESS); + return (ISC_R_NOTFOUND); +} + +static isc_result_t +dbiterator_prev(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_PREV(sdlziter->current, link); + if (sdlziter->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_next(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_NEXT(sdlziter->current, link); + if (sdlziter->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, + dns_name_t *name) +{ + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + attachnode(iterator->db, sdlziter->current, nodep); + if (name != NULL) + return (dns_name_copy(sdlziter->current->name, name, NULL)); + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_pause(dns_dbiterator_t *iterator) { + UNUSED(iterator); + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) { + UNUSED(iterator); + return (dns_name_copy(dns_rootname, name, NULL)); +} + +/* + * Rdataset Iterator Methods. These methods were "borrowed" from the SDB + * driver interface. See the SDB driver interface documentation for more info. + */ + +static void +rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)(*iteratorp); + detachnode(sdlziterator->common.db, &sdlziterator->common.node); + isc_mem_put(sdlziterator->common.db->mctx, sdlziterator, + sizeof(sdlz_rdatasetiter_t)); + *iteratorp = NULL; +} + +static isc_result_t +rdatasetiter_first(dns_rdatasetiter_t *iterator) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)iterator->node; + + if (ISC_LIST_EMPTY(sdlznode->lists)) + return (ISC_R_NOMORE); + sdlziterator->current = ISC_LIST_HEAD(sdlznode->lists); + return (ISC_R_SUCCESS); +} + +static isc_result_t +rdatasetiter_next(dns_rdatasetiter_t *iterator) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + + sdlziterator->current = ISC_LIST_NEXT(sdlziterator->current, link); + if (sdlziterator->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static void +rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + + list_tordataset(sdlziterator->current, iterator->db, iterator->node, + rdataset); +} + + +/* + * SDLZ core methods. This is the core of the new DLZ functionality. + */ + +/* + * Build a 'bind' database driver structure to be returned by + * either the find zone or the allow zone transfer method. + * This method is only available in this source file, it is + * not made available anywhere else. + */ + +isc_result_t +dns_sdlzcreateDBP(isc_mem_t *mctx, void *driverarg, void *dbdata, + dns_name_t *name, dns_rdataclass_t rdclass, dns_db_t **dbp) +{ + + isc_result_t result; + dns_sdlz_db_t *sdlzdb; + dns_sdlzimplementation_t *imp; + + /* check that things are as we expect */ + REQUIRE(dbp != NULL && *dbp == NULL); + REQUIRE(name != NULL); + + imp = (dns_sdlzimplementation_t *) driverarg; + + /* allocate and zero memory for driver structure */ + sdlzdb = isc_mem_get(mctx, sizeof(dns_sdlz_db_t)); + if (sdlzdb == NULL) + return (ISC_R_NOMEMORY); + memset(sdlzdb, 0, sizeof(dns_sdlz_db_t)); + + /* initialize and set origin */ + dns_name_init(&sdlzdb->common.origin, NULL); + result = dns_name_dupwithoffsets(name, mctx, &sdlzdb->common.origin); + if (result != ISC_R_SUCCESS) + goto mem_cleanup; + + /* initialize the reference count mutex */ + result = isc_mutex_init(&sdlzdb->refcnt_lock); + if (result != ISC_R_SUCCESS) + goto name_cleanup; + + /* set the rest of the database structure attributes */ + sdlzdb->dlzimp = imp; + sdlzdb->common.methods = &sdlzdb_methods; + sdlzdb->common.attributes = 0; + sdlzdb->common.rdclass = rdclass; + sdlzdb->common.mctx = NULL; + sdlzdb->dbdata = dbdata; + sdlzdb->references = 1; + + /* attach to the memory context */ + isc_mem_attach(mctx, &sdlzdb->common.mctx); + + /* mark structure as valid */ + sdlzdb->common.magic = DNS_DB_MAGIC; + sdlzdb->common.impmagic = SDLZDB_MAGIC; + *dbp = (dns_db_t *) sdlzdb; + + return result; + + /* reference count mutex could not be initialized, clean up name memory */ +name_cleanup: + dns_name_free(&sdlzdb->common.origin, mctx); +mem_cleanup: + isc_mem_put(mctx, sdlzdb, sizeof(dns_sdlz_db_t)); + return result; +} + +isc_result_t +dns_sdlzallowzonexfr(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, + isc_sockaddr_t *clientaddr, dns_db_t **dbp) +{ + + isc_buffer_t b; + isc_buffer_t b2; + char namestr[DNS_NAME_MAXTEXT + 1]; + char clientstr[(sizeof "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255") + 1]; + isc_netaddr_t netaddr; + isc_result_t result; + dns_sdlzimplementation_t *imp; + + /* Perform checks to make sure data is as we expect / require it to be. */ + REQUIRE(driverarg != NULL); + REQUIRE(name != NULL); + REQUIRE(clientaddr != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + imp = (dns_sdlzimplementation_t *) driverarg; + + /* Convert DNS name to ascii text */ + isc_buffer_init(&b, namestr, sizeof(namestr)); + result = dns_name_totext(name, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return result; + isc_buffer_putuint8(&b, 0); + + /* convert client address to ascii text */ + isc_buffer_init(&b2, clientstr, sizeof(clientstr)); + isc_netaddr_fromsockaddr(&netaddr, clientaddr); + result = isc_netaddr_totext(&netaddr, &b2); + if (result != ISC_R_SUCCESS) + return result; + isc_buffer_putuint8(&b2, 0); + + // make sure strings are always lowercase + dns_sdlz_tolower(namestr); + dns_sdlz_tolower(clientstr); + + /* Call SDLZ driver's find zone method */ + if(imp->methods->allowzonexfr != NULL){ + MAYBE_LOCK(imp); + result = imp->methods->allowzonexfr(imp->driverarg, dbdata, namestr, clientstr); + MAYBE_UNLOCK(imp); + /* if zone is supported and transfers allowed build a 'bind' database driver */ + if(result == ISC_R_SUCCESS) + result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, name, rdclass, dbp); + return result; + } + + return ISC_R_NOTIMPLEMENTED; +} + +isc_result_t +dns_sdlzcreate(isc_mem_t *mctx, const char *dlzname, unsigned int argc, + char *argv[], void *driverarg, void **dbdata) +{ + dns_sdlzimplementation_t *imp; + isc_result_t result = ISC_R_NOTFOUND; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Loading SDLZ driver."); + + /* Performs checks to make sure data is as we expect / require it to be. */ + REQUIRE(driverarg != NULL); + REQUIRE(dlzname != NULL); + REQUIRE(dbdata != NULL); + UNUSED(mctx); + + imp = driverarg; + + /* If the create method exists, call it. */ + if(imp->methods->create != NULL){ + MAYBE_LOCK(imp); + result = imp->methods->create(dlzname, argc, argv, imp->driverarg, dbdata); + MAYBE_UNLOCK(imp); + } + + /* Write debugging message to log */ + if(result == ISC_R_SUCCESS){ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "SDLZ driver loaded successfully."); + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "SDLZ driver failed to load."); + } + + return result; +} + +void +dns_sdlzdestroy(void *driverdata, void **dbdata) +{ + + dns_sdlzimplementation_t *imp; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unloading SDLZ driver."); + + imp = driverdata; + + /* If the destroy method exists, call it. */ + if(imp->methods->destroy != NULL){ + MAYBE_LOCK(imp); + imp->methods->destroy(imp->driverarg, dbdata); + MAYBE_UNLOCK(imp); + } +} + +isc_result_t +dns_sdlzfindzone(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, dns_db_t **dbp) +{ + isc_buffer_t b; + char namestr[DNS_NAME_MAXTEXT + 1]; + isc_result_t result; + dns_sdlzimplementation_t *imp; + + /* Perform checks to make sure data is as we expect / require it to be. */ + REQUIRE(driverarg != NULL); + REQUIRE(name != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + imp = (dns_sdlzimplementation_t *) driverarg; + + /* Convert DNS name to ascii text */ + isc_buffer_init(&b, namestr, sizeof(namestr)); + result = dns_name_totext(name, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return result; + isc_buffer_putuint8(&b, 0); + + // make sure strings are always lowercase + dns_sdlz_tolower(namestr); + + /* Call SDLZ driver's find zone method */ + MAYBE_LOCK(imp); + result = imp->methods->findzone(imp->driverarg, dbdata, namestr); + MAYBE_UNLOCK(imp); + + /* if zone is supported build a 'bind' database driver structure to return */ + if(result == ISC_R_SUCCESS) + result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, name, rdclass, dbp); + + return result; +} + +static dns_dlzmethods_t sdlzmethods = {dns_sdlzcreate, dns_sdlzdestroy, dns_sdlzfindzone, + dns_sdlzallowzonexfr}; + +isc_result_t +dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods, + void *driverarg, unsigned int flags, isc_mem_t *mctx, + dns_sdlzimplementation_t **sdlzimp) +{ + + dns_sdlzimplementation_t *imp; + isc_result_t result; + + /* Performs checks to make sure data is as we expect / require it to be. */ + REQUIRE(drivername != NULL); + REQUIRE(methods != NULL); + REQUIRE(methods->findzone != NULL); + REQUIRE(methods->lookup != NULL); + REQUIRE(mctx != NULL); + REQUIRE(sdlzimp != NULL && *sdlzimp == NULL); + REQUIRE((flags & ~(DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE)) == 0); + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering SDLZ driver '%s'", drivername); + + /* Allocate memory for a sdlz_implementation object. Error if we cannot. */ + imp = isc_mem_get(mctx, sizeof(dns_sdlzimplementation_t)); + if (imp == NULL) + return (ISC_R_NOMEMORY); + + /* Make sure memory region is set to all 0's */ + memset(imp, 0, sizeof(dns_sdlzimplementation_t)); + + /* Store the data passed into this method */ + imp->methods = methods; + imp->driverarg = driverarg; + imp->flags = flags; + imp->mctx = NULL; + + /* attach the new sdlz_implementation object to a memory context */ + isc_mem_attach(mctx, &imp->mctx); + + /* + * initialize the driver lock, error if we cannot + * (used if a driver does not support multiple threads) + */ + result = isc_mutex_init(&imp->driverlock); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + goto cleanup_mctx; + } + + imp->dlz_imp = NULL; + + /* + * register the DLZ driver. Pass in our "extra" sdlz information as + * a driverarg. (that's why we stored the passed in driver arg in our + * sdlz_implementation structure) Also, store the dlz_implementation + * structure in our sdlz_implementation. + */ + result = dns_dlzregister(drivername, &sdlzmethods, imp, mctx, + &imp->dlz_imp); + + /* if registration fails, cleanup and get outta here. */ + if (result != ISC_R_SUCCESS) + goto cleanup_mutex; + + *sdlzimp = imp; + + return (ISC_R_SUCCESS); + + cleanup_mutex: + /* destroy the driver lock, we don't need it anymore */ + DESTROYLOCK(&imp->driverlock); + + cleanup_mctx: + /* + * return the memory back to the available memory pool and remove it from + * the memory context. + */ + isc_mem_put(mctx, imp, sizeof(dns_sdlzimplementation_t)); + isc_mem_detach(&mctx); + return (result); +} + +void +dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp) { + + dns_sdlzimplementation_t *imp; + isc_mem_t *mctx; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering SDLZ driver."); + + /* Performs checks to make sure data is as we expect / require it to be. */ + REQUIRE(sdlzimp != NULL && *sdlzimp != NULL); + + imp = *sdlzimp; + + /* Unregister the DLZ driver implementation */ + dns_dlzunregister(&imp->dlz_imp); + + /* destroy the driver lock, we don't need it anymore */ + DESTROYLOCK(&imp->driverlock); + + mctx = imp->mctx; + + /* + * return the memory back to the available memory pool and remove it from + * the memory context. + */ + isc_mem_put(mctx, imp, sizeof(dns_sdlzimplementation_t)); + isc_mem_detach(&mctx); + + *sdlzimp = NULL; +} + +#endif + diff -Nuar bind-9.3.2-orig/lib/dns/view.c bind-9.3.2-mod/lib/dns/view.c --- bind-9.3.2-orig/lib/dns/view.c 2004-03-10 03:55:58.000000000 +0100 +++ bind-9.3.2-mod/lib/dns/view.c 2006-02-20 19:13:55.000000000 +0100 @@ -28,6 +28,9 @@ #include #include #include +#ifdef DLZ +#include +#endif #include #include #include @@ -122,6 +125,9 @@ view->cache = NULL; view->cachedb = NULL; +#ifdef DLZ + view->dlzdatabase = NULL; +#endif view->hints = NULL; view->resolver = NULL; view->adb = NULL; @@ -259,6 +265,10 @@ isc_task_detach(&view->task); if (view->hints != NULL) dns_db_detach(&view->hints); +#ifdef DLZ + if (view->dlzdatabase != NULL) + dns_dlzdestroy(&view->dlzdatabase); +#endif if (view->cachedb != NULL) dns_db_detach(&view->cachedb); if (view->cache != NULL) diff -Nuar bind-9.3.2-orig/lib/dns/win32/libdns.def bind-9.3.2-mod/lib/dns/win32/libdns.def --- bind-9.3.2-orig/lib/dns/win32/libdns.def 2004-04-19 08:39:55.000000000 +0200 +++ bind-9.3.2-mod/lib/dns/win32/libdns.def 2006-02-20 19:13:55.000000000 +0100 @@ -707,6 +707,18 @@ dst_lib_initmsgcat dst_result_totext dst_result_register +dns_dlzregister +dns_dlzunregister +dns_dlzallowzonexfr +dns_dlzcreate +dns_dlzdestroy +dns_dlzstrtoargv +dns_dlzfindzone +dns_sdlzregister +dns_sdlzunregister +dns_sdlz_putnamedrr +dns_sdlz_putrr +dns_sdlz_putsoa dns_ds_buildrdata dns_order_create dns_order_add diff -Nuar bind-9.3.2-orig/lib/isc/unix/dir.c bind-9.3.2-mod/lib/isc/unix/dir.c --- bind-9.3.2-orig/lib/isc/unix/dir.c 2004-03-08 10:04:55.000000000 +0100 +++ bind-9.3.2-mod/lib/isc/unix/dir.c 2006-02-20 19:13:55.000000000 +0100 @@ -56,12 +56,31 @@ */ isc_result_t isc_dir_open(isc_dir_t *dir, const char *dirname) { + char *p; isc_result_t result = ISC_R_SUCCESS; REQUIRE(VALID_DIR(dir)); REQUIRE(dirname != NULL); /* + * Copy directory name. Need to have enough space for the name, + * a possible path separator, the wildcard, and the final NUL. + */ + if (strlen(dirname) + 3 > sizeof(dir->dirname)) + /* XXXDCL ? */ + return (ISC_R_NOSPACE); + strcpy(dir->dirname, dirname); + + /* + * Append path separator, if needed, and "*". + */ + p = dir->dirname + strlen(dir->dirname); + if (dir->dirname < p && *(p - 1) != '/') + *p++ = '/'; + *p++ = '*'; + *p++ = '\0'; + + /* * Open stream. */ dir->handle = opendir(dirname); diff -Nuar bind-9.3.2-orig/lib/isccfg/Makefile.in bind-9.3.2-mod/lib/isccfg/Makefile.in --- bind-9.3.2-orig/lib/isccfg/Makefile.in 2004-07-20 09:01:58.000000000 +0200 +++ bind-9.3.2-mod/lib/isccfg/Makefile.in 2006-02-20 19:13:55.000000000 +0100 @@ -27,7 +27,7 @@ CINCLUDES = -I. ${DNS_INCLUDES} ${ISC_INCLUDES} ${ISCCFG_INCLUDES} -CDEFINES = +CDEFINES = @USE_DLZ@ CWARNINGS = ISCLIBS = ../../lib/isc/libisc.@A@ diff -Nuar bind-9.3.2-orig/lib/isccfg/namedconf.c bind-9.3.2-mod/lib/isccfg/namedconf.c --- bind-9.3.2-orig/lib/isccfg/namedconf.c 2005-10-26 07:06:40.000000000 +0200 +++ bind-9.3.2-mod/lib/isccfg/namedconf.c 2006-02-20 19:13:55.000000000 +0100 @@ -104,6 +104,44 @@ static cfg_type_t cfg_type_viewopts; static cfg_type_t cfg_type_zone; static cfg_type_t cfg_type_zoneopts; +#ifdef DLZ +static cfg_type_t cfg_type_dynamically_loadable_zones; +static cfg_type_t cfg_type_dynamically_loadable_zones_opts; +#endif + +#ifdef DLZ + +/* + * Clauses that can be found in a 'dynamically loadable zones' statement + */ +static cfg_clausedef_t +dynamically_loadable_zones_clauses[] = { + { "database", &cfg_type_astring, 0 }, + { NULL, NULL, 0 } +}; + +#endif + + +#ifdef DLZ +/* + * A dynamically loadable zones statement. + */ +static cfg_tuplefielddef_t dynamically_loadable_zones_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "options", &cfg_type_dynamically_loadable_zones_opts, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_dynamically_loadable_zones = { + "dlz", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, + dynamically_loadable_zones_fields + }; + +#endif + + /* tkey-dhkey */ @@ -563,6 +601,9 @@ namedconf_or_view_clauses[] = { { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, +#ifdef DLZ + { "dlz", &cfg_type_dynamically_loadable_zones, 0 }, // only 1 DLZ per view allowed +#endif { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI }, { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } @@ -845,6 +886,9 @@ namedconf_or_view_clauses, view_clauses, zone_clauses, +#ifdef DLZ + dynamically_loadable_zones_clauses, +#endif NULL }; static cfg_type_t cfg_type_viewopts = { @@ -859,7 +903,24 @@ NULL }; static cfg_type_t cfg_type_zoneopts = { - "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets }; + "zoneopts", cfg_parse_map, cfg_print_map, + cfg_doc_map, &cfg_rep_map, zone_clausesets }; + +#ifdef DLZ + /* The "dynamically loadable zones" statement syntax. */ + + static cfg_clausedef_t * + dynamically_loadable_zones_clausesets[] = { + dynamically_loadable_zones_clauses, + NULL + }; + static cfg_type_t cfg_type_dynamically_loadable_zones_opts = { + "dynamically_loadable_zones_opts", cfg_parse_map, + cfg_print_map, cfg_doc_map, &cfg_rep_map, + dynamically_loadable_zones_clausesets + }; + +#endif /* * Clauses that can be found within the 'key' statement. @@ -877,7 +938,9 @@ NULL }; static cfg_type_t cfg_type_key = { - "key", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, key_clausesets }; + "key", cfg_parse_named_map, cfg_print_map, + cfg_doc_map, &cfg_rep_map, key_clausesets + }; /* diff -Nuar bind-9.3.2-orig/lib/isccfg/namedconf.c.orig bind-9.3.2-mod/lib/isccfg/namedconf.c.orig --- bind-9.3.2-orig/lib/isccfg/namedconf.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ bind-9.3.2-mod/lib/isccfg/namedconf.c.orig 2005-10-26 07:06:40.000000000 +0200 @@ -0,0 +1,1908 @@ +/* + * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2002, 2003 Internet Software Consortium. + * + * 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: namedconf.c,v 1.21.44.32 2005/10/26 05:06:40 marka Exp $ */ + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base) + +/* Check a return value. */ +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto cleanup; \ + } while (0) + +/* Clean up a configuration object if non-NULL. */ +#define CLEANUP_OBJ(obj) \ + do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0) + + +/* + * Forward declarations of static functions. + */ + +static isc_result_t +parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, + const cfg_type_t *othertype, cfg_obj_t **ret); + +static isc_result_t +parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); + +static isc_result_t +parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); + +static void +print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj); + +static void +doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); + +static void +doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); + +static cfg_type_t cfg_type_acl; +static cfg_type_t cfg_type_addrmatchelt; +static cfg_type_t cfg_type_bracketed_aml; +static cfg_type_t cfg_type_bracketed_namesockaddrkeylist; +static cfg_type_t cfg_type_bracketed_sockaddrlist; +static cfg_type_t cfg_type_controls; +static cfg_type_t cfg_type_controls_sockaddr; +static cfg_type_t cfg_type_destinationlist; +static cfg_type_t cfg_type_dialuptype; +static cfg_type_t cfg_type_key; +static cfg_type_t cfg_type_logfile; +static cfg_type_t cfg_type_logging; +static cfg_type_t cfg_type_logseverity; +static cfg_type_t cfg_type_lwres; +static cfg_type_t cfg_type_masterselement; +static cfg_type_t cfg_type_nameportiplist; +static cfg_type_t cfg_type_negated; +static cfg_type_t cfg_type_notifytype; +static cfg_type_t cfg_type_optional_class; +static cfg_type_t cfg_type_optional_facility; +static cfg_type_t cfg_type_optional_facility; +static cfg_type_t cfg_type_optional_keyref; +static cfg_type_t cfg_type_optional_port; +static cfg_type_t cfg_type_options; +static cfg_type_t cfg_type_portiplist; +static cfg_type_t cfg_type_querysource4; +static cfg_type_t cfg_type_querysource6; +static cfg_type_t cfg_type_querysource; +static cfg_type_t cfg_type_server; +static cfg_type_t cfg_type_server_key_kludge; +static cfg_type_t cfg_type_size; +static cfg_type_t cfg_type_sizenodefault; +static cfg_type_t cfg_type_sockaddr4wild; +static cfg_type_t cfg_type_sockaddr6wild; +static cfg_type_t cfg_type_view; +static cfg_type_t cfg_type_viewopts; +static cfg_type_t cfg_type_zone; +static cfg_type_t cfg_type_zoneopts; + +/* tkey-dhkey */ + +static cfg_tuplefielddef_t tkey_dhkey_fields[] = { + { "name", &cfg_type_qstring, 0 }, + { "keyid", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_tkey_dhkey = { + "tkey-dhkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + tkey_dhkey_fields +}; + +/* listen-on */ + +static cfg_tuplefielddef_t listenon_fields[] = { + { "port", &cfg_type_optional_port, 0 }, + { "acl", &cfg_type_bracketed_aml, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_listenon = { + "listenon", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, listenon_fields }; + +/* acl */ + +static cfg_tuplefielddef_t acl_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "value", &cfg_type_bracketed_aml, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_acl = { + "acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, acl_fields }; + +/* masters */ +static cfg_tuplefielddef_t masters_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "port", &cfg_type_optional_port, 0 }, + { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_masters = { + "masters", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, masters_fields }; + +/* + * "sockaddrkeylist", a list of socket addresses with optional keys + * and an optional default port, as used in the masters option. + * E.g., + * "port 1234 { mymasters; 10.0.0.1 key foo; 1::2 port 69; }" + */ + +static cfg_tuplefielddef_t namesockaddrkey_fields[] = { + { "masterselement", &cfg_type_masterselement, 0 }, + { "key", &cfg_type_optional_keyref, 0 }, + { NULL, NULL, 0 }, +}; + +static cfg_type_t cfg_type_namesockaddrkey = { + "namesockaddrkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + namesockaddrkey_fields +}; + +static cfg_type_t cfg_type_bracketed_namesockaddrkeylist = { + "bracketed_namesockaddrkeylist", cfg_parse_bracketed_list, + cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_namesockaddrkey +}; + +static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = { + { "port", &cfg_type_optional_port, 0 }, + { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_namesockaddrkeylist = { + "sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + namesockaddrkeylist_fields +}; + +/* + * A list of socket addresses with an optional default port, + * as used in the also-notify option. E.g., + * "port 1234 { 10.0.0.1; 1::2 port 69; }" + */ +static cfg_tuplefielddef_t portiplist_fields[] = { + { "port", &cfg_type_optional_port, 0 }, + { "addresses", &cfg_type_bracketed_sockaddrlist, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_portiplist = { + "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + portiplist_fields +}; + +/* + * A public key, as in the "pubkey" statement. + */ +static cfg_tuplefielddef_t pubkey_fields[] = { + { "flags", &cfg_type_uint32, 0 }, + { "protocol", &cfg_type_uint32, 0 }, + { "algorithm", &cfg_type_uint32, 0 }, + { "key", &cfg_type_qstring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_pubkey = { + "pubkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, pubkey_fields }; + +/* + * A list of RR types, used in grant statements. + * Note that the old parser allows quotes around the RR type names. + */ +static cfg_type_t cfg_type_rrtypelist = { + "rrtypelist", cfg_parse_spacelist, cfg_print_spacelist, cfg_doc_terminal, + &cfg_rep_list, &cfg_type_astring +}; + +static const char *mode_enums[] = { "grant", "deny", NULL }; +static cfg_type_t cfg_type_mode = { + "mode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &mode_enums +}; + +static const char *matchtype_enums[] = { + "name", "subdomain", "wildcard", "self", NULL }; +static cfg_type_t cfg_type_matchtype = { + "matchtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &matchtype_enums +}; + +/* + * A grant statement, used in the update policy. + */ +static cfg_tuplefielddef_t grant_fields[] = { + { "mode", &cfg_type_mode, 0 }, + { "identity", &cfg_type_astring, 0 }, /* domain name */ + { "matchtype", &cfg_type_matchtype, 0 }, + { "name", &cfg_type_astring, 0 }, /* domain name */ + { "types", &cfg_type_rrtypelist, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_grant = { + "grant", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, grant_fields }; + +static cfg_type_t cfg_type_updatepolicy = { + "update_policy", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_grant +}; + +/* + * A view statement. + */ +static cfg_tuplefielddef_t view_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "class", &cfg_type_optional_class, 0 }, + { "options", &cfg_type_viewopts, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_view = { + "view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, view_fields }; + +/* + * A zone statement. + */ +static cfg_tuplefielddef_t zone_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "class", &cfg_type_optional_class, 0 }, + { "options", &cfg_type_zoneopts, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_zone = { + "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, zone_fields }; + +/* + * A "category" clause in the "logging" statement. + */ +static cfg_tuplefielddef_t category_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "destinations", &cfg_type_destinationlist,0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_category = { + "category", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, category_fields }; + + +/* + * A trusted key, as used in the "trusted-keys" statement. + */ +static cfg_tuplefielddef_t trustedkey_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "flags", &cfg_type_uint32, 0 }, + { "protocol", &cfg_type_uint32, 0 }, + { "algorithm", &cfg_type_uint32, 0 }, + { "key", &cfg_type_qstring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_trustedkey = { + "trustedkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + trustedkey_fields +}; + +static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring }; + +static cfg_type_t cfg_type_optional_wild_class = { + "optional_wild_class", parse_optional_keyvalue, print_keyvalue, + doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw +}; + +static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring }; + +static cfg_type_t cfg_type_optional_wild_type = { + "optional_wild_type", parse_optional_keyvalue, + print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw +}; + +static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring }; + +static cfg_type_t cfg_type_optional_wild_name = { + "optional_wild_name", parse_optional_keyvalue, + print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw +}; + +/* + * An rrset ordering element. + */ +static cfg_tuplefielddef_t rrsetorderingelement_fields[] = { + { "class", &cfg_type_optional_wild_class, 0 }, + { "type", &cfg_type_optional_wild_type, 0 }, + { "name", &cfg_type_optional_wild_name, 0 }, + { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */ + { "ordering", &cfg_type_ustring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_rrsetorderingelement = { + "rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + rrsetorderingelement_fields +}; + +/* + * A global or view "check-names" option. Note that the zone + * "check-names" option has a different syntax. + */ + +static const char *checktype_enums[] = { "master", "slave", "response", NULL }; +static cfg_type_t cfg_type_checktype = { + "checktype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, + &cfg_rep_string, &checktype_enums +}; + +static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL }; +static cfg_type_t cfg_type_checkmode = { + "checkmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, + &cfg_rep_string, &checkmode_enums +}; + +static cfg_tuplefielddef_t checknames_fields[] = { + { "type", &cfg_type_checktype, 0 }, + { "mode", &cfg_type_checkmode, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_checknames = { + "checknames", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + checknames_fields +}; + +static cfg_type_t cfg_type_bracketed_sockaddrlist = { + "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_sockaddr +}; + +static cfg_type_t cfg_type_rrsetorder = { + "rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_rrsetorderingelement +}; + +static keyword_type_t port_kw = { "port", &cfg_type_uint32 }; + +static cfg_type_t cfg_type_optional_port = { + "optional_port", parse_optional_keyvalue, print_keyvalue, + doc_optional_keyvalue, &cfg_rep_uint32, &port_kw +}; + +/* A list of keys, as in the "key" clause of the controls statement. */ +static cfg_type_t cfg_type_keylist = { + "keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, + &cfg_type_astring +}; + +static cfg_type_t cfg_type_trustedkeys = { + "trusted-keys", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, + &cfg_type_trustedkey +}; + +static const char *forwardtype_enums[] = { "first", "only", NULL }; +static cfg_type_t cfg_type_forwardtype = { + "forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &forwardtype_enums +}; + +static const char *zonetype_enums[] = { + "master", "slave", "stub", "hint", "forward", "delegation-only", NULL }; +static cfg_type_t cfg_type_zonetype = { + "zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &zonetype_enums +}; + +static const char *loglevel_enums[] = { + "critical", "error", "warning", "notice", "info", "dynamic", NULL }; +static cfg_type_t cfg_type_loglevel = { + "loglevel", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &loglevel_enums +}; + +static const char *transferformat_enums[] = { + "many-answers", "one-answer", NULL }; +static cfg_type_t cfg_type_transferformat = { + "transferformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, + &transferformat_enums +}; + +/* + * The special keyword "none", as used in the pid-file option. + */ + +static void +print_none(cfg_printer_t *pctx, cfg_obj_t *obj) { + UNUSED(obj); + cfg_print_chars(pctx, "none", 4); +} + +static cfg_type_t cfg_type_none = { + "none", NULL, print_none, NULL, &cfg_rep_void, NULL +}; + +/* + * A quoted string or the special keyword "none". Used in the pid-file option. + */ +static isc_result_t +parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) +{ + isc_result_t result; + CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(TOKEN_STRING(pctx), "none") == 0) + return (cfg_create_obj(pctx, &cfg_type_none, ret)); + cfg_ungettoken(pctx); + return (cfg_parse_qstring(pctx, type, ret)); + cleanup: + return (result); +} + +static void +doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + cfg_print_chars(pctx, "( | none )", 26); +} + +static cfg_type_t cfg_type_qstringornone = { + "qstringornone", parse_qstringornone, NULL, doc_qstringornone, NULL, NULL }; + +/* + * keyword hostname + */ + +static void +print_hostname(cfg_printer_t *pctx, cfg_obj_t *obj) { + UNUSED(obj); + cfg_print_chars(pctx, "hostname", 4); +} + +static cfg_type_t cfg_type_hostname = { + "hostname", NULL, print_hostname, NULL, &cfg_rep_boolean, NULL +}; + +/* + * "server-id" argument. + */ + +static isc_result_t +parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) +{ + isc_result_t result; + CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(TOKEN_STRING(pctx), "none") == 0) + return (cfg_create_obj(pctx, &cfg_type_none, ret)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(TOKEN_STRING(pctx), "hostname") == 0) { + return (cfg_create_obj(pctx, &cfg_type_hostname, ret)); + } + cfg_ungettoken(pctx); + return (cfg_parse_qstring(pctx, type, ret)); + cleanup: + return (result); +} + +static void +doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + cfg_print_chars(pctx, "( | none | hostname )", 26); +} + +static cfg_type_t cfg_type_serverid = { + "serverid", parse_serverid, NULL, doc_serverid, NULL, NULL }; + +/* + * Port list. + */ +static isc_result_t +parse_port(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + + UNUSED(type); + + CHECK(cfg_parse_uint32(pctx, NULL, ret)); + if ((*ret)->value.uint32 > 0xffff) { + cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port"); + cfg_obj_destroy(pctx, ret); + result = ISC_R_RANGE; + } + cleanup: + return (result); +} + +static cfg_type_t cfg_type_port = { + "port", parse_port, NULL, cfg_doc_terminal, + NULL, NULL +}; + +static cfg_type_t cfg_type_bracketed_portlist = { + "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_port +}; + +/* + * Clauses that can be found within the top level of the named.conf + * file only. + */ +static cfg_clausedef_t +namedconf_clauses[] = { + { "options", &cfg_type_options, 0 }, + { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, + { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, + { "masters", &cfg_type_masters, CFG_CLAUSEFLAG_MULTI }, + { "logging", &cfg_type_logging, 0 }, + { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI }, + { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can occur at the top level or in the view + * statement, but not in the options block. + */ +static cfg_clausedef_t +namedconf_or_view_clauses[] = { + { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, + { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, + { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI }, + { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found within the 'options' statement. + */ +static cfg_clausedef_t +options_clauses[] = { + { "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 }, + { "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 }, + { "blackhole", &cfg_type_bracketed_aml, 0 }, + { "coresize", &cfg_type_size, 0 }, + { "datasize", &cfg_type_size, 0 }, + { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK }, + { "dump-file", &cfg_type_qstring, 0 }, + { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "files", &cfg_type_size, 0 }, + { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "heartbeat-interval", &cfg_type_uint32, 0 }, + { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP }, + { "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, + { "hostname", &cfg_type_qstringornone, 0 }, + { "interface-interval", &cfg_type_uint32, 0 }, + { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, + { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, + { "match-mapped-addresses", &cfg_type_boolean, 0 }, + { "memstatistics-file", &cfg_type_qstring, 0 }, + { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, + { "pid-file", &cfg_type_qstringornone, 0 }, + { "port", &cfg_type_uint32, 0 }, + { "querylog", &cfg_type_boolean, 0 }, + { "recursing-file", &cfg_type_qstring, 0 }, + { "random-device", &cfg_type_qstring, 0 }, + { "recursive-clients", &cfg_type_uint32, 0 }, + { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, + { "serial-query-rate", &cfg_type_uint32, 0 }, + { "server-id", &cfg_type_serverid, 0 }, + { "stacksize", &cfg_type_size, 0 }, + { "statistics-file", &cfg_type_qstring, 0 }, + { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI }, + { "tcp-clients", &cfg_type_uint32, 0 }, + { "tcp-listen-queue", &cfg_type_uint32, 0 }, + { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 }, + { "tkey-gssapi-credential", &cfg_type_qstring, 0 }, + { "tkey-domain", &cfg_type_qstring, 0 }, + { "transfers-per-ns", &cfg_type_uint32, 0 }, + { "transfers-in", &cfg_type_uint32, 0 }, + { "transfers-out", &cfg_type_uint32, 0 }, + { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "use-ixfr", &cfg_type_boolean, 0 }, + { "version", &cfg_type_qstringornone, 0 }, + { "flush-zones-on-shutdown", &cfg_type_boolean, 0 }, + { NULL, NULL, 0 } +}; + + +static cfg_type_t cfg_type_namelist = { + "namelist", cfg_parse_bracketed_list, cfg_print_bracketed_list, + cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_qstring }; + +static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist }; + +static cfg_type_t cfg_type_optional_exclude = { + "optional_exclude", parse_optional_keyvalue, print_keyvalue, + doc_optional_keyvalue, &cfg_rep_list, &exclude_kw }; + +static cfg_type_t cfg_type_algorithmlist = { + "algorithmlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, + cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; + +static cfg_tuplefielddef_t disablealgorithm_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "algorithms", &cfg_type_algorithmlist, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_disablealgorithm = { + "disablealgorithm", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, disablealgorithm_fields +}; + +static cfg_tuplefielddef_t mustbesecure_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "value", &cfg_type_boolean, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_mustbesecure = { + "mustbesecure", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, mustbesecure_fields +}; + +/* + * dnssec-lookaside + */ + +static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring }; + +static cfg_type_t cfg_type_trustanchor = { + "trust-anchor", parse_keyvalue, print_keyvalue, doc_keyvalue, + &cfg_rep_string, &trustanchor_kw +}; + +static cfg_tuplefielddef_t lookaside_fields[] = { + { "domain", &cfg_type_astring, 0 }, + { "trust-anchor", &cfg_type_trustanchor, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_lookaside = { + "lookaside", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, lookaside_fields +}; + +/* + * Clauses that can be found within the 'view' statement, + * with defaults in the 'options' statement. + */ + +static cfg_clausedef_t +view_clauses[] = { + { "allow-recursion", &cfg_type_bracketed_aml, 0 }, + { "allow-v6-synthesis", &cfg_type_bracketed_aml, + CFG_CLAUSEFLAG_OBSOLETE }, + { "sortlist", &cfg_type_bracketed_aml, 0 }, + { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP }, + { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT }, + { "minimal-responses", &cfg_type_boolean, 0 }, + { "recursion", &cfg_type_boolean, 0 }, + { "rrset-order", &cfg_type_rrsetorder, 0 }, + { "provide-ixfr", &cfg_type_boolean, 0 }, + { "request-ixfr", &cfg_type_boolean, 0 }, + { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, + { "additional-from-auth", &cfg_type_boolean, 0 }, + { "additional-from-cache", &cfg_type_boolean, 0 }, + /* + * Note that the query-source option syntax is different + * from the other -source options. + */ + { "query-source", &cfg_type_querysource4, 0 }, + { "query-source-v6", &cfg_type_querysource6, 0 }, + { "cleaning-interval", &cfg_type_uint32, 0 }, + { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, + { "lame-ttl", &cfg_type_uint32, 0 }, + { "max-ncache-ttl", &cfg_type_uint32, 0 }, + { "max-cache-ttl", &cfg_type_uint32, 0 }, + { "transfer-format", &cfg_type_transferformat, 0 }, + { "max-cache-size", &cfg_type_sizenodefault, 0 }, + { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI }, + { "cache-file", &cfg_type_qstring, 0 }, + { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, + { "preferred-glue", &cfg_type_astring, 0 }, + { "dual-stack-servers", &cfg_type_nameportiplist, 0 }, + { "edns-udp-size", &cfg_type_uint32, 0 }, + { "root-delegation-only", &cfg_type_optional_exclude, 0 }, + { "disable-algorithms", &cfg_type_disablealgorithm, + CFG_CLAUSEFLAG_MULTI }, + { "dnssec-enable", &cfg_type_boolean, 0 }, + { "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI }, + { "dnssec-must-be-secure", &cfg_type_mustbesecure, + CFG_CLAUSEFLAG_MULTI }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found within the 'view' statement only. + */ +static cfg_clausedef_t +view_only_clauses[] = { + { "match-clients", &cfg_type_bracketed_aml, 0 }, + { "match-destinations", &cfg_type_bracketed_aml, 0 }, + { "match-recursive-only", &cfg_type_boolean, 0 }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found in a 'zone' statement, + * with defaults in the 'view' or 'options' statement. + */ +static cfg_clausedef_t +zone_clauses[] = { + { "allow-query", &cfg_type_bracketed_aml, 0 }, + { "allow-transfer", &cfg_type_bracketed_aml, 0 }, + { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 }, + { "allow-notify", &cfg_type_bracketed_aml, 0 }, + { "notify", &cfg_type_notifytype, 0 }, + { "notify-source", &cfg_type_sockaddr4wild, 0 }, + { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, + { "also-notify", &cfg_type_portiplist, 0 }, + { "dialup", &cfg_type_dialuptype, 0 }, + { "forward", &cfg_type_forwardtype, 0 }, + { "forwarders", &cfg_type_portiplist, 0 }, + { "ixfr-from-differences", &cfg_type_boolean, 0 }, + { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE }, + { "max-journal-size", &cfg_type_sizenodefault, 0 }, + { "max-transfer-time-in", &cfg_type_uint32, 0 }, + { "max-transfer-time-out", &cfg_type_uint32, 0 }, + { "max-transfer-idle-in", &cfg_type_uint32, 0 }, + { "max-transfer-idle-out", &cfg_type_uint32, 0 }, + { "max-retry-time", &cfg_type_uint32, 0 }, + { "min-retry-time", &cfg_type_uint32, 0 }, + { "max-refresh-time", &cfg_type_uint32, 0 }, + { "min-refresh-time", &cfg_type_uint32, 0 }, + { "multi-master", &cfg_type_boolean, 0 }, + { "sig-validity-interval", &cfg_type_uint32, 0 }, + { "transfer-source", &cfg_type_sockaddr4wild, 0 }, + { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, + { "alt-transfer-source", &cfg_type_sockaddr4wild, 0 }, + { "alt-transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, + { "use-alt-transfer-source", &cfg_type_boolean, 0 }, + { "zone-statistics", &cfg_type_boolean, 0 }, + { "key-directory", &cfg_type_qstring, 0 }, + { NULL, NULL, 0 } +}; + +/* + * Clauses that can be found in a 'zone' statement + * only. + */ +static cfg_clausedef_t +zone_only_clauses[] = { + { "type", &cfg_type_zonetype, 0 }, + { "allow-update", &cfg_type_bracketed_aml, 0 }, + { "file", &cfg_type_qstring, 0 }, + { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, + { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, + { "masters", &cfg_type_namesockaddrkeylist, 0 }, + { "pubkey", &cfg_type_pubkey, + CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE }, + { "update-policy", &cfg_type_updatepolicy, 0 }, + { "database", &cfg_type_astring, 0 }, + { "delegation-only", &cfg_type_boolean, 0 }, + /* + * Note that the format of the check-names option is different between + * the zone options and the global/view options. Ugh. + */ + { "check-names", &cfg_type_checkmode, 0 }, + { NULL, NULL, 0 } +}; + + +/* The top-level named.conf syntax. */ + +static cfg_clausedef_t * +namedconf_clausesets[] = { + namedconf_clauses, + namedconf_or_view_clauses, + NULL +}; + +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = { + "namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, + &cfg_rep_map, namedconf_clausesets +}; + +/* The "options" statement syntax. */ + +static cfg_clausedef_t * +options_clausesets[] = { + options_clauses, + view_clauses, + zone_clauses, + NULL +}; +static cfg_type_t cfg_type_options = { + "options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, options_clausesets }; + +/* The "view" statement syntax. */ + +static cfg_clausedef_t * +view_clausesets[] = { + view_only_clauses, + namedconf_or_view_clauses, + view_clauses, + zone_clauses, + NULL +}; +static cfg_type_t cfg_type_viewopts = { + "view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, view_clausesets }; + +/* The "zone" statement syntax. */ + +static cfg_clausedef_t * +zone_clausesets[] = { + zone_only_clauses, + zone_clauses, + NULL +}; +static cfg_type_t cfg_type_zoneopts = { + "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets }; + +/* + * Clauses that can be found within the 'key' statement. + */ +static cfg_clausedef_t +key_clauses[] = { + { "algorithm", &cfg_type_astring, 0 }, + { "secret", &cfg_type_astring, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +key_clausesets[] = { + key_clauses, + NULL +}; +static cfg_type_t cfg_type_key = { + "key", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, key_clausesets }; + + +/* + * Clauses that can be found in a 'server' statement. + */ +static cfg_clausedef_t +server_clauses[] = { + { "bogus", &cfg_type_boolean, 0 }, + { "provide-ixfr", &cfg_type_boolean, 0 }, + { "request-ixfr", &cfg_type_boolean, 0 }, + { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "transfers", &cfg_type_uint32, 0 }, + { "transfer-format", &cfg_type_transferformat, 0 }, + { "keys", &cfg_type_server_key_kludge, 0 }, + { "edns", &cfg_type_boolean, 0 }, + { "transfer-source", &cfg_type_sockaddr4wild, 0 }, + { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, + { NULL, NULL, 0 } +}; +static cfg_clausedef_t * +server_clausesets[] = { + server_clauses, + NULL +}; +static cfg_type_t cfg_type_server = { + "server", cfg_parse_addressed_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, + server_clausesets +}; + + +/* + * Clauses that can be found in a 'channel' clause in the + * 'logging' statement. + * + * These have some additional constraints that need to be + * checked after parsing: + * - There must exactly one of file/syslog/null/stderr + * + */ +static cfg_clausedef_t +channel_clauses[] = { + /* Destinations. We no longer require these to be first. */ + { "file", &cfg_type_logfile, 0 }, + { "syslog", &cfg_type_optional_facility, 0 }, + { "null", &cfg_type_void, 0 }, + { "stderr", &cfg_type_void, 0 }, + /* Options. We now accept these for the null channel, too. */ + { "severity", &cfg_type_logseverity, 0 }, + { "print-time", &cfg_type_boolean, 0 }, + { "print-severity", &cfg_type_boolean, 0 }, + { "print-category", &cfg_type_boolean, 0 }, + { NULL, NULL, 0 } +}; +static cfg_clausedef_t * +channel_clausesets[] = { + channel_clauses, + NULL +}; +static cfg_type_t cfg_type_channel = { + "channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map, + &cfg_rep_map, channel_clausesets +}; + +/* A list of log destination, used in the "category" clause. */ +static cfg_type_t cfg_type_destinationlist = { + "destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_astring }; + +/* + * Clauses that can be found in a 'logging' statement. + */ +static cfg_clausedef_t +logging_clauses[] = { + { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI }, + { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI }, + { NULL, NULL, 0 } +}; +static cfg_clausedef_t * +logging_clausesets[] = { + logging_clauses, + NULL +}; +static cfg_type_t cfg_type_logging = { + "logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets }; + + +static isc_result_t +parse_unitstring(char *str, isc_resourcevalue_t *valuep) { + char *endp; + unsigned int len; + isc_uint64_t value; + isc_uint64_t unit; + + value = isc_string_touint64(str, &endp, 10); + if (*endp == 0) { + *valuep = value; + return (ISC_R_SUCCESS); + } + + len = strlen(str); + if (len < 2 || endp[1] != '\0') + return (ISC_R_FAILURE); + + switch (str[len - 1]) { + case 'k': + case 'K': + unit = 1024; + break; + case 'm': + case 'M': + unit = 1024 * 1024; + break; + case 'g': + case 'G': + unit = 1024 * 1024 * 1024; + break; + default: + return (ISC_R_FAILURE); + } + if (value > ISC_UINT64_MAX / unit) + return (ISC_R_FAILURE); + *valuep = value * unit; + return (ISC_R_SUCCESS); +} + +static isc_result_t +parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + cfg_obj_t *obj = NULL; + isc_uint64_t val; + + UNUSED(type); + + CHECK(cfg_gettoken(pctx, 0)); + if (pctx->token.type != isc_tokentype_string) { + result = ISC_R_UNEXPECTEDTOKEN; + goto cleanup; + } + CHECK(parse_unitstring(TOKEN_STRING(pctx), &val)); + + CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj)); + obj->value.uint64 = val; + *ret = obj; + return (ISC_R_SUCCESS); + + cleanup: + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer and optional unit"); + return (result); +} + +/* + * A size value (number + optional unit). + */ +static cfg_type_t cfg_type_sizeval = { + "sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal, + &cfg_rep_uint64, NULL }; + +/* + * A size, "unlimited", or "default". + */ + +static isc_result_t +parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret)); +} + +static const char *size_enums[] = { "unlimited", "default", NULL }; +static cfg_type_t cfg_type_size = { + "size", parse_size, cfg_print_ustring, cfg_doc_terminal, + &cfg_rep_string, size_enums +}; + +/* + * A size or "unlimited", but not "default". + */ +static const char *sizenodefault_enums[] = { "unlimited", NULL }; +static cfg_type_t cfg_type_sizenodefault = { + "size_no_default", parse_size, cfg_print_ustring, cfg_doc_terminal, + &cfg_rep_string, sizenodefault_enums +}; + +/* + * optional_keyvalue + */ +static isc_result_t +parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, + isc_boolean_t optional, cfg_obj_t **ret) +{ + isc_result_t result; + cfg_obj_t *obj = NULL; + const keyword_type_t *kw = type->of; + + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) { + CHECK(cfg_gettoken(pctx, 0)); + CHECK(kw->type->parse(pctx, kw->type, &obj)); + obj->type = type; /* XXX kludge */ + } else { + if (optional) { + CHECK(cfg_parse_void(pctx, NULL, &obj)); + } else { + cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'", + kw->name); + result = ISC_R_UNEXPECTEDTOKEN; + goto cleanup; + } + } + *ret = obj; + cleanup: + return (result); +} + +static isc_result_t +parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, + const cfg_type_t *othertype, cfg_obj_t **ret) +{ + isc_result_t result; + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string && + cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) { + CHECK(cfg_parse_enum(pctx, enumtype, ret)); + } else { + CHECK(cfg_parse_obj(pctx, othertype, ret)); + } + cleanup: + return (result); +} + +static void +doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type) { + cfg_doc_terminal(pctx, type); +#if 0 /* XXX */ + cfg_print_chars(pctx, "( ", 2);... +#endif + +} + +static isc_result_t +parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret)); +} + +static isc_result_t +parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret)); +} + +static void +print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj) { + const keyword_type_t *kw = obj->type->of; + cfg_print_cstr(pctx, kw->name); + cfg_print_chars(pctx, " ", 1); + kw->type->print(pctx, obj); +} + +static void +doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) { + const keyword_type_t *kw = type->of; + cfg_print_cstr(pctx, kw->name); + cfg_print_chars(pctx, " ", 1); + cfg_doc_obj(pctx, kw->type); +} + +static void +doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) { + const keyword_type_t *kw = type->of; + cfg_print_chars(pctx, "[ ", 2); + cfg_print_cstr(pctx, kw->name); + cfg_print_chars(pctx, " ", 1); + cfg_doc_obj(pctx, kw->type); + cfg_print_chars(pctx, " ]", 2); +} + +static const char *dialup_enums[] = { + "notify", "notify-passive", "refresh", "passive", NULL }; +static isc_result_t +parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} +static cfg_type_t cfg_type_dialuptype = { + "dialuptype", parse_dialup_type, cfg_print_ustring, doc_enum_or_other, + &cfg_rep_string, dialup_enums +}; + +static const char *notify_enums[] = { "explicit", NULL }; +static isc_result_t +parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} +static cfg_type_t cfg_type_notifytype = { + "notifytype", parse_notify_type, cfg_print_ustring, doc_enum_or_other, + &cfg_rep_string, notify_enums, +}; + +static keyword_type_t key_kw = { "key", &cfg_type_astring }; + +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = { + "keyref", parse_keyvalue, print_keyvalue, doc_keyvalue, + &cfg_rep_string, &key_kw +}; + +static cfg_type_t cfg_type_optional_keyref = { + "optional_keyref", parse_optional_keyvalue, print_keyvalue, + doc_optional_keyvalue, &cfg_rep_string, &key_kw +}; + +/* + * A "controls" statement is represented as a map with the multivalued + * "inet" and "unix" clauses. Inet controls are tuples; unix controls + * are cfg_unsupported_t objects. + */ + +static keyword_type_t controls_allow_kw = { + "allow", &cfg_type_bracketed_aml }; +static cfg_type_t cfg_type_controls_allow = { + "controls_allow", parse_keyvalue, + print_keyvalue, doc_keyvalue, + &cfg_rep_list, &controls_allow_kw +}; + +static keyword_type_t controls_keys_kw = { + "keys", &cfg_type_keylist }; +static cfg_type_t cfg_type_controls_keys = { + "controls_keys", parse_optional_keyvalue, + print_keyvalue, doc_optional_keyvalue, + &cfg_rep_list, &controls_keys_kw +}; + +static cfg_tuplefielddef_t inetcontrol_fields[] = { + { "address", &cfg_type_controls_sockaddr, 0 }, + { "allow", &cfg_type_controls_allow, 0 }, + { "keys", &cfg_type_controls_keys, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_inetcontrol = { + "inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + inetcontrol_fields +}; + +static cfg_clausedef_t +controls_clauses[] = { + { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI }, + { "unix", &cfg_type_unsupported, + CFG_CLAUSEFLAG_MULTI|CFG_CLAUSEFLAG_NOTIMP }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +controls_clausesets[] = { + controls_clauses, + NULL +}; +static cfg_type_t cfg_type_controls = { + "controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, &controls_clausesets +}; + +/* + * An optional class, as used in view and zone statements. + */ +static isc_result_t +parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + UNUSED(type); + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string) + CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret)); + else + CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); + cleanup: + return (result); +} + +static cfg_type_t cfg_type_optional_class = { + "optional_class", parse_optional_class, NULL, cfg_doc_terminal, + NULL, NULL +}; + +static isc_result_t +parse_querysource(cfg_parser_t *pctx, int flags, cfg_obj_t **ret) { + isc_result_t result; + cfg_obj_t *obj = NULL; + isc_netaddr_t netaddr; + in_port_t port; + unsigned int have_address = 0; + unsigned int have_port = 0; + + if ((flags & CFG_ADDR_V4OK) != 0) + isc_netaddr_any(&netaddr); + else if ((flags & CFG_ADDR_V6OK) != 0) + isc_netaddr_any6(&netaddr); + else + INSIST(0); + + port = 0; + + CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj)); + for (;;) { + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string) { + if (strcasecmp(TOKEN_STRING(pctx), + "address") == 0) + { + /* read "address" */ + CHECK(cfg_gettoken(pctx, 0)); + CHECK(cfg_parse_rawaddr(pctx, + flags | CFG_ADDR_WILDOK, + &netaddr)); + have_address++; + } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) + { + /* read "port" */ + CHECK(cfg_gettoken(pctx, 0)); + CHECK(cfg_parse_rawport(pctx, + CFG_ADDR_WILDOK, + &port)); + have_port++; + } else { + cfg_parser_error(pctx, CFG_LOG_NEAR, + "expected 'address' or 'port'"); + return (ISC_R_UNEXPECTEDTOKEN); + } + } else + break; + } + if (have_address > 1 || have_port > 1 || + have_address + have_port == 0) { + cfg_parser_error(pctx, 0, "expected one address and/or port"); + return (ISC_R_UNEXPECTEDTOKEN); + } + + isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); + *ret = obj; + return (ISC_R_SUCCESS); + + cleanup: + cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source"); + CLEANUP_OBJ(obj); + return (result); +} + +static isc_result_t +parse_querysource4(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + UNUSED(type); + return (parse_querysource(pctx, CFG_ADDR_V4OK, ret)); +} + +static isc_result_t +parse_querysource6(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + UNUSED(type); + return (parse_querysource(pctx, CFG_ADDR_V6OK, ret)); +} + +static void +print_querysource(cfg_printer_t *pctx, cfg_obj_t *obj) { + isc_netaddr_t na; + isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr); + cfg_print_chars(pctx, "address ", 8); + cfg_print_rawaddr(pctx, &na); + cfg_print_chars(pctx, " port ", 6); + cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr)); +} + +static cfg_type_t cfg_type_querysource4 = { + "querysource4", parse_querysource4, NULL, cfg_doc_terminal, + NULL, NULL +}; +static cfg_type_t cfg_type_querysource6 = { + "querysource6", parse_querysource6, NULL, cfg_doc_terminal, + NULL, NULL +}; +static cfg_type_t cfg_type_querysource = { + "querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL }; + +/* addrmatchelt */ + +static isc_result_t +parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + UNUSED(type); + + CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); + + if (pctx->token.type == isc_tokentype_string || + pctx->token.type == isc_tokentype_qstring) { + if (pctx->token.type == isc_tokentype_string && + (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) { + CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret)); + } else { + if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | + CFG_ADDR_V4PREFIXOK | + CFG_ADDR_V6OK)) + { + CHECK(cfg_parse_netprefix(pctx, NULL, ret)); + } else { + CHECK(cfg_parse_astring(pctx, NULL, ret)); + } + } + } else if (pctx->token.type == isc_tokentype_special) { + if (pctx->token.value.as_char == '{') { + /* Nested match list. */ + CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml, ret)); + } else if (pctx->token.value.as_char == '!') { + CHECK(cfg_gettoken(pctx, 0)); /* read "!" */ + CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret)); + } else { + goto bad; + } + } else { + bad: + cfg_parser_error(pctx, CFG_LOG_NEAR, + "expected IP match list element"); + return (ISC_R_UNEXPECTEDTOKEN); + } + cleanup: + return (result); +} + +/* + * A negated address match list element (like "! 10.0.0.1"). + * Somewhat sneakily, the caller is expected to parse the + * "!", but not to print it. + */ + +static cfg_tuplefielddef_t negated_fields[] = { + { "value", &cfg_type_addrmatchelt, 0 }, + { NULL, NULL, 0 } +}; + +static void +print_negated(cfg_printer_t *pctx, cfg_obj_t *obj) { + cfg_print_chars(pctx, "!", 1); + cfg_print_tuple(pctx, obj); +} + +static cfg_type_t cfg_type_negated = { + "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple, + &negated_fields +}; + +/* An address match list element */ + +static cfg_type_t cfg_type_addrmatchelt = { + "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal, + NULL, NULL +}; + +/* A bracketed address match list */ + +static cfg_type_t cfg_type_bracketed_aml = { + "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list, + cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt +}; + +/* + * The socket address syntax in the "controls" statement is silly. + * It allows both socket address families, but also allows "*", + * whis is gratuitously interpreted as the IPv4 wildcard address. + */ +static unsigned int controls_sockaddr_flags = + CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK; +static cfg_type_t cfg_type_controls_sockaddr = { + "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, + cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags +}; + +/* + * Handle the special kludge syntax of the "keys" clause in the "server" + * statement, which takes a single key with or without braces and semicolon. + */ +static isc_result_t +parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) +{ + isc_result_t result; + isc_boolean_t braces = ISC_FALSE; + UNUSED(type); + + /* Allow opening brace. */ + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_special && + pctx->token.value.as_char == '{') { + result = cfg_gettoken(pctx, 0); + braces = ISC_TRUE; + } + + CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret)); + + if (braces) { + /* Skip semicolon if present. */ + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_special && + pctx->token.value.as_char == ';') + CHECK(cfg_gettoken(pctx, 0)); + + CHECK(cfg_parse_special(pctx, '}')); + } + cleanup: + return (result); +} +static cfg_type_t cfg_type_server_key_kludge = { + "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal, + NULL, NULL +}; + + +/* + * An optional logging facility. + */ + +static isc_result_t +parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) +{ + isc_result_t result; + UNUSED(type); + + CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type == isc_tokentype_string || + pctx->token.type == isc_tokentype_qstring) { + CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret)); + } else { + CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); + } + cleanup: + return (result); +} + +static cfg_type_t cfg_type_optional_facility = { + "optional_facility", parse_optional_facility, NULL, cfg_doc_terminal, + NULL, NULL }; + + +/* + * A log severity. Return as a string, except "debug N", + * which is returned as a keyword object. + */ + +static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 }; +static cfg_type_t cfg_type_debuglevel = { + "debuglevel", parse_keyvalue, + print_keyvalue, doc_keyvalue, + &cfg_rep_uint32, &debug_kw +}; + +static isc_result_t +parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + UNUSED(type); + + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(TOKEN_STRING(pctx), "debug") == 0) { + CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */ + CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER)); + if (pctx->token.type == isc_tokentype_number) { + CHECK(cfg_parse_uint32(pctx, NULL, ret)); + } else { + /* + * The debug level is optional and defaults to 1. + * This makes little sense, but we support it for + * compatibility with BIND 8. + */ + CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret)); + (*ret)->value.uint32 = 1; + } + (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */ + } else { + CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret)); + } + cleanup: + return (result); +} + +static cfg_type_t cfg_type_logseverity = { + "log_severity", parse_logseverity, NULL, cfg_doc_terminal, + NULL, NULL }; + +/* + * The "file" clause of the "channel" statement. + * This is yet another special case. + */ + +static const char *logversions_enums[] = { "unlimited", NULL }; +static isc_result_t +parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret)); +} +static cfg_type_t cfg_type_logversions = { + "logversions", parse_logversions, cfg_print_ustring, cfg_doc_terminal, + &cfg_rep_string, logversions_enums +}; + +static cfg_tuplefielddef_t logfile_fields[] = { + { "file", &cfg_type_qstring, 0 }, + { "versions", &cfg_type_logversions, 0 }, + { "size", &cfg_type_size, 0 }, + { NULL, NULL, 0 } +}; + +static isc_result_t +parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + cfg_obj_t *obj = NULL; + const cfg_tuplefielddef_t *fields = type->of; + + CHECK(cfg_create_tuple(pctx, type, &obj)); + + /* Parse the mandatory "file" field */ + CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); + + /* Parse "versions" and "size" fields in any order. */ + for (;;) { + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string) { + CHECK(cfg_gettoken(pctx, 0)); + if (strcasecmp(TOKEN_STRING(pctx), + "versions") == 0 && + obj->value.tuple[1] == NULL) { + CHECK(cfg_parse_obj(pctx, fields[1].type, + &obj->value.tuple[1])); + } else if (strcasecmp(TOKEN_STRING(pctx), + "size") == 0 && + obj->value.tuple[2] == NULL) { + CHECK(cfg_parse_obj(pctx, fields[2].type, + &obj->value.tuple[2])); + } else { + break; + } + } else { + break; + } + } + + /* Create void objects for missing optional values. */ + if (obj->value.tuple[1] == NULL) + CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1])); + if (obj->value.tuple[2] == NULL) + CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2])); + + *ret = obj; + return (ISC_R_SUCCESS); + + cleanup: + CLEANUP_OBJ(obj); + return (result); +} + +static void +print_logfile(cfg_printer_t *pctx, cfg_obj_t *obj) { + cfg_print_obj(pctx, obj->value.tuple[0]); /* file */ + if (obj->value.tuple[1]->type->print != cfg_print_void) { + cfg_print_chars(pctx, " versions ", 10); + cfg_print_obj(pctx, obj->value.tuple[1]); + } + if (obj->value.tuple[2]->type->print != cfg_print_void) { + cfg_print_chars(pctx, " size ", 6); + cfg_print_obj(pctx, obj->value.tuple[2]); + } +} + +static cfg_type_t cfg_type_logfile = { + "log_file", parse_logfile, print_logfile, cfg_doc_terminal, + &cfg_rep_tuple, logfile_fields +}; + +/* An IPv4/IPv6 address with optional port, "*" accepted as wildcard. */ +static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK; +static cfg_type_t cfg_type_sockaddr4wild = { + "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr, + cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags +}; + +static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK; +static cfg_type_t cfg_type_sockaddr6wild = { + "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr, + cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags +}; + +/* + * lwres + */ + +static cfg_tuplefielddef_t lwres_view_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "class", &cfg_type_optional_class, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_lwres_view = { + "lwres_view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + lwres_view_fields +}; + +static cfg_type_t cfg_type_lwres_searchlist = { + "lwres_searchlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_astring }; + +static cfg_clausedef_t +lwres_clauses[] = { + { "listen-on", &cfg_type_portiplist, 0 }, + { "view", &cfg_type_lwres_view, 0 }, + { "search", &cfg_type_lwres_searchlist, 0 }, + { "ndots", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +lwres_clausesets[] = { + lwres_clauses, + NULL +}; +static cfg_type_t cfg_type_lwres = { + "lwres", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, lwres_clausesets }; + +/* + * rndc + */ + +static cfg_clausedef_t +rndcconf_options_clauses[] = { + { "default-server", &cfg_type_astring, 0 }, + { "default-key", &cfg_type_astring, 0 }, + { "default-port", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +rndcconf_options_clausesets[] = { + rndcconf_options_clauses, + NULL +}; + +static cfg_type_t cfg_type_rndcconf_options = { + "rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, + rndcconf_options_clausesets +}; + +static cfg_clausedef_t +rndcconf_server_clauses[] = { + { "key", &cfg_type_astring, 0 }, + { "port", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +rndcconf_server_clausesets[] = { + rndcconf_server_clauses, + NULL +}; + +static cfg_type_t cfg_type_rndcconf_server = { + "rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, + rndcconf_server_clausesets +}; + +static cfg_clausedef_t +rndcconf_clauses[] = { + { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, + { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI }, + { "options", &cfg_type_rndcconf_options, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +rndcconf_clausesets[] = { + rndcconf_clauses, + NULL +}; + +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = { + "rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, + &cfg_rep_map, rndcconf_clausesets +}; + +static cfg_clausedef_t +rndckey_clauses[] = { + { "key", &cfg_type_key, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t * +rndckey_clausesets[] = { + rndckey_clauses, + NULL +}; + +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = { + "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, + &cfg_rep_map, rndckey_clausesets +}; + +static cfg_tuplefielddef_t nameport_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "port", &cfg_type_optional_port, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_nameport = { + "nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, nameport_fields +}; + +static void +doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + cfg_print_chars(pctx, "( ", 2); + cfg_print_cstr(pctx, ""); + cfg_print_chars(pctx, " ", 1); + cfg_print_cstr(pctx, "[port ]"); + cfg_print_chars(pctx, " | ", 3); + cfg_print_cstr(pctx, ""); + cfg_print_chars(pctx, " ", 1); + cfg_print_cstr(pctx, "[port ]"); + cfg_print_chars(pctx, " | ", 3); + cfg_print_cstr(pctx, ""); + cfg_print_chars(pctx, " ", 1); + cfg_print_cstr(pctx, "[port ]"); + cfg_print_chars(pctx, " )", 2); +} + +static isc_result_t +parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) +{ + isc_result_t result; + cfg_obj_t *obj = NULL; + UNUSED(type); + + CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type == isc_tokentype_string || + pctx->token.type == isc_tokentype_qstring) { + if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK)) + CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret)); + else { + const cfg_tuplefielddef_t *fields = + cfg_type_nameport.of; + CHECK(cfg_create_tuple(pctx, &cfg_type_nameport, + &obj)); + CHECK(cfg_parse_obj(pctx, fields[0].type, + &obj->value.tuple[0])); + CHECK(cfg_parse_obj(pctx, fields[1].type, + &obj->value.tuple[1])); + *ret = obj; + obj = NULL; + } + } else { + cfg_parser_error(pctx, CFG_LOG_NEAR, + "expected IP address or hostname"); + return (ISC_R_UNEXPECTEDTOKEN); + } + cleanup: + CLEANUP_OBJ(obj); + return (result); +} + +static cfg_type_t cfg_type_sockaddrnameport = { + "sockaddrnameport_element", parse_sockaddrnameport, NULL, + doc_sockaddrnameport, NULL, NULL +}; + +static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = { + "bracketed_sockaddrnameportlist", cfg_parse_bracketed_list, + cfg_print_bracketed_list, cfg_doc_bracketed_list, + &cfg_rep_list, &cfg_type_sockaddrnameport +}; + +/* + * A list of socket addresses or name with an optional default port, + * as used in the dual-stack-servers option. E.g., + * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }" + */ +static cfg_tuplefielddef_t nameportiplist_fields[] = { + { "port", &cfg_type_optional_port, 0 }, + { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_nameportiplist = { + "nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, nameportiplist_fields +}; + +/* + * masters element. + */ + +static void +doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + cfg_print_chars(pctx, "( ", 2); + cfg_print_cstr(pctx, ""); + cfg_print_chars(pctx, " | ", 3); + cfg_print_cstr(pctx, ""); + cfg_print_chars(pctx, " ", 1); + cfg_print_cstr(pctx, "[port ]"); + cfg_print_chars(pctx, " | ", 3); + cfg_print_cstr(pctx, ""); + cfg_print_chars(pctx, " ", 1); + cfg_print_cstr(pctx, "[port ]"); + cfg_print_chars(pctx, " )", 2); +} + +static isc_result_t +parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) +{ + isc_result_t result; + cfg_obj_t *obj = NULL; + UNUSED(type); + + CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type == isc_tokentype_string || + pctx->token.type == isc_tokentype_qstring) { + if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK)) + CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret)); + else + CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret)); + } else { + cfg_parser_error(pctx, CFG_LOG_NEAR, + "expected IP address or masters name"); + return (ISC_R_UNEXPECTEDTOKEN); + } + cleanup: + CLEANUP_OBJ(obj); + return (result); +} + +static cfg_type_t cfg_type_masterselement = { + "masters_element", parse_masterselement, NULL, + doc_masterselement, NULL, NULL +}; diff -Nuar bind-9.3.2-orig/lib/isccfg/parser.c bind-9.3.2-mod/lib/isccfg/parser.c --- bind-9.3.2-orig/lib/isccfg/parser.c 2004-05-15 05:46:13.000000000 +0200 +++ bind-9.3.2-mod/lib/isccfg/parser.c 2006-02-20 19:13:55.000000000 +0100 @@ -1085,7 +1085,6 @@ } } - isc_boolean_t cfg_obj_islist(cfg_obj_t *obj) { REQUIRE(obj != NULL);