From 9336f01769f16a8eda79340094d663db0f8537c7 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 20 Oct 2011 22:01:48 +0000 Subject: [PATCH] 3176. [doc] Corrected example code and added a README to the sample external DLZ module in contrib/dlz/example. [RT #26215] --- CHANGES | 4 + contrib/dlz/example/README | 180 ++++++++++ contrib/dlz/example/dlz_example.c | 561 ++++++++++++++++++------------ contrib/dlz/example/dlz_minimal.h | 105 +++++- contrib/dlz/example/named.conf | 50 +++ 5 files changed, 663 insertions(+), 237 deletions(-) create mode 100644 contrib/dlz/example/README create mode 100644 contrib/dlz/example/named.conf diff --git a/CHANGES b/CHANGES index 4b5089fecf..aeefe843f0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +3176. [doc] Corrected example code and added a README to the + sample external DLZ module in contrib/dlz/example. + [RT #26215] + 3175. [bug] Fix how DNSSEC positive wildcard responses from a NSEC3 signed zone are validated. Stop sending a unnecessary NSEC3 record when generating such diff --git a/contrib/dlz/example/README b/contrib/dlz/example/README new file mode 100644 index 0000000000..42d38ce455 --- /dev/null +++ b/contrib/dlz/example/README @@ -0,0 +1,180 @@ +OVERVIEW: + +DLZ (Dynamically Loadable Zones) is an extention to BIND 9 that +allows zone data to be retrieved directly from an external database. +There is no required format or schema. DLZ drivers exist for several +different database backends including PostgreSQL, MySQL, and LDAP and +can be written for any other. + +Historically, DLZ drivers had to be statically linked with the named +binary and were turned on via a configure option at compile time (for +example, "configure --with-dlz-ldap"). Currently, the drivers provided +in the BIND 9 tarball in contrib/dlz/drivers are still linked this way. + +However, as of BIND 9.8, it is also possible to link some DLZ modules +dynamically at runtime, via the DLZ "dlopen" driver, which acts as a +generic wrapper around a shared object that implements the DLZ API. The +"dlopen" driver is linked into named by default, so configure options are +no longer necessary. + +When the DLZ module provides data to named, it does so in text format. +The response is converted to DNS wire format by named. This conversion, +and the lack of any internal caching, places significant limits on the +query performance of DLZ modules. Consequently, DLZ is not recommended +for use on high-volume servers. However, it can be used in a hidden +master configuration, with slaves retrieving zone updates via AXFR. +(Note, however, that DLZ has no built-in support for DNS notify; slaves +are not automatically informed of changes to the zones in the database.) + +EXAMPLE DRIVER: + +This directory contains an example of an externally-lodable DLZ module, +dlz_example.c, which demonstrates the features of the DLZ API. It sets up +a single zone, whose name is configured in named.conf. The zone can answer +queries and AXFR requests, and accept DDNS updates. + +By default, at runtime, the zone implemented by this driver will contain +an SOA, NS, and a single A record at the apex. If configured in named.conf +to use the name "example.nil", then, the zone will look like this: + + example.nil. 3600 IN SOA example.nil. hostmaster.example.nil. ( + 123 900 600 86400 3600 + ) + example.nil. 3600 IN NS example.nil. + example.nil. 1800 IN A 10.53.0.1 + +The driver is also capable of retrieving information about the querying +client, and altering its response on the basis of this information. To +demonstrate this feature, the example driver responds to queries for +"source-addr./TXT" with the source address of the query. +Note, however, that this record will *not* be included in AXFR or ANY +responses. (Normally, this feature would be used to alter responses in +some other fashion, e.g., by providing different address records for +a particular name depending on the network from which the query arrived.) + +IMPLEMENTATION NOTES: + +The minimal set of type definitions, prototypes, and macros needed +for implementing a DLZ driver is in dlz_minimal.h. Copy this header +file into your source tree when creating an external DLZ module. + +The DLZ dlopen driver provides a set of callback functions: + + - void log(int level, const char *fmt, ...); + + Writes the specified string to the named log, at the specified + log level. Uses printf() format semantics. + + - isc_result_t putrr(dns_sdlzlookup_t *lookup, const char *type, + dns_ttl_t ttl, const char *data); + + Puts a DNS resource record into the query response, which + referenced by the opaque structure 'lookup' provided by named. + + - isc_result_t putnamedrr(dns_sdlzallnotes_t *allnodes, + const char *name, const char *type, + dns_ttl_t ttl, const char *data); + + Puts a DNS resource record into an AXFR response, which is + referenced by the opaque structure 'allnodes' provided by named. + + - isc_result_t writable_zone(dns_view_t *view, const char *zone_name); + + Allows the DLZ module to inform named that a given zone can recieve + DDNS updates. + +The external DLZ module can define the following functions (some of these +are mandatory, others optional). + + - int dlz_version(unsigned int *flags); + + Required for alL external DLZ modules, to indicate the version number + of the DLZ dlopen driver that this module supports. It should return + the value DLZ_DLOPEN_VERSION, which is defined in dlz_minimal.h and + is currently 2. 'flags' is updated to indicate capabilities + of the module. In particular, if the module is thread-safe then it + sets 'flags' to include DNS_SDLZFLAG_THREADSAFE. (Other capability + flags may be added in the future.) + + - isc_result_t dlz_create(const char *dlzname, + unsigned int argc, char *argv[], + void **dbdata, ...); + + Required for all external DLZ modules; this call initializes the + module. + + - void dlz_destroy(void *dbdata); + + Optional. If supplied, this will be called when the driver is + unloaded. + + - isc_result_t dlz_findzonedb(void *dbdata, const char *name); + + Required for all external DLZ modules. This indicates whether the + DLZ module can answer for a given zone. Returns ISC_R_SUCCESS if + so, otherwise ISC_R_NOTFOUND. + + - isc_result_t dlz_lookup(const char *zone, const char *name, void *dbdata, + dns_sdlzlookup_t *lookup, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo); + + Required for all external DLZ modules. This carries out the database + lookup for a query. + + - isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, + const char *client); + + Optional. Supply this if you want the module to support AXFR + for the specified zone and client. A return value of ISC_R_SUCCESS + means AXFR is allowed, any other value means it isn't. + + - isc_result_t dlz_allnodes(const char *zone, void *dbdata, + dns_sdlzallnodes_t *allnodes); + + Optional, but must be supplied dlz_allowzonexfr() is. This function + returns all nodes in the zone in order to perform a zone transfer. + + - isc_result_t dlz_newversion(const char *zone, void *dbdata, + void **versionp); + + Optional. Supply this if you want the module to support DDNS + updates. This function starts a transaction in the database. + + + - void dlz_closeversion(const char *zone, isc_boolean_t commit, + void *dbdata, void **versionp); + + Optional, but must be supplied if dlz_newversion() is. This function + closes a transaction. 'commit' indicates whether to commit the changes + to the database, or ignore them. + + - isc_result_t dlz_configure(dns_view_t *view, void *dbdata); + + Optional, but must be supplied in order to support DDNS updates. + + - isc_boolean_t dlz_ssumatch(const char *signer, const char *name, + const char *tcpaddr, const char *type, + const char *key, uint32_t keydatalen, + uint8_t *keydata, void *dbdata); + + Optional, but must be supplied in order to support DDNS updates. + + - isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, + void *dbdata, void *version); + + Optional, but must be supplied in order to support DDNS updates. + Adds the data in 'rdatastr' to a database node. + + - isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, + void *dbdata, void *version); + + Optional, but must be supplied in order to support DDNS updates. + Removes the data in 'rdatastr' from a database node. + + - isc_result_t dlz_delrdataset(const char *name, const char *rdatastr, + void *dbdata, void *version); + + Optional, but must be supplied in order to support DDNS updates. + Deletes all data matching the type specified in 'rdatastr' from + the database. diff --git a/contrib/dlz/example/dlz_example.c b/contrib/dlz/example/dlz_example.c index c8e468010a..0c44e64af4 100644 --- a/contrib/dlz/example/dlz_example.c +++ b/contrib/dlz/example/dlz_example.c @@ -1,42 +1,48 @@ /* - * Copyright (C) 2010 Andrew Tridgell + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") * - * 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. + * Permission to use, copy, modify, and/or 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 THE AUTHOR - * 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 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: dlz_example.c,v 1.3 2011/10/20 22:01:48 each Exp $ */ + /* - this provides a very simple example of an external loadable DLZ - driver, with update support + * This provides a very simple example of an external loadable DLZ + * driver, with update support. */ #include #include #include -#include -#include #include +#include #include "dlz_minimal.h" +#ifdef WIN32 +#define STRTOK_R(a, b, c) strtok_s(a, b, c) +#elif defined(_REENTRANT) +#define STRTOK_R(a, b, c) strtok_r(a, b, c) +#else +#define STRTOK_R(a, b, c) strtok(a, b) +#endif -/* for this simple example, use fixed sized strings */ +/* For this simple example, use fixed sized strings */ struct record { char name[100]; char type[10]; char data[200]; - uint32_t ttl; + dns_ttl_t ttl; }; #define MAX_RECORDS 100 @@ -44,50 +50,49 @@ struct record { struct dlz_example_data { char *zone_name; - /* an example driver doesn't need good memory management :-) */ + /* An example driver doesn't need good memory management :-) */ struct record current[MAX_RECORDS]; struct record adds[MAX_RECORDS]; struct record deletes[MAX_RECORDS]; - bool transaction_started; + isc_boolean_t transaction_started; - /* helper functions from the dlz_dlopen driver */ - void (*log)(int level, const char *fmt, ...); - isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type, - dns_ttl_t ttl, const char *data); - isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name, - const char *type, dns_ttl_t ttl, const char *data); - isc_result_t (*writeable_zone)(dns_view_t *view, const char *zone_name); + /* Helper functions from the dlz_dlopen driver */ + log_t *log; + dns_sdlz_putrr_t *putrr; + dns_sdlz_putnamedrr_t *putnamedrr; + dns_dlz_writeablezone_t *writeable_zone; }; -static bool single_valued(const char *type) -{ +static isc_boolean_t +single_valued(const char *type) { const char *single[] = { "soa", "cname", NULL }; int i; - for (i=0; single[i]; i++) { + + for (i = 0; single[i]; i++) { if (strcasecmp(single[i], type) == 0) { - return true; + return (ISC_TRUE); } } - return false; + return (ISC_FALSE); } /* - add a record to a list + * Add a record to a list */ -static isc_result_t add_name(struct dlz_example_data *state, - struct record *list, const char *name, const char *type, - uint32_t ttl, const char *data) +static isc_result_t +add_name(struct dlz_example_data *state, struct record *list, + const char *name, const char *type, dns_ttl_t ttl, const char *data) { int i; - bool single = single_valued(type); + isc_boolean_t single = single_valued(type); int first_empty = -1; - for (i=0; ilog(ISC_LOG_ERROR, "dlz_example: out of record space"); - return ISC_R_FAILURE; + return (ISC_R_FAILURE); } strcpy(list[i].name, name); strcpy(list[i].type, type); strcpy(list[i].data, data); list[i].ttl = ttl; - return ISC_R_SUCCESS; + return (ISC_R_SUCCESS); } /* - delete a record from a list + * Delete a record from a list */ -static isc_result_t del_name(struct dlz_example_data *state, - struct record *list, const char *name, const char *type, - uint32_t ttl, const char *data) +static isc_result_t +del_name(struct dlz_example_data *state, struct record *list, + const char *name, const char *type, dns_ttl_t ttl, + const char *data) { int i; - for (i=0; itype.sa.sa_family) { + case AF_INET: + port = ntohs(addr->type.sin.sin_port); + ret = inet_ntop(AF_INET, &addr->type.sin.sin_addr, addr_buf, + sizeof(addr_buf)); + break; + case AF_INET6: + port = ntohs(addr->type.sin6.sin6_port); + ret = inet_ntop(AF_INET6, &addr->type.sin6.sin6_addr, addr_buf, + sizeof(addr_buf)); + break; + default: + return (ISC_R_FAILURE); + } + + if (ret == NULL) + return (ISC_R_FAILURE); + + snprintf(buffer, size, "%s#%u", addr_buf, port); + return (ISC_R_SUCCESS); +} /* - return the version of the API + * Return the version of the API */ -int dlz_version(unsigned int *flags) +int +dlz_version(unsigned int *flags) { + UNUSED(flags); + return (DLZ_DLOPEN_VERSION); +} + +/* + * Remember a helper function from the bind9 dlz_dlopen driver + */ +static void +b9_add_helper(struct dlz_example_data *state, + const char *helper_name, void *ptr) { - return DLZ_DLOPEN_VERSION; -} - -/* - remember a helper function from the bind9 dlz_dlopen driver - */ -static void b9_add_helper(struct dlz_example_data *state, const char *helper_name, void *ptr) -{ - if (strcmp(helper_name, "log") == 0) { - state->log = ptr; - } - if (strcmp(helper_name, "putrr") == 0) { - state->putrr = ptr; - } - if (strcmp(helper_name, "putnamedrr") == 0) { - state->putnamedrr = ptr; - } - if (strcmp(helper_name, "writeable_zone") == 0) { - state->writeable_zone = ptr; - } + if (strcmp(helper_name, "log") == 0) + state->log = (log_t *)ptr; + if (strcmp(helper_name, "putrr") == 0) + state->putrr = (dns_sdlz_putrr_t *)ptr; + if (strcmp(helper_name, "putnamedrr") == 0) + state->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr; + if (strcmp(helper_name, "writeable_zone") == 0) + state->writeable_zone = (dns_dlz_writeablezone_t *)ptr; } /* - called to initialise the driver + * Called to initialize the driver */ -isc_result_t dlz_create(const char *dlzname, unsigned int argc, char *argv[], - void **dbdata, ...) +isc_result_t +dlz_create(const char *dlzname, unsigned int argc, char *argv[], + void **dbdata, ...) { struct dlz_example_data *state; const char *helper_name; va_list ap; char soa_data[200]; - state = calloc(1, sizeof(struct dlz_example_data)); - if (state == NULL) { - return ISC_R_NOMEMORY; - } + UNUSED(dlzname); - /* fill in the helper functions */ + state = calloc(1, sizeof(struct dlz_example_data)); + if (state == NULL) + return (ISC_R_NOMEMORY); + + /* Fill in the helper functions */ va_start(ap, dbdata); while ((helper_name = va_arg(ap, const char *)) != NULL) { b9_add_helper(state, helper_name, va_arg(ap, void*)); @@ -186,8 +222,9 @@ isc_result_t dlz_create(const char *dlzname, unsigned int argc, char *argv[], va_end(ap); if (argc < 2) { - state->log(ISC_LOG_ERROR, "dlz_example: please specify a zone name"); - return ISC_R_FAILURE; + state->log(ISC_LOG_ERROR, + "dlz_example: please specify a zone name"); + return (ISC_R_FAILURE); } state->zone_name = strdup(argv[1]); @@ -195,169 +232,218 @@ isc_result_t dlz_create(const char *dlzname, unsigned int argc, char *argv[], sprintf(soa_data, "%s hostmaster.%s 123 900 600 86400 3600", state->zone_name, state->zone_name); - add_name(state, &state->current[0], state->zone_name, "soa", 3600, soa_data); - add_name(state, &state->current[0], state->zone_name, "ns", 3600, state->zone_name); - add_name(state, &state->current[0], state->zone_name, "a", 1800, "10.53.0.1"); + add_name(state, &state->current[0], state->zone_name, + "soa", 3600, soa_data); + add_name(state, &state->current[0], state->zone_name, + "ns", 3600, state->zone_name); + add_name(state, &state->current[0], state->zone_name, + "a", 1800, "10.53.0.1"); - state->log(ISC_LOG_INFO, "dlz_example: started for zone %s", state->zone_name); + state->log(ISC_LOG_INFO, + "dlz_example: started for zone %s", + state->zone_name); *dbdata = state; - return ISC_R_SUCCESS; + return (ISC_R_SUCCESS); } /* - shutdown the backend + * Shut down the backend */ -void dlz_destroy(void *dbdata) -{ +void +dlz_destroy(void *dbdata) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; - state->log(ISC_LOG_INFO, "dlz_example: shutting down zone %s", state->zone_name); + + state->log(ISC_LOG_INFO, + "dlz_example: shutting down zone %s", + state->zone_name); free(state->zone_name); free(state); } /* - see if we handle a given zone + * See if we handle a given zone */ -isc_result_t dlz_findzonedb(void *dbdata, const char *name) -{ +isc_result_t +dlz_findzonedb(void *dbdata, const char *name) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; - if (strcasecmp(state->zone_name, name) == 0) { - return ISC_R_SUCCESS; - } - return ISC_R_NOTFOUND; + + if (strcasecmp(state->zone_name, name) == 0) + return (ISC_R_SUCCESS); + + return (ISC_R_NOTFOUND); } - - /* - lookup one record + * Look up one record in the sample database. + * + * If the queryname is "source-addr", we add a TXT record containing + * the address of the client; this demonstrates the use of 'methods' + * and 'clientinfo'. */ -isc_result_t dlz_lookup(const char *zone, const char *name, - void *dbdata, dns_sdlzlookup_t *lookup) +isc_result_t +dlz_lookup(const char *zone, const char *name, void *dbdata, + dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) { + isc_result_t result; struct dlz_example_data *state = (struct dlz_example_data *)dbdata; - int i; - bool found = false; + isc_boolean_t found = ISC_FALSE; + isc_sockaddr_t *src; char full_name[100]; - - if (strcmp(name, "@") == 0) { + int i; + + UNUSED(zone); + + if (strcmp(name, "@") == 0) strcpy(full_name, state->zone_name); - } else { + else sprintf(full_name, "%s.%s", name, state->zone_name); + + if (strcmp(name, "source-addr") == 0) { + char buf[100]; + strcpy(buf, "unknown"); + if (methods != NULL && + methods->version - methods->age >= + DNS_CLIENTINFOMETHODS_VERSION) + { + methods->sourceip(clientinfo, &src); + fmt_address(src, buf, sizeof(buf)); + } + + fprintf(stderr, "connection from: %s\n", buf); + + found = ISC_TRUE; + result = state->putrr(lookup, "TXT", 0, buf); + if (result != ISC_R_SUCCESS) + return (result); } - for (i=0; icurrent[i].name, full_name) == 0) { - isc_result_t result; - found = true; - result = state->putrr(lookup, state->current[i].type, - state->current[i].ttl, state->current[i].data); - if (result != ISC_R_SUCCESS) { - return result; - } + found = ISC_TRUE; + result = state->putrr(lookup, state->current[i].type, + state->current[i].ttl, + state->current[i].data); + if (result != ISC_R_SUCCESS) + return (result); } } - if (!found) { - return ISC_R_NOTFOUND; - } - return ISC_R_SUCCESS; + + if (!found) + return (ISC_R_NOTFOUND); + + return (ISC_R_SUCCESS); } /* - see if a zone transfer is allowed + * See if a zone transfer is allowed */ -isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client) -{ - /* just say yes for all our zones */ - return dlz_findzonedb(dbdata, name); +isc_result_t +dlz_allowzonexfr(void *dbdata, const char *name, const char *client) { + UNUSED(client); + + /* Just say yes for all our zones */ + return (dlz_findzonedb(dbdata, name)); } /* - perform a zone transfer + * Perform a zone transfer */ -isc_result_t dlz_allnodes(const char *zone, void *dbdata, - dns_sdlzallnodes_t *allnodes) -{ +isc_result_t +dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; int i; - for (i=0; icurrent[i].name) == 0) { + if (strlen(state->current[i].name) == 0U) { continue; } - result = state->putnamedrr(allnodes, state->current[i].name, state->current[i].type, - state->current[i].ttl, state->current[i].data); - if (result != ISC_R_SUCCESS) { - return result; - } + result = state->putnamedrr(allnodes, state->current[i].name, + state->current[i].type, + state->current[i].ttl, + state->current[i].data); + if (result != ISC_R_SUCCESS) + return (result); } - return ISC_R_SUCCESS; + return (ISC_R_SUCCESS); } /* - start a transaction + * Start a transaction */ -isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp) -{ +isc_result_t +dlz_newversion(const char *zone, void *dbdata, void **versionp) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; if (state->transaction_started) { - state->log(ISC_LOG_INFO, "dlz_example: transaction already started for zone %s", zone); - return ISC_R_FAILURE; + state->log(ISC_LOG_INFO, + "dlz_example: transaction already " + "started for zone %s", zone); + return (ISC_R_FAILURE); } - state->transaction_started = true; - + state->transaction_started = ISC_TRUE; *versionp = (void *) &state->transaction_started; - return ISC_R_SUCCESS; + return (ISC_R_SUCCESS); } /* - end a transaction + * End a transaction */ -void dlz_closeversion(const char *zone, isc_boolean_t commit, void *dbdata, void **versionp) +void +dlz_closeversion(const char *zone, isc_boolean_t commit, + void *dbdata, void **versionp) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; if (!state->transaction_started) { - state->log(ISC_LOG_INFO, "dlz_example: transaction not started for zone %s", zone); + state->log(ISC_LOG_INFO, + "dlz_example: transaction not started for zone %s", + zone); *versionp = NULL; return; } - state->transaction_started = false; + state->transaction_started = ISC_FALSE; *versionp = NULL; if (commit) { int i; - state->log(ISC_LOG_INFO, "dlz_example: committing transaction on zone %s", zone); - for (i=0; iadds[i].name) > 0) { - add_name(state, &state->current[0], - state->adds[i].name, - state->adds[i].type, - state->adds[i].ttl, + state->log(ISC_LOG_INFO, + "dlz_example: committing transaction on zone %s", + zone); + for (i = 0; i < MAX_RECORDS; i++) { + if (strlen(state->adds[i].name) > 0U) { + add_name(state, &state->current[0], + state->adds[i].name, + state->adds[i].type, + state->adds[i].ttl, state->adds[i].data); } } - for (i=0; ideletes[i].name) > 0) { - del_name(state, &state->current[0], - state->deletes[i].name, - state->deletes[i].type, - state->deletes[i].ttl, + for (i = 0; i < MAX_RECORDS; i++) { + if (strlen(state->deletes[i].name) > 0U) { + del_name(state, &state->current[0], + state->deletes[i].name, + state->deletes[i].type, + state->deletes[i].ttl, state->deletes[i].data); } } } else { - state->log(ISC_LOG_INFO, "dlz_example: cancelling transaction on zone %s", zone); + state->log(ISC_LOG_INFO, + "dlz_example: cancelling transaction on zone %s", + zone); } memset(state->adds, 0, sizeof(state->adds)); memset(state->deletes, 0, sizeof(state->deletes)); @@ -365,118 +451,155 @@ void dlz_closeversion(const char *zone, isc_boolean_t commit, void *dbdata, void /* - configure a writeable zone + * Configure a writeable zone */ -isc_result_t dlz_configure(dns_view_t *view, void *dbdata) -{ +isc_result_t +dlz_configure(dns_view_t *view, void *dbdata) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; isc_result_t result; state->log(ISC_LOG_INFO, "dlz_example: starting configure"); if (state->writeable_zone == NULL) { - state->log(ISC_LOG_INFO, "dlz_example: no writeable_zone method available"); - return ISC_R_FAILURE; + state->log(ISC_LOG_INFO, + "dlz_example: no writeable_zone method available"); + return (ISC_R_FAILURE); } result = state->writeable_zone(view, state->zone_name); if (result != ISC_R_SUCCESS) { - state->log(ISC_LOG_ERROR, "dlz_example: failed to configure zone %s", state->zone_name); - return result; + state->log(ISC_LOG_ERROR, + "dlz_example: failed to configure zone %s", + state->zone_name); + return (result); } - state->log(ISC_LOG_INFO, "dlz_example: configured writeable zone %s", state->zone_name); - return ISC_R_SUCCESS; + state->log(ISC_LOG_INFO, + "dlz_example: configured writeable zone %s", + state->zone_name); + return (ISC_R_SUCCESS); } /* - authorize a zone update + * Authorize a zone update */ -isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, - const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata, - void *dbdata) +isc_boolean_t +dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, + const char *type, const char *key, uint32_t keydatalen, + unsigned char *keydata, void *dbdata) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; + + UNUSED(tcpaddr); + UNUSED(type); + UNUSED(key); + UNUSED(keydatalen); + UNUSED(keydata); + if (strncmp(name, "deny.", 5) == 0) { - state->log(ISC_LOG_INFO, "dlz_example: denying update of name=%s by %s", + state->log(ISC_LOG_INFO, + "dlz_example: denying update of name=%s by %s", name, signer); - return false; + return (ISC_FALSE); } - state->log(ISC_LOG_INFO, "dlz_example: allowing update of name=%s by %s", + state->log(ISC_LOG_INFO, + "dlz_example: allowing update of name=%s by %s", name, signer); - return true; + return (ISC_TRUE); } -static isc_result_t modrdataset(struct dlz_example_data *state, const char *name, const char *rdatastr, - struct record *list) +static isc_result_t +modrdataset(struct dlz_example_data *state, const char *name, + const char *rdatastr, struct record *list) { char *full_name, *dclass, *type, *data, *ttlstr; char *buf = strdup(rdatastr); isc_result_t result; +#if defined(WIN32) || defined(_REENTRANT) char *saveptr = NULL; +#endif /* - the format is: - FULLNAME\tTTL\tDCLASS\tTYPE\tDATA - - The DATA field is space separated, and is in the data format - for the type used by dig + * The format is: + * FULLNAME\tTTL\tDCLASS\tTYPE\tDATA + * + * The DATA field is space separated, and is in the data format + * for the type used by dig */ - full_name = strtok_r(buf, "\t", &saveptr); - if (full_name == NULL) return ISC_R_FAILURE; - ttlstr = strtok_r(NULL, "\t", &saveptr); - if (ttlstr == NULL) return ISC_R_FAILURE; - dclass = strtok_r(NULL, "\t", &saveptr); - if (dclass == NULL) return ISC_R_FAILURE; - type = strtok_r(NULL, "\t", &saveptr); - if (type == NULL) return ISC_R_FAILURE; - data = strtok_r(NULL, "\t", &saveptr); - if (data == NULL) return ISC_R_FAILURE; + full_name = STRTOK_R(buf, "\t", &saveptr); + if (full_name == NULL) + return (ISC_R_FAILURE); - result = add_name(state, list, name, type, strtoul(ttlstr, NULL, 10), data); + ttlstr = STRTOK_R(NULL, "\t", &saveptr); + if (ttlstr == NULL) + return (ISC_R_FAILURE); + + dclass = STRTOK_R(NULL, "\t", &saveptr); + if (dclass == NULL) + return (ISC_R_FAILURE); + + type = STRTOK_R(NULL, "\t", &saveptr); + if (type == NULL) + return (ISC_R_FAILURE); + + data = STRTOK_R(NULL, "\t", &saveptr); + if (data == NULL) + return (ISC_R_FAILURE); + + result = add_name(state, list, name, type, + strtoul(ttlstr, NULL, 10), data); free(buf); - return result; + return (result); } -isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version) +isc_result_t +dlz_addrdataset(const char *name, const char *rdatastr, + void *dbdata, void *version) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; - if (version != (void *) &state->transaction_started) { - return ISC_R_FAILURE; - } + if (version != (void *) &state->transaction_started) + return (ISC_R_FAILURE); - state->log(ISC_LOG_INFO, "dlz_example: adding rdataset %s '%s'", name, rdatastr); + state->log(ISC_LOG_INFO, + "dlz_example: adding rdataset %s '%s'", + name, rdatastr); - return modrdataset(state, name, rdatastr, &state->adds[0]); + return (modrdataset(state, name, rdatastr, &state->adds[0])); } -isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version) +isc_result_t +dlz_subrdataset(const char *name, const char *rdatastr, + void *dbdata, void *version) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; - if (version != (void *) &state->transaction_started) { - return ISC_R_FAILURE; - } + if (version != (void *) &state->transaction_started) + return (ISC_R_FAILURE); - state->log(ISC_LOG_INFO, "dlz_example: subtracting rdataset %s '%s'", name, rdatastr); - - return modrdataset(state, name, rdatastr, &state->deletes[0]); + state->log(ISC_LOG_INFO, + "dlz_example: subtracting rdataset %s '%s'", + name, rdatastr); + + return (modrdataset(state, name, rdatastr, &state->deletes[0])); } -isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version) +isc_result_t +dlz_delrdataset(const char *name, const char *type, + void *dbdata, void *version) { struct dlz_example_data *state = (struct dlz_example_data *)dbdata; - if (version != (void *) &state->transaction_started) { - return ISC_R_FAILURE; - } + if (version != (void *) &state->transaction_started) + return (ISC_R_FAILURE); - state->log(ISC_LOG_INFO, "dlz_example: deleting rdataset %s of type %s", name, type); - - return ISC_R_SUCCESS; + state->log(ISC_LOG_INFO, + "dlz_example: deleting rdataset %s of type %s", + name, type); + + return (ISC_R_SUCCESS); } diff --git a/contrib/dlz/example/dlz_minimal.h b/contrib/dlz/example/dlz_minimal.h index 4337bdeb83..a1fc7952d5 100644 --- a/contrib/dlz/example/dlz_minimal.h +++ b/contrib/dlz/example/dlz_minimal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Andrew Tridgell + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the @@ -17,13 +17,23 @@ */ /* - This header provides a minimal set of defines and typedefs needed - for building an external DLZ module for bind9. When creating a new - external DLZ driver, please copy this header into your own source - tree. + * This header provides a minimal set of defines and typedefs needed + * for building an external DLZ module for bind9. When creating a new + * external DLZ driver, please copy this header into your own source + * tree. */ + +#include +#include +#ifdef ISC_PLATFORM_HAVESYSUNH +#include +#endif +#include +#include +#include + typedef unsigned int isc_result_t; -typedef bool isc_boolean_t; +typedef int isc_boolean_t; typedef uint32_t dns_ttl_t; #define DLZ_DLOPEN_VERSION 2 @@ -37,6 +47,10 @@ typedef uint32_t dns_ttl_t; #define ISC_R_NOTFOUND 23 #define ISC_R_FAILURE 25 +/* boolean values */ +#define ISC_TRUE 1 +#define ISC_FALSE 0 + /* log levels */ #define ISC_LOG_INFO (-1) #define ISC_LOG_NOTICE (-2) @@ -44,20 +58,80 @@ typedef uint32_t dns_ttl_t; #define ISC_LOG_ERROR (-4) #define ISC_LOG_CRITICAL (-5) -/* some opaque structures */ +/* other useful definitions */ +#define UNUSED(x) (void)(x) + +/* opaque structures */ typedef void *dns_sdlzlookup_t; typedef void *dns_sdlzallnodes_t; typedef void *dns_view_t; -typedef void *dns_dlzclientcallback_t; /* - * prototypes for the functions you can include in your driver + * Method and type definitions needed for retrieval of client info + * from the caller. + */ +typedef struct isc_sockaddr { + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +#ifdef ISC_PLATFORM_HAVESYSUNH + struct sockaddr_un sunix; +#endif + } type; + unsigned int length; + void * link; +} isc_sockaddr_t; + +#define DNS_CLIENTINFO_VERSION 1 +typedef struct dns_clientinfo { + uint16_t version; + void *data; +} dns_clientinfo_t; + +typedef isc_result_t (*dns_clientinfo_sourceip_t)(dns_clientinfo_t *client, + isc_sockaddr_t **addrp); + +#define DNS_CLIENTINFOMETHODS_VERSION 1 +#define DNS_CLIENTINFOMETHODS_AGE 0 + +typedef struct dns_clientinfomethods { + uint16_t version; + uint16_t age; + dns_clientinfo_sourceip_t sourceip; +} dns_clientinfomethods_t; + +/* + * Method definitions for callbacks provided by the dlopen driver + */ +typedef void log_t(int level, const char *fmt, ...); + +typedef isc_result_t dns_sdlz_putrr_t(dns_sdlzlookup_t *lookup, + const char *type, + dns_ttl_t ttl, + const char *data); + +typedef isc_result_t dns_sdlz_putnamedrr_t(dns_sdlzallnodes_t *allnodes, + const char *name, + const char *type, + dns_ttl_t ttl, + const char *data); + +typedef isc_result_t dns_dlz_writeablezone_t(dns_view_t *view, + const char *zone_name); + + +/* + * prototypes for the functions you can include in your module */ /* * dlz_version() is required for all DLZ external drivers. It should - * return DLZ_DLOPEN_VERSION + * return DLZ_DLOPEN_VERSION. 'flags' is updated to indicate capabilities + * of the module. In particular, if the module is thread-safe then it + * sets 'flags' to include DNS_SDLZFLAG_THREADSAFE. Other capability + * flags may be added in the future. */ int dlz_version(unsigned int *flags); @@ -87,7 +161,9 @@ dlz_findzonedb(void *dbdata, const char *name); */ isc_result_t dlz_lookup(const char *zone, const char *name, void *dbdata, - dns_sdlzlookup_t *lookup); + dns_sdlzlookup_t *lookup, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo); /* * dlz_allowzonexfr() is optional, and should be supplied if you want to @@ -126,13 +202,6 @@ dlz_closeversion(const char *zone, isc_boolean_t commit, void *dbdata, isc_result_t dlz_configure(dns_view_t *view, void *dbdata); -/* - * dlz_setclientcallback() is optional, but must be supplied if you want - * to retrieve information about the client before sending a reply. - */ -isc_result_t -dlz_setclientcallback(dns_dlzclientcallback_t callback); - /* * dlz_ssumatch() is optional, but must be supplied if you want to support * dynamic updates diff --git a/contrib/dlz/example/named.conf b/contrib/dlz/example/named.conf new file mode 100644 index 0000000000..5c9e196228 --- /dev/null +++ b/contrib/dlz/example/named.conf @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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: named.conf,v 1.2 2011/10/20 22:01:48 each Exp $ */ + +/* + * This is a sample named.conf file that uses the DLZ module defined in + * dlz_example.c. It sets up a zone 'example.nil' which can accept DDNS + * updates. + * + * By default, when run, the zone contains the following records: + * + * example.nil. 3600 IN SOA example.nil. hostmaster.example.nil. ( + * 123 900 600 86400 3600 + * ) + * example.nil. 3600 IN NS example.nil. + * example.nil. 1800 IN A 10.53.0.1 + * + * Additionally, a query for 'source-addr.example.nil/TXT' is always + * answered with the source address of the query. This is used to + * demonstrate the code that retreives client information from the + * caller. + * + * To use this driver, "dlz_external.so" must be moved into the working + * directory for named. + */ + +options { + allow-transfer { any; }; + allow-query { any; }; + notify yes; + recursion no; +}; + +dlz "example" { + database "dlopen ./dlz_example.so example.nil"; +};