]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
3176. [doc] Corrected example code and added a README to the
authorEvan Hunt <each@isc.org>
Thu, 20 Oct 2011 22:01:48 +0000 (22:01 +0000)
committerEvan Hunt <each@isc.org>
Thu, 20 Oct 2011 22:01:48 +0000 (22:01 +0000)
sample external DLZ module in contrib/dlz/example.
[RT #26215]

CHANGES
contrib/dlz/example/README [new file with mode: 0644]
contrib/dlz/example/dlz_example.c
contrib/dlz/example/dlz_minimal.h
contrib/dlz/example/named.conf [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 4b5089fecf80e3f585de55611f1c466f8198e54e..aeefe843f0f480e9d67aa7d310b10d904a835e12 100644 (file)
--- 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 (file)
index 0000000..42d38ce
--- /dev/null
@@ -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.<zonename>/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.
index c8e468010ae31d297c745d008f3e9604aee99339..0c44e64af436ab8d3c16f3e5bfa48394437a1313 100644 (file)
@@ -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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdbool.h>
-#include <stdint.h>
 #include <stdarg.h>
+#include <stdint.h>
 
 #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; i<MAX_RECORDS; i++) {
-               if (first_empty == -1 && strlen(list[i].name) == 0) {
+       for (i = 0; i < MAX_RECORDS; i++) {
+               if (first_empty == -1 && strlen(list[i].name) == 0U) {
                        first_empty = i;
                }
-               if (strcasecmp(list[i].name, name) != 0) 
+               if (strcasecmp(list[i].name, name) != 0)
                        continue;
                if (strcasecmp(list[i].type, type) != 0)
                        continue;
@@ -100,24 +105,28 @@ static isc_result_t add_name(struct dlz_example_data *state,
        }
        if (i == MAX_RECORDS) {
                state->log(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; i<MAX_RECORDS; i++) {
+
+       UNUSED(state);
+
+       for (i = 0; i < MAX_RECORDS; i++) {
                if (strcasecmp(name, list[i].name) == 0 &&
                    strcasecmp(type, list[i].type) == 0 &&
                    strcasecmp(data, list[i].data) == 0 &&
@@ -126,59 +135,86 @@ static isc_result_t del_name(struct dlz_example_data *state,
                }
        }
        if (i == MAX_RECORDS) {
-               return ISC_R_NOTFOUND;
+               return (ISC_R_NOTFOUND);
        }
        memset(&list[i], 0, sizeof(struct record));
-       return ISC_R_SUCCESS;
+       return (ISC_R_SUCCESS);
 }
 
+static isc_result_t
+fmt_address(isc_sockaddr_t *addr, char *buffer, size_t size) {
+       char addr_buf[100];
+       const char *ret;
+       uint16_t port = 0;
+
+       switch (addr->type.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)
-{
-       return DLZ_DLOPEN_VERSION;
+int
+dlz_version(unsigned int *flags) {
+       UNUSED(flags);
+       return (DLZ_DLOPEN_VERSION);
 }
 
 /*
  remember a helper function from the bind9 dlz_dlopen driver
* 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)
+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];
 
+       UNUSED(dlzname);
+
        state = calloc(1, sizeof(struct dlz_example_data));
-       if (state == NULL) {
-               return ISC_R_NOMEMORY;
-       }
+       if (state == NULL)
+               return (ISC_R_NOMEMORY);
 
-       /* fill in the helper functions */
+       /* 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; i<MAX_RECORDS; i++) {
+
+       for (i = 0; i < MAX_RECORDS; i++) {
                if (strcasecmp(state->current[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; i<MAX_RECORDS; i++) {
+       UNUSED(zone);
+
+       for (i = 0; i < MAX_RECORDS; i++) {
                isc_result_t result;
-               if (strlen(state->current[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; i<MAX_RECORDS; i++) {
-                       if (strlen(state->adds[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; i<MAX_RECORDS; i++) {
-                       if (strlen(state->deletes[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;
-
-       result = add_name(state, list, name, type, strtoul(ttlstr, NULL, 10), data);
+       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);
+
+       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);
 
-       state->log(ISC_LOG_INFO, "dlz_example: deleting rdataset %s of type %s", name, type);
-                  
-       return ISC_R_SUCCESS;
+       return (ISC_R_SUCCESS);
 }
index 4337bdeb835ed14b51c84e3b4ae84f83b59e0648..a1fc7952d5672a0aaa6c84e11f644d0456d3059e 100644 (file)
@@ -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
  */
 
 /*
-  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 <sys/types.h>
+#include <sys/socket.h>
+#ifdef ISC_PLATFORM_HAVESYSUNH
+#include <sys/un.h>
+#endif
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 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 (file)
index 0000000..5c9e196
--- /dev/null
@@ -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";
+};