]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
4376. [experimental] Added support for Catalog Zones, a new method for
authorWitold Krecicki <wpk@isc.org>
Thu, 26 May 2016 19:23:19 +0000 (21:23 +0200)
committerWitold Krecicki <wpk@isc.org>
Thu, 26 May 2016 19:23:19 +0000 (21:23 +0200)
                        provisioning secondary servers in which a list of
                        zones to be served is stored in a DNS zone and can
                        be propagated to slaves via AXFR/IXFR. [RT #41581]

4375.   [func]          Add support for automatic reallocation of isc_buffer
                        to isc_buffer_put* functions. [RT #42394]

56 files changed:
.gitignore
CHANGES
bin/named/config.c
bin/named/include/named/config.h
bin/named/named.conf.docbook
bin/named/server.c
bin/named/zoneconf.c
bin/tests/system/catz/clean.sh [new file with mode: 0644]
bin/tests/system/catz/ns1/catalog.example.db.in [new file with mode: 0644]
bin/tests/system/catz/ns1/named.conf [new file with mode: 0644]
bin/tests/system/catz/ns2/named.conf [new file with mode: 0644]
bin/tests/system/catz/ns3/dom4.example.db [new file with mode: 0644]
bin/tests/system/catz/ns3/named.conf [new file with mode: 0644]
bin/tests/system/catz/setup.sh [new file with mode: 0644]
bin/tests/system/catz/tests.sh [new file with mode: 0644]
bin/tests/system/conf.sh.in
doc/arm/Bv9ARM-book.xml
doc/arm/Bv9ARM.pdf
doc/arm/catz.xml [new file with mode: 0644]
doc/arm/notes.xml
lib/dns/Makefile.in
lib/dns/acl.c
lib/dns/catz.c [new file with mode: 0644]
lib/dns/db.c
lib/dns/include/dns/catz.h [new file with mode: 0644]
lib/dns/include/dns/db.h
lib/dns/include/dns/events.h
lib/dns/include/dns/ipkeylist.h [new file with mode: 0644]
lib/dns/include/dns/rbt.h
lib/dns/include/dns/types.h
lib/dns/include/dns/view.h
lib/dns/include/dns/zone.h
lib/dns/include/dns/zt.h
lib/dns/ipkeylist.c [new file with mode: 0644]
lib/dns/name.c
lib/dns/rbt.c
lib/dns/rbtdb.c
lib/dns/view.c
lib/dns/win32/libdns.def.in
lib/dns/xfrin.c
lib/dns/zone.c
lib/dns/zt.c
lib/isc/Makefile.in
lib/isc/buffer.c
lib/isc/ht.c [new file with mode: 0644]
lib/isc/include/isc/buffer.h
lib/isc/include/isc/hash.h
lib/isc/include/isc/ht.h [new file with mode: 0644]
lib/isc/include/isc/util.h
lib/isc/mem.c
lib/isc/task.c
lib/isc/tests/Makefile.in
lib/isc/tests/buffer_test.c
lib/isc/tests/ht_test.c [new file with mode: 0644]
lib/isc/win32/libisc.def.in
lib/isccfg/namedconf.c

index e8d7aa4873d8b32971fcc6275ae3c66f625014e6..65540e2f901e9420cf83a3f5c494df47e6b61488 100644 (file)
@@ -55,3 +55,6 @@ unit/atf-src/test-programs/sh_helpers
 # ccc-analyzer store its results in .plist directories
 *.plist/
 *~
+.project
+.cproject
+.settings
diff --git a/CHANGES b/CHANGES
index 8641170c453918f265aa0a9df13e6c8f9f532258..e69d2f8d5df3e7c86600afffba22e716bc154e49 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,11 @@
+4376.  [experimental]  Added support for Catalog Zones, a new method for
+                       provisioning secondary servers in which a list of
+                       zones to be served is stored in a DNS zone and can
+                       be propagated to slaves via AXFR/IXFR. [RT #41581]
+
+4375.  [func]          Add support for automatic reallocation of isc_buffer
+                       to isc_buffer_put* functions. [RT #42394]
+
 4374.  [bug]           Use SAVE/RESTORE macros in query.c to reduce the
                        probability of reference counting errors as seen
                        in 4365. [RT #42405]
index e74ebbfef584ff4210471470936e551e8598baf2..fb061e7d3d9733676abc70c34dcc2b98c819a713 100644 (file)
@@ -576,9 +576,7 @@ get_masters_def(const cfg_obj_t *cctx, const char *name,
 
 isc_result_t
 ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
-                         isc_mem_t *mctx, isc_sockaddr_t **addrsp,
-                         isc_dscp_t **dscpsp, dns_name_t ***keysp,
-                         isc_uint32_t *countp)
+                         isc_mem_t *mctx, dns_ipkeylist_t *ipkl)
 {
        isc_uint32_t addrcount = 0, dscpcount = 0, keycount = 0, i = 0;
        isc_uint32_t listcount = 0, l = 0, j;
@@ -601,10 +599,10 @@ ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
                isc_dscp_t dscp;
        } *stack = NULL;
 
-       REQUIRE(addrsp != NULL && *addrsp == NULL);
-       REQUIRE(dscpsp != NULL && *dscpsp == NULL);
-       REQUIRE(keysp != NULL && *keysp == NULL);
-       REQUIRE(countp != NULL);
+       REQUIRE(ipkl != NULL);
+       REQUIRE(ipkl->addrs == NULL);
+       REQUIRE(ipkl->keys == NULL);
+       REQUIRE(ipkl->dscps == NULL);
 
        /*
         * Get system defaults.
@@ -857,10 +855,10 @@ ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
 
        INSIST(keycount == addrcount);
 
-       *addrsp = addrs;
-       *dscpsp = dscps;
-       *keysp = keys;
-       *countp = addrcount;
+       ipkl->addrs = addrs;
+       ipkl->dscps = dscps;
+       ipkl->keys = keys;
+       ipkl->count = addrcount;
 
        return (ISC_R_SUCCESS);
 
@@ -886,37 +884,6 @@ ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
        return (result);
 }
 
-void
-ns_config_putipandkeylist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
-                         isc_dscp_t **dscpsp, dns_name_t ***keysp,
-                         isc_uint32_t count)
-{
-       unsigned int i;
-       dns_name_t **keys;
-
-       REQUIRE(addrsp != NULL && *addrsp != NULL);
-       REQUIRE(dscpsp == NULL || *dscpsp != NULL);
-       REQUIRE(keysp != NULL && *keysp != NULL);
-
-       keys = *keysp;
-
-       isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t));
-       if (dscpsp != NULL)
-               isc_mem_put(mctx, *dscpsp, count * sizeof(isc_dscp_t));
-       for (i = 0; i < count; i++) {
-               if (keys[i] == NULL)
-                       continue;
-               if (dns_name_dynamic(keys[i]))
-                       dns_name_free(keys[i], mctx);
-               isc_mem_put(mctx, keys[i], sizeof(dns_name_t));
-       }
-       isc_mem_put(mctx, *keysp, count * sizeof(dns_name_t *));
-       *addrsp = NULL;
-       if (dscpsp != NULL)
-               *dscpsp = NULL;
-       *keysp = NULL;
-}
-
 isc_result_t
 ns_config_getport(const cfg_obj_t *config, in_port_t *portp) {
        const cfg_obj_t *maps[3];
index 5302c9b29f45710e8b584b5a8eaf843e6193a7a5..f051bfeca39a545cd39812b494239c9323e4c672 100644 (file)
@@ -31,11 +31,11 @@ isc_result_t
 ns_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf);
 
 isc_result_t
-ns_config_get(cfg_obj_t const * const *maps, const charname,
+ns_config_get(cfg_obj_t const * const *maps, const char *name,
              const cfg_obj_t **obj);
 
 isc_result_t
-ns_checknames_get(const cfg_obj_t **maps, const charname,
+ns_checknames_get(const cfg_obj_t **maps, const char *name,
                  const cfg_obj_t **obj);
 
 int
@@ -64,14 +64,7 @@ ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
 
 isc_result_t
 ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
-                         isc_mem_t *mctx, isc_sockaddr_t **addrsp,
-                         isc_dscp_t **dscpp, dns_name_t ***keys,
-                         isc_uint32_t *countp);
-
-void
-ns_config_putipandkeylist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
-                         isc_dscp_t **dscpsp, dns_name_t ***keys,
-                         isc_uint32_t count);
+                         isc_mem_t *mctx, dns_ipkeylist_t *ipkl);
 
 isc_result_t
 ns_config_getport(const cfg_obj_t *config, in_port_t *portp);
index b919a628c76b23c3e64d35fe04a4a3fe3d06be4d..932afea19de540b789580e4f61f7f962c088db2b 100644 (file)
@@ -283,6 +283,16 @@ options {
        check-mx-cname ( fail | warn | ignore );
        check-srv-cname ( fail | warn | ignore );
        cache-file <replaceable>quoted_string</replaceable>; // test option
+       catalog-zones {
+           zone <replaceable>quoted_string</replaceable>
+               <optional> default-masters
+               <optional>port <replaceable>ip_port</replaceable></optional>
+               <optional>dscp <replaceable>ip_dscp</replaceable></optional>
+               { ( <replaceable>masters_list</replaceable> | <replaceable>ip_addr</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> <optional>key <replaceable>key</replaceable></optional> ) ; <optional>...</optional> }</optional>
+           <optional>in-memory <replaceable>yes_or_no</replaceable></optional>
+           <optional>min-update-interval <replaceable>interval</replaceable></optional>
+           ; ... };
+       ;
        suppress-initial-notify <replaceable>boolean</replaceable>; // not yet implemented
        preferred-glue <replaceable>string</replaceable>;
        dual-stack-servers <optional> port <replaceable>integer</replaceable> </optional> {
index f06678d078915786157aecb9acc79c6a939c31a1..58ab7c06be25b516044d4038a22d9c8d3832f02d 100644 (file)
 #include <dns/adb.h>
 #include <dns/badcache.h>
 #include <dns/cache.h>
+#include <dns/catz.h>
 #include <dns/db.h>
 #include <dns/dispatch.h>
 #include <dns/dlz.h>
 #include <dns/dns64.h>
 #include <dns/dyndb.h>
+#include <dns/events.h>
 #include <dns/forward.h>
 #include <dns/journal.h>
 #include <dns/keytable.h>
@@ -275,6 +277,19 @@ typedef struct {
                isc_refcount_t refs;
 } ns_zoneload_t;
 
+typedef struct {
+       ns_server_t *server;
+} catz_cb_data_t;
+
+typedef struct catz_chgzone_event {
+       ISC_EVENT_COMMON(struct catz_chgzone_event);
+       dns_catz_entry_t *entry;
+       dns_catz_zone_t *origin;
+       dns_view_t *view;
+       catz_cb_data_t *cbd;
+       isc_boolean_t mod;
+} catz_chgzone_event_t;
+
 /*
  * These zones should not leak onto the Internet.
  */
@@ -1990,12 +2005,439 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
                            "updated RPZ policy: version %d",
                            view->rpzs->rpz_ver);
        }
+
        if (pview != NULL)
                dns_view_detach(&pview);
 
        return (ISC_R_SUCCESS);
 }
 
+static void
+catz_addmodzone_taskaction(isc_task_t *task, isc_event_t *event0) {
+       catz_chgzone_event_t *ev = (catz_chgzone_event_t *) event0;
+       isc_result_t result;
+       isc_buffer_t namebuf;
+       isc_buffer_t *confbuf;
+       char nameb[DNS_NAME_FORMATSIZE];
+       const cfg_obj_t *zlist = NULL;
+       cfg_obj_t *zoneconf = NULL;
+       cfg_obj_t *zoneobj = NULL;
+       ns_cfgctx_t *cfg;
+       dns_zone_t *zone = NULL;
+
+       cfg = (ns_cfgctx_t *) ev->view->new_zone_config;
+       if (cfg == NULL) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                             "catz: allow-new-zones statement missing from "
+                             "config; cannot add zone from the catalog");
+               goto cleanup;
+       }
+
+       isc_buffer_init(&namebuf, nameb, DNS_NAME_FORMATSIZE);
+       dns_name_totext(dns_catz_entry_getname(ev->entry), ISC_TRUE, &namebuf);
+       isc_buffer_putuint8(&namebuf, 0);
+
+       /* Zone shouldn't already exist */
+       result = dns_zt_find(ev->view->zonetable,
+                            dns_catz_entry_getname(ev->entry), 0, NULL, &zone);
+       if (result == ISC_R_SUCCESS) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+                             "catz: zone \"%s\" already exists", nameb);
+               goto cleanup;
+       } else if (result == DNS_R_PARTIALMATCH) {
+               /* Create our sub-zone anyway */
+               dns_zone_detach(&zone);
+               zone = NULL;
+       } else if (result != ISC_R_NOTFOUND) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                             "catz: error \"%s\" while trying to "
+                             "add zone \"%s\"",
+                             isc_result_totext(result), nameb);
+               goto cleanup;
+       }
+
+       /* Create a config for new zone */
+       confbuf = NULL;
+       dns_catz_generate_zonecfg(ev->origin, ev->entry, &confbuf);
+       cfg_parser_reset(cfg->add_parser);
+       result = cfg_parse_buffer2(cfg->add_parser, confbuf, "catz",
+                                  &cfg_type_addzoneconf, &zoneconf);
+       isc_buffer_free(&confbuf);
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                             "catz: error \"%s\" while trying to generate "
+                             "config for zone \"%s\"",
+                             isc_result_totext(result), nameb);
+               goto cleanup;
+       }
+       CHECK(cfg_map_get(zoneconf, "zone", &zlist));
+       if (!cfg_obj_islist(zlist))
+               CHECK(ISC_R_FAILURE);
+
+       /* For now we only support adding one zone at a time */
+       zoneobj = cfg_listelt_value(cfg_list_first(zlist));
+
+       /* Mark view unfrozen so that zone can be added */
+
+       result = isc_task_beginexclusive(task);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       dns_view_thaw(ev->view);
+       result = configure_zone(cfg->config, zoneobj, cfg->vconfig,
+                               ev->cbd->server->mctx, ev->view, NULL,
+                               cfg->actx, ISC_TRUE, ISC_FALSE, ev->mod);
+       dns_view_freeze(ev->view);
+       isc_task_endexclusive(task);
+
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+                             "catz: failed to configure zone \"%s\" - %d",
+                             nameb, result);
+               goto cleanup;
+       }
+
+       /* Is it there yet? */
+       CHECK(dns_zt_find(ev->view->zonetable,
+                       dns_catz_entry_getname(ev->entry), 0, NULL, &zone));
+
+       /*
+        * Load the zone from the master file.  If this fails, we'll
+        * need to undo the configuration we've done already.
+        */
+       result = dns_zone_loadnew(zone);
+       if (result != ISC_R_SUCCESS) {
+               dns_db_t *dbp = NULL;
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                             "catz: dns_zone_loadnew() failed "
+                             "with %s; reverting.",
+                             isc_result_totext(result));
+
+               /* If the zone loaded partially, unload it */
+               if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+                       dns_db_detach(&dbp);
+                       dns_zone_unload(zone);
+               }
+
+               /* Remove the zone from the zone table */
+               dns_zt_unmount(ev->view->zonetable, zone);
+               goto cleanup;
+       }
+
+       /* Flag the zone as having been added at runtime */
+       dns_zone_setadded(zone, ISC_TRUE);
+
+ cleanup:
+       if (zone != NULL)
+               dns_zone_detach(&zone);
+       if (zoneconf != NULL)
+               cfg_obj_destroy(cfg->add_parser, &zoneconf);
+       dns_catz_entry_detach(ev->origin, &ev->entry);
+       dns_catz_zone_detach(&ev->origin);
+       dns_view_detach(&ev->view);
+       isc_event_free(ISC_EVENT_PTR(&ev));
+}
+
+static void
+catz_delzone_taskaction(isc_task_t *task, isc_event_t *event0) {
+       catz_chgzone_event_t *ev = (catz_chgzone_event_t *) event0;
+       isc_result_t result;
+       dns_zone_t *zone = NULL;
+       dns_db_t *dbp = NULL;
+       char cname[DNS_NAME_FORMATSIZE];
+       const char * file;
+
+       result = isc_task_beginexclusive(task);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+       dns_name_format(dns_catz_entry_getname(ev->entry), cname,
+       DNS_NAME_FORMATSIZE);
+       result = dns_zt_find(ev->view->zonetable,
+                            dns_catz_entry_getname(ev->entry), 0, NULL, &zone);
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+                             "catz: catz_delzone_taskaction: "
+                             "zone '%s' not found", cname);
+               goto cleanup;
+       }
+
+       /* TODO make other flag for CZ zones */
+       /* TODO2 make sure that we delete only 'own' zones */
+       if (!dns_zone_getadded(zone)) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+                             "catz: catz_delzone_taskaction: "
+                             "zone '%s' is not a dynamically added zone",
+                             cname);
+               goto cleanup;
+       }
+
+       /* Stop answering for this zone */
+       if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+               dns_db_detach(&dbp);
+               dns_zone_unload(zone);
+       }
+
+       CHECK(dns_zt_unmount(ev->view->zonetable, zone));
+       file = dns_zone_getfile(zone);
+       isc_file_remove(file);
+
+       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                     NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+                     "catz: catz_delzone_taskaction: "
+                     "zone '%s' deleted", cname);
+  cleanup:
+       isc_task_endexclusive(task);
+       if (zone != NULL)
+               dns_zone_detach(&zone);
+       dns_catz_entry_detach(ev->origin, &ev->entry);
+       dns_catz_zone_detach(&ev->origin);
+       dns_view_detach(&ev->view);
+       isc_event_free(ISC_EVENT_PTR(&ev));
+}
+
+static isc_result_t
+catz_create_chg_task(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+                    dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata,
+                    isc_eventtype_t type)
+{
+       catz_chgzone_event_t *event;
+       isc_task_t *task;
+       isc_result_t result;
+       isc_taskaction_t action;
+
+       switch (type) {
+       case DNS_EVENT_CATZADDZONE:
+       case DNS_EVENT_CATZMODZONE:
+               action = catz_addmodzone_taskaction;
+               break;
+       case DNS_EVENT_CATZDELZONE:
+               action = catz_delzone_taskaction;
+               break;
+       default:
+               REQUIRE(0);
+       }
+
+       event = (catz_chgzone_event_t *) isc_event_allocate(view->mctx, origin,
+                                                           type, action, NULL,
+                                                           sizeof(*event));
+       if (event == NULL)
+               return (ISC_R_NOMEMORY);
+
+       event->cbd = (catz_cb_data_t *) udata;
+       event->entry = NULL;
+       event->origin = NULL;
+       event->view = NULL;
+       event->mod = ISC_TF(type == DNS_EVENT_CATZMODZONE);
+       dns_catz_entry_attach(entry, &event->entry);
+       dns_catz_zone_attach(origin, &event->origin);
+       dns_view_attach(view, &event->view);
+
+       task = NULL;
+       result = isc_taskmgr_excltask(taskmgr, &task);
+       REQUIRE(result == ISC_R_SUCCESS);
+       isc_task_send(task, ISC_EVENT_PTR(&event));
+       isc_task_detach(&task);
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+catz_addzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+            dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata)
+{
+       return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+                                    DNS_EVENT_CATZADDZONE));
+}
+
+static isc_result_t
+catz_delzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+            dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata)
+{
+       return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+                                    DNS_EVENT_CATZDELZONE));
+}
+
+static isc_result_t
+catz_modzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+            dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata)
+{
+       return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+                                    DNS_EVENT_CATZMODZONE));
+}
+
+static isc_result_t
+configure_catz_zone(dns_view_t *view, const cfg_obj_t *config,
+                   const cfg_listelt_t *element)
+{
+       const cfg_obj_t *catz_obj, *obj;
+       dns_catz_zone_t *zone = NULL;
+       const char *str;
+       isc_result_t result;
+       dns_name_t origin;
+       dns_catz_options_t *opts;
+       dns_view_t *pview = NULL;
+
+       dns_name_init(&origin, NULL);
+       catz_obj = cfg_listelt_value(element);
+
+       str = cfg_obj_asstring(cfg_tuple_get(catz_obj, "zone name"));
+
+       result = dns_name_fromstring(&origin, str, DNS_NAME_DOWNCASE,
+                                    view->mctx);
+       if (result == ISC_R_SUCCESS && dns_name_equal(&origin, dns_rootname))
+               result = DNS_R_EMPTYLABEL;
+
+       if (result != ISC_R_SUCCESS) {
+               cfg_obj_log(catz_obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+                           "catz: invalid zone name '%s'", str);
+               goto cleanup;
+       }
+
+       result = dns_catz_add_zone(view->catzs, &origin, &zone);
+       if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
+               cfg_obj_log(catz_obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+                           "catz: unable to create catalog zone '%s', "
+                           "error %s",
+                           str, isc_result_totext(result));
+               goto cleanup;
+       }
+
+       if (result == ISC_R_EXISTS) {
+               isc_ht_iter_t *it = NULL;
+
+               result = dns_viewlist_find(&ns_g_server->viewlist,
+                                          view->name,
+                                          view->rdclass, &pview);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+               /*
+                * xxxwpk todo: reconfigure the zone!!!!
+                */
+               cfg_obj_log(catz_obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+                           "catz: catalog zone '%s' will not be reconfigured",
+                           str);
+               /*
+                * We have to walk through all the member zones and attach
+                * them to current view
+                */
+               result = dns_catz_get_iterator(zone, &it);
+               if (result != ISC_R_SUCCESS) {
+                       cfg_obj_log(catz_obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+                                   "catz: unable to create iterator");
+                       goto cleanup;
+               }
+
+               for (result = isc_ht_iter_first(it);
+                    result == ISC_R_SUCCESS;
+                    result = isc_ht_iter_next(it))
+               {
+                       dns_name_t *name = NULL;
+                       dns_zone_t *dnszone = NULL;
+                       dns_catz_entry_t *entry = NULL;
+                       isc_result_t tresult;
+
+                       isc_ht_iter_current(it, (void **) &entry);
+                       name = dns_catz_entry_getname(entry);
+
+                       tresult = dns_view_findzone(pview, name, &dnszone);
+                       RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
+
+                       dns_zone_setview(dnszone, view);
+                       if (view->acache != NULL)
+                               dns_zone_setacache(dnszone, view->acache);
+                       dns_view_addzone(view, dnszone);
+               }
+
+               isc_ht_iter_destroy(&it);
+
+               result = ISC_R_SUCCESS;
+       }
+
+       dns_catz_zone_resetdefoptions(zone);
+       opts = dns_catz_zone_getdefoptions(zone);
+
+       obj = cfg_tuple_get(catz_obj, "default-masters");
+       if (obj != NULL)
+               result = ns_config_getipandkeylist(config, obj,
+                                                  view->mctx, &opts->masters);
+
+       obj = cfg_tuple_get(catz_obj, "in-memory");
+       if (obj != NULL && cfg_obj_isboolean(obj))
+               opts->in_memory = cfg_obj_asboolean(obj);
+
+       obj = cfg_tuple_get(catz_obj, "min-update-interval");
+       if (obj != NULL && cfg_obj_isuint32(obj))
+               opts->min_update_interval = cfg_obj_asuint32(obj);
+
+  cleanup:
+       if (pview != NULL)
+               dns_view_detach(&pview);
+       dns_name_free(&origin, view->mctx);
+
+       return (result);
+}
+
+static catz_cb_data_t ns_catz_cbdata;
+static dns_catz_zonemodmethods_t ns_catz_zonemodmethods = {
+       catz_addzone,
+       catz_modzone,
+       catz_delzone,
+       &ns_catz_cbdata
+};
+
+static isc_result_t
+configure_catz(dns_view_t *view, const cfg_obj_t *config,
+              const cfg_obj_t *catz_obj)
+{
+       const cfg_listelt_t *zone_element;
+       const dns_catz_zones_t *old = NULL;
+       dns_view_t *pview = NULL;
+       isc_result_t result;
+
+       /* xxxwpk TODO do it cleaner, once, somewhere */
+       ns_catz_cbdata.server = ns_g_server;
+
+       zone_element = cfg_list_first(cfg_tuple_get(catz_obj, "zone list"));
+       if (zone_element == NULL)
+               return (ISC_R_SUCCESS);
+
+       CHECK(dns_catz_new_zones(&view->catzs, &ns_catz_zonemodmethods,
+                                view->mctx, ns_g_taskmgr, ns_g_timermgr));
+
+       result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
+                                  view->rdclass, &pview);
+       if (result == ISC_R_SUCCESS)
+               old = pview->catzs;
+
+       if (old != NULL) {
+               dns_catz_catzs_detach(&view->catzs);
+               dns_catz_catzs_attach(pview->catzs, &view->catzs);
+               dns_catz_prereconfig(view->catzs);
+       }
+
+       while (zone_element != NULL) {
+               CHECK(configure_catz_zone(view, config, zone_element));
+               zone_element = cfg_list_next(zone_element);
+       }
+
+       if (old != NULL)
+               dns_catz_postreconfig(view->catzs);
+
+       result = ISC_R_SUCCESS;
+
+  cleanup:
+       if (pview != NULL)
+               dns_view_detach(&pview);
+
+       return (result);
+}
+
 #define CHECK_RRL(cond, pat, val1, val2)                               \
        do {                                                            \
                if (!(cond)) {                                          \
@@ -2693,6 +3135,12 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
                CHECK(configure_rpz(view, obj, &old_rpz_ok));
        }
 
+       obj = NULL;
+       if (view->rdclass == dns_rdataclass_in && need_hints &&
+           ns_config_get(maps, "catalog-zones", &obj) == ISC_R_SUCCESS) {
+               CHECK(configure_catz(view, config, obj));
+       }
+
        /*
         * Configure the zones.
         */
@@ -4574,6 +5022,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
        dns_rdataclass_t zclass;
        const char *ztypestr;
        dns_rpz_num_t rpz_num;
+       isc_boolean_t zone_is_catz = ISC_FALSE;
 
        options = NULL;
        (void)cfg_map_get(config, "options", &options);
@@ -4801,6 +5250,10 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                        break;
        }
 
+       if (view->catzs != NULL &&
+           dns_catz_get_zone(view->catzs, origin) != NULL)
+               zone_is_catz = ISC_TRUE;
+
        /*
         * See if we can reuse an existing zone.  This is
         * only possible if all of these are true:
@@ -4850,7 +5303,6 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
                dns_zone_setstats(zone, ns_g_server->zonestats);
        }
-
        if (rpz_num != DNS_RPZ_INVALID_NUM) {
                result = dns_zone_rpz_enable(zone, view->rpzs, rpz_num);
                if (result != ISC_R_SUCCESS) {
@@ -4864,6 +5316,9 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                }
        }
 
+       if (zone_is_catz)
+               dns_zone_catz_enable(zone, view->catzs);
+
        /*
         * If the zone contains a 'forwarders' statement, configure
         * selective forwarding.
@@ -4919,7 +5374,19 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
         * Add the zone to its view in the new view list.
         */
        if (!modify)
-               CHECK(dns_view_addzone(view, zone));
+       CHECK(dns_view_addzone(view, zone));
+
+       if (zone_is_catz) {
+               /*
+                * force catz reload if the zone is loaded;
+                * if it's not it'll get reloaded on zone load
+                */
+               dns_db_t *db = NULL;
+
+               tresult = dns_zone_getdb(zone, &db);
+               if (tresult == ISC_R_SUCCESS)
+                       dns_catz_dbupdate_callback(db, view->catzs);
+       }
 
        /*
         * Ensure that zone keys are reloaded on reconfig
@@ -5684,6 +6151,20 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        if (result == ISC_R_SUCCESS)
                allow = cfg_obj_asboolean(nz);
 
+       /*
+        * A non-empty catalog-zones statement implies allow-new-zones
+        */
+       if (!allow) {
+               const cfg_obj_t *cz = NULL;
+               result = ns_config_get(maps, "catalog-zones", &cz);
+               if (result == ISC_R_SUCCESS) {
+                       const cfg_listelt_t *e =
+                               cfg_list_first(cfg_tuple_get(cz, "zone list"));
+                       if (e != NULL)
+                               allow = ISC_TRUE;
+               }
+       }
+
        if (!allow) {
                dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
                return (ISC_R_SUCCESS);
index 335519a13992c31f05b0b322a75135b14630bfb3..2d297a7c80c4f51cd1122251ef4bdbe0054c2c23 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <dns/acl.h>
 #include <dns/db.h>
+#include <dns/ipkeylist.h>
 #include <dns/fixedname.h>
 #include <dns/log.h>
 #include <dns/name.h>
@@ -805,9 +806,6 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
        const char *filename = NULL;
        const char *dupcheck;
        dns_notifytype_t notifytype = dns_notifytype_yes;
-       isc_sockaddr_t *addrs;
-       isc_dscp_t *dscps;
-       dns_name_t **keynames;
        isc_uint32_t count;
        unsigned int dbargc;
        char **dbargv;
@@ -1149,23 +1147,19 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
                     (notifytype == dns_notifytype_masteronly &&
                      ztype == dns_zone_master)))
                {
-                       isc_uint32_t addrcount;
-                       addrs = NULL;
-                       keynames = NULL;
-                       dscps = NULL;
+                       dns_ipkeylist_t ipkl;
+                       ipkl.count = 0;
+                       ipkl.addrs = NULL;
+                       ipkl.dscps = NULL;
+                       ipkl.keys = NULL;
                        RETERR(ns_config_getipandkeylist(config, obj, mctx,
-                                                        &addrs, &dscps,
-                                                        &keynames,
-                                                        &addrcount));
-                       result = dns_zone_setalsonotifydscpkeys(zone, addrs,
-                                                               dscps, keynames,
-                                                               addrcount);
-                       if (addrcount != 0)
-                               ns_config_putipandkeylist(mctx, &addrs, &dscps,
-                                                         &keynames, addrcount);
-                       else
-                               INSIST(addrs == NULL && dscps == NULL &&
-                                      keynames == NULL);
+                                                        &ipkl));
+                       result = dns_zone_setalsonotifydscpkeys(zone,
+                                                               ipkl.addrs,
+                                                               ipkl.dscps,
+                                                               ipkl.keys,
+                                                               ipkl.count);
+                       dns_ipkeylist_clear(mctx, &ipkl);
                        RETERR(result);
                } else
                        RETERR(dns_zone_setalsonotify(zone, NULL, 0));
@@ -1628,19 +1622,19 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
                obj = NULL;
                (void)cfg_map_get(zoptions, "masters", &obj);
                if (obj != NULL) {
-                       addrs = NULL;
-                       dscps = NULL;
-                       keynames = NULL;
+                       dns_ipkeylist_t ipkl;
+                       ipkl.count = 0;
+                       ipkl.addrs = NULL;
+                       ipkl.dscps = NULL;
+                       ipkl.keys = NULL;
                        RETERR(ns_config_getipandkeylist(config, obj, mctx,
-                                                        &addrs, &dscps,
-                                                        &keynames, &count));
-                       result = dns_zone_setmasterswithkeys(mayberaw, addrs,
-                                                            keynames, count);
-                       if (count != 0)
-                               ns_config_putipandkeylist(mctx, &addrs, &dscps,
-                                                         &keynames, count);
-                       else
-                               INSIST(addrs == NULL && keynames == NULL);
+                                                        &ipkl));
+                       result = dns_zone_setmasterswithkeys(mayberaw,
+                                                            ipkl.addrs,
+                                                            ipkl.keys,
+                                                            ipkl.count);
+                       dns_ipkeylist_clear(mctx, &ipkl);
+                       RETERR(result);
                } else
                        result = dns_zone_setmasters(mayberaw, NULL, 0);
                RETERR(result);
diff --git a/bin/tests/system/catz/clean.sh b/bin/tests/system/catz/clean.sh
new file mode 100644 (file)
index 0000000..4e4bdc4
--- /dev/null
@@ -0,0 +1,23 @@
+# Copyright (C) 2014-2016  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.
+
+rm -f dig.out.*
+rm -f nsupdate.out.*
+rm -f ns*/named.memstats
+rm -f ns*/named.run
+rm -f ns*/named.lock
+rm -f ns{1,2}/*dom*example.db
+rm -f ns{1,2}/catalog.example.db
+rm -f ns*/*.jnl
+rm -f ns*/*.nzf
diff --git a/bin/tests/system/catz/ns1/catalog.example.db.in b/bin/tests/system/catz/ns1/catalog.example.db.in
new file mode 100644 (file)
index 0000000..53013fe
--- /dev/null
@@ -0,0 +1,3 @@
+@ 3600 SOA . . 1 86400 3600 86400 3600
+@ 3600 IN NS invalid.
+version IN TXT "1"
diff --git a/bin/tests/system/catz/ns1/named.conf b/bin/tests/system/catz/ns1/named.conf
new file mode 100644 (file)
index 0000000..db67cbf
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011, 2013, 2016  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.
+ */
+
+include "../../common/rndc.key";
+
+controls {
+       inet 10.53.0.1 port 9953 allow { any; } keys { rndc_key; };
+};
+
+options {
+       query-source address 10.53.0.1;
+       notify-source 10.53.0.1;
+       transfer-source 10.53.0.1;
+       port 5300;
+       allow-new-zones yes;
+       pid-file "named.pid";
+       listen-on { 10.53.0.1; };
+       listen-on-v6 { none; };
+       notify no;
+       recursion no;
+};
+
+zone "catalog.example" {
+       type master;
+       file "catalog.example.db";
+       allow-transfer { any; };
+       allow-update { any; };
+       also-notify { 10.53.0.2; };
+       notify explicit;
+};
diff --git a/bin/tests/system/catz/ns2/named.conf b/bin/tests/system/catz/ns2/named.conf
new file mode 100644 (file)
index 0000000..9df04ae
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011, 2013, 2016  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.
+ */
+
+include "../../common/rndc.key";
+
+controls {
+       inet 10.53.0.2 port 9953 allow { any; } keys { rndc_key; };
+};
+
+options {
+       query-source address 10.53.0.2;
+       notify-source 10.53.0.2;
+       transfer-source 10.53.0.2;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.2; };
+       listen-on-v6 { none; };
+       notify no;
+       recursion no;
+       serial-query-rate 100;
+       catalog-zones {
+               zone "catalog.example" default-masters { 10.53.0.1; };
+       };
+};
+
+zone "catalog.example" {
+       type slave;
+       file "catalog.example.db";
+       masters { 10.53.0.1; };
+};
diff --git a/bin/tests/system/catz/ns3/dom4.example.db b/bin/tests/system/catz/ns3/dom4.example.db
new file mode 100644 (file)
index 0000000..4cbd501
--- /dev/null
@@ -0,0 +1,2 @@
+@ 3600 IN SOA . . 1 3600 3600 3600 3600
+@ IN NS invalid.
diff --git a/bin/tests/system/catz/ns3/named.conf b/bin/tests/system/catz/ns3/named.conf
new file mode 100644 (file)
index 0000000..d4cb795
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011, 2013, 2016  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.
+ */
+
+include "../../common/rndc.key";
+
+controls {
+       inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
+};
+
+options {
+       query-source address 10.53.0.3;
+       notify-source 10.53.0.3;
+       transfer-source 10.53.0.3;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       notify no;
+       recursion no;
+};
+
+zone "dom4.example" {
+       type master;
+       file "dom4.example.db";
+       allow-transfer { any; };
+       allow-update { any; };
+       notify explicit;
+};
diff --git a/bin/tests/system/catz/setup.sh b/bin/tests/system/catz/setup.sh
new file mode 100644 (file)
index 0000000..ece0f4f
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (C) 2012-2015  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.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+$SHELL clean.sh
+
+cat ns1/catalog.example.db.in > ns1/catalog.example.db
diff --git a/bin/tests/system/catz/tests.sh b/bin/tests/system/catz/tests.sh
new file mode 100644 (file)
index 0000000..77d5441
--- /dev/null
@@ -0,0 +1,305 @@
+#!/bin/sh -x
+#
+# Copyright (C) 2014-2016  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.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+status=0
+n=0
+
+n=`expr $n + 1`
+echo "I:checking that dom1.example is not served by master ($n)"
+ret=0
+$DIG soa dom1.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:Adding a domain dom1.example to master via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom1.example.db
+echo "@ IN NS invalid." >> ns1/dom1.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom1.example '{type master; file "dom1.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:checking that dom1.example is now served by master ($n)"
+ret=0
+$DIG soa dom1.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I:Adding domain dom1.example to catalog zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add e721433b6160b450260d4f54b3ec8bab30cb3b83.zones.catalog.example 3600 IN PTR dom1.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom1.example' from catalog 'catalog.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom1.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:checking that dom1.example is served by slave ($n)"
+ret=0
+$DIG soa dom1.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:Removing domain dom1.example from catalog zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+   server 10.53.0.1 5300
+   update delete e721433b6160b450260d4f54b3ec8bab30cb3b83.zones.catalog.example
+   send
+END
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'dom1.example' from catalog 'catalog.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:checking that dom1.example is not served by slave ($n)"
+ret=0
+$DIG soa dom1.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:Adding a domain dom2.example to master via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom2.example.db
+echo "@ IN NS invalid." >> ns1/dom2.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom2.example '{type master; file "dom2.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:Adding domains dom2.example, dom3.example and some trash to catalog zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add 636722929740e507aaf27c502812fc395d30fb17.zones.catalog.example 3600 IN PTR dom2.example.
+    update add b901f492f3ebf6c1e5b597e51766f02f0479eb03.zones.catalog.example 3600 IN PTR dom3.example.
+    update add e721433b6160b450260d4f54b3ec8bab30cb3b83.zones.catalog.example 3600 IN NS foo.bar.
+    update add trash.catalog.example 3600 IN A 1.2.3.4
+    update add trash2.foo.catalog.example 3600 IN A 1.2.3.4
+    update add trash3.zones.catalog.example 3600 IN NS a.dom2.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:checking that dom3.example is not served by master ($n)"
+ret=0
+$DIG soa dom3.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:Adding a domain dom3.example to master via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom3.example.db
+echo "@ IN NS invalid." >> ns1/dom3.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom3.example '{type master; file "dom3.example.db"; also-notify { 10.53.0.2; }; };' || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:checking that dom3.example is served by master ($n)"
+ret=0
+$DIG soa dom2.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom3.example' from catalog 'catalog.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom3.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:checking that dom3.example is served by slave ($n)"
+ret=0
+$DIG soa dom3.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:Adding dom4.example with 'masters' defined and a random label ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add somerandomlabel.zones.catalog.example 3600 IN PTR dom4.example.
+    update add masters.somerandomlabel.zones.catalog.example 3600 IN A 10.53.0.3
+    send
+END
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom4.example' from catalog 'catalog.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom4.example/IN' from 10.53.0.3#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:checking that dom4.example is served by slave ($n)"
+ret=0
+$DIG soa dom4.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:Removing domain dom2.example from catalog zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update delete 636722929740e507aaf27c502812fc395d30fb17.zones.catalog.example
+    send
+END
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'dom2.example' from catalog 'catalog.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+
+n=`expr $n + 1`
+echo "I:checking that dom2.example is not served by slave ($n)"
+ret=0
+$DIG soa dom2.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:checking that dom3.example is still served by slave ($n)"
+ret=0
+$DIG soa dom3.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:exit status: $status"
+exit $status
index 3e924723d1e00d0ee764f95e97f6aa83653e9422..3963447ed4e004862fc5d62509dc34b5b596443f 100644 (file)
@@ -68,7 +68,7 @@ RANDFILE=$TOP/bin/tests/system/random.data
 # load on the machine to make it unusable to other users.
 # v6synth
 SUBDIRS="acl additional allow_query addzone autosign builtin
-        cacheclean case checkconf @CHECKDS@ checknames checkzone
+        cacheclean case catz checkconf @CHECKDS@ checknames checkzone
         cookie @COVERAGE@ database digdelv dlv dlvauto dlz dlzexternal
         dname dns64 dnssec dsdigest dscp @DNSTAP@ dyndb ecdsa ednscompliance
         emptyzones fetchlimit filter-aaaa formerr forward geoip glue gost
index d14538e79b19513554943140a1f0159da5bc0bc6..25c81fd6dee92bc3510e49323f473d6c8379809b 100644 (file)
@@ -2373,6 +2373,8 @@ options {
 
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="dyndb.xml"/>
 
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="catz.xml"/>
+
     <section xml:id="ipv6"><info><title>IPv6 Support in <acronym>BIND</acronym> 9</title></info>
       <para>
        <acronym>BIND</acronym> 9 fully supports all currently
@@ -4674,6 +4676,16 @@ badresp:1,adberr:0,findfail:0,valfail:0]
       <optional> qname-wait-recurse <replaceable>yes_or_no</replaceable> </optional>
       <optional> automatic-interface-scan <replaceable>yes_or_no</replaceable> </optional>
     ; </optional>
+    <optional> catalog-zones {
+        zone <replaceable>quoted_string</replaceable>
+            <optional> default-masters
+               <optional>port <replaceable>ip_port</replaceable></optional>
+               <optional>dscp <replaceable>ip_dscp</replaceable></optional>
+               { ( <replaceable>masters_list</replaceable> | <replaceable>ip_addr</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> <optional>key <replaceable>key</replaceable></optional> ) ; <optional>...</optional> }</optional>
+         <optional>in-memory <replaceable>yes_or_no</replaceable></optional>
+         <optional>min-update-interval <replaceable>interval</replaceable></optional>
+       ; <optional>...</optional> };
+    ; </optional>
     <optional>v6-bias <replaceable>number</replaceable> ; </optional>
 };
 </programlisting>
index 1569804414e80ac112c0f3f35748d24eb7ac1678..a537c7664b15cbd2b6ff04441eafd3d7894c8e15 100644 (file)
Binary files a/doc/arm/Bv9ARM.pdf and b/doc/arm/Bv9ARM.pdf differ
diff --git a/doc/arm/catz.xml b/doc/arm/catz.xml
new file mode 100644 (file)
index 0000000..9316ee8
--- /dev/null
@@ -0,0 +1,229 @@
+<!--
+ - Copyright (C) 2016  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.
+-->
+<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="catz-info"><info><title>Catalog Zones</title></info>
+
+  <para>
+    A "catalog zone" is a special DNS zone that contains a list of
+    other zones to be served, along with their configuration parameters.
+    Zones listed in a catalog zone are called "member zones".
+    When a catalog zone is loaded or transferred to a slave server
+    which supports this functionality, the slave server will create
+    the member zones automatically. When the catalog zone is updated
+    is updated (for example, to add or delete member zones, or change
+    their configuration aprameters) those changes are immediately put
+    into effect. Because the catalog zone is a normal DNS zone, these
+    configuration changes can be propagated using the standard AXFR/IXFR
+    zone transfer mechanism.
+  </para>
+  <para>
+    Catalog zones' format and behavior are specified as an internet draft
+    for interoperability among DNS implementations.  As of this release, the
+    latest revision of the DNS catalog zones draft can be found here:
+    https://datatracker.ietf.org/doc/draft-muks-dnsop-dns-catalog-zones/
+  </para>
+
+  <section><info><title>Principle of Operation</title></info>
+    <para>
+      Normally, if a zone is to be served by a slave server, the
+      <filename>named.conf</filename> file on the server must list the
+      zone, or the zone must be added using <command>rndc addzone</command>.
+      In environments with a large number of slave servers and/or where
+      the zones being served are changing frequently, the overhead involved
+      in maintaining consistent zone configuration on all the slave
+      servers can be significant.
+    </para>
+    <para>
+      A catalog zone is a way to ease this administrative burden.  It is a
+      DNS zone that lists member zones that should be served by slave servers.
+      When a slave server receives an update to the catalog zone, it adds,
+      removes, or reconfigures member zones based on the data received.
+    </para>
+    <para>
+      To use a catalog zone, it must first be set up as a normal zone on
+      the master and the on slave servers that will be configured to use
+      it.  It must also be added to a <option>catalog-zones</option> list
+      in the <option>options</option> or <option>view</option> statement
+      in <filename>named.conf</filename>.  (This is comparable to the way
+      a policy zone is configured as a normal zone and also listed in
+      a <option>response-policy</option> statement.)
+    </para>
+    <para>
+      To use the catalog zone feature to serve a new member zone:
+      <itemizedlist>
+        <listitem>
+          <para>
+           Set up the the member zone to be served on the master as normal.
+           This could be done by editing <filename>named.conf</filename>,
+           or by running <command>rndc addzone</command>.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            Add an entry to the catalog zone for the new member zone.
+           This could be done by editing the catalog zone's master file
+           and running <command>rndc reload</command>, or by updating
+           the zone using <command>nsupdate</command>.
+          </para>
+        </listitem>
+      </itemizedlist>
+      The change to the catalog zone will be propagated from the master to all
+      slaves using the normal AXFR/IXFR mechanism.  When the slave receives the
+      update to the catalog zone, it will detect the entry for the new member
+      zone, create an instance of of that zone on the slave server, and point
+      that instance to the <option>masters</option> specified in the catalog
+      zone data. The newly created member zone is a normal slave zone, so
+      BIND will immediately initiate a transfer of zone contents from the
+      master.  Once complete, the slave will start serving the member zone.
+    </para>
+    <para>
+      Removing a member zone from a slave server requires nothing more than
+      deleting the member zone's entry in the catalog zone. The change to the
+      catalog cone is propagated to the slave server using the normal AXFR/IXFR
+      transfer mechanism.  The slave server, on processing the update, will
+      notice that the member zone has been removed.  It will stop serving the
+      zone and remove it froms its list of configured zones.  (Removing the
+      member zone from the master server has to be done in the normal way,
+      by editing the configuration file or running
+      <command>rndc delzone</command>.)
+    </para>
+  </section>
+
+  <section><info><title>Configuring Catalog Zones</title></info>
+    <para>
+      Catalog zones are configured with a <command>catalog-zones</command>
+      statement in the <literal>options</literal> or <literal>view</literal>
+      section of <filename>named.conf</filename>. For example,
+    </para>
+<screen>
+catalog-zones {
+    zone "catalog.example" default-masters { 10.53.0.1; } in-memory true min-update-interval 10;
+};
+</screen>
+    <para>
+      This statement specifies that the zone
+      <literal>catalog.example</literal> is a catalog zone. This zone must be
+      properly configured in the same view. In most configurations, it would
+      be a slave zone.
+    </para>
+    <para>
+      The <option>default-masters</option> option defines the default masters 
+      for member zones listed in a catalog zone. This can be overriden by
+      options within a catalog zone. If no such options are included, then
+      member zones will transfer their contents from the servers listed in
+      this option.
+    </para>
+    <para>
+      The <option>in-memory</option> option, if set to <literal>yes</literal>,
+      causes member zones to be stored only in memory. This is functionally
+      equivalent to configuring a slave zone without a <option>file</option>.
+      option.  The default is <literal>no</literal>; member zones' content
+      will be stored locally in a file whose name is automatically generated
+      from the view name, catalog zone name, and member zone name.
+    </para>
+    <para>
+      The <option>min-update-interval</option> option sets the minimum
+      interval between processing of updates to catalog zones, in seconds.
+      If an update to a catalog zone (for example, via IXFR) happens less
+      than <option>min-update-interval</option> seconds after the most
+      recent update, then the changes will not be carried out until this
+      interval has elapsed.  The default is <literal>5</literal> seconds.
+    </para>
+    <para>
+      Catalog zones are defined on a per-view basis. Configuring a non-empty
+      <option>catalog-zones</option> statement in a view will automatically
+      turn on <option>allow-new-zones</option> for that view. (Note: this
+      means <command>rndc addzone</command> and <command>rndc delzone</command>
+      will also work in any view that supports catalog zones.)
+    </para>
+  </section>
+
+  <section><info><title>Catalog Zone format</title></info>
+    <para>
+      A catalog zone is a regular DNS zone; therefore, it has to have a
+      single <literal>SOA</literal> and at least one <literal>NS</literal>
+      record.
+    </para>
+    <para>
+      A record stating the version of the catalog zone format is
+      also required. If the version number listed is not supported by
+      the server, then a catalog zone may not be used by that server.
+    </para>
+<screen>
+catalog.example.    IN SOA . . 2016022901 900 600 86400 1
+catalog.example.    IN NS nsexample.
+version.catalog.example.    IN TXT "1"
+</screen>
+    <para>
+      Note that this record must have the domain name
+      version.<replaceable>catalog-zone-name</replaceable>.  This illustrates
+      how the meaning of data stored in a catalog zone is indicated by the
+      the domain name label immediately before the catalog zone domain.
+    </para>
+    <para>
+      Catalog zones can contain a set of global options that are applied to
+      all member zones, overriding the settings for the catalog zone
+      in the configuration file.  Currently only the "masters" option
+      is supported:
+      <!-- TODO masters IN MX (with TSIG), allow-query, allow-tranfer -->
+    </para>
+<screen>
+masters.catalog.example    IN A 192.0.2.1
+masters.catalog.example    IN AAAA 2001:db8::1
+</screen>
+    <para>
+      (Note that if more than one server is defined, the order in which
+      they are used is undefined. The above example could correspond to
+      a zone configured with
+      <option>masters { 192.0.2.1; 2001:db8::1; };</option>
+      or with
+      <option>masters { 2001:db8::1; 192.0.2.1; };</option>.
+      There is currently no way to force a particular ordering.)
+    </para>
+    <para>
+      A member zone is added by including a <literal>PTR</literal>
+      resource record in the <literal>zones</literal> sub-domain of the
+      catalog zone. The record label is a <literal>SHA-1</literal> hash
+      of the member zone name in wire format. The target of the PTR
+      record is the member zone name. For example, to add the member
+      zone <literal>domain.example</literal>:
+    </para>
+<screen>
+5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example IN PTR domain.example.
+</screen>
+    <para>
+      The hash is necessary to identify options for a specific member
+      zone. The member zone-specific options are defined the same way as
+      global options, but in the member zone subdomain:
+    </para>
+<screen>
+masters.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example IN A 192.0.2.2
+masters.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example IN AAAA 2001:db8::2
+</screen>
+    <para>
+      As would be expected, options defined for a specific zone override
+      the global options defined in the catalog zone. These in turn override
+      the global options defined in the <literal>catalog-zones</literal>
+      statement in the configuration file.
+    </para>
+    <para>
+      (Note that none of the global records an option will be inherited if
+      any records are defined for that option for the specific zone.  For
+      example, if the zone had a <literal>masters</literal> record of type
+      A but not AAAA, then it would <emphasis>not</emphasis> inherit the
+      type AAAA record from the global option.)
+    </para>
+  </section>
+</section>
index 593cbf3879a12ee8207206c3f31ef28511013028..b0993c4697900e4ebb01e98070ba034da05045cf 100644 (file)
 
   <section xml:id="relnotes_features"><info><title>New Features</title></info>
     <itemizedlist>
+      <listitem>
+        <para>
+         A new method of provisioning secondary servers called
+         "Catalog Zones" has been added. This is an implementation of
+         <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://datatracker.ietf.org/doc/draft-muks-dnsop-dns-catalog-zones/">
+           draft-muks-dnsop-dns-catalog-zones/
+         </link>.
+        </para>
+        <para>
+         A catalog zone is a regular DNS zone which contains a list
+         of "member zones", along with the configuration options for
+         each of those zones.  When a server is configured to use a
+         catalog zone, all the zones listed in the catalog zone are
+         added to the local server as slave zones.  When the catalog
+         zone is updated (e.g., by adding or removing zones, or
+         changing configuration options for existing zones) those
+         changes will be put into effect.  Since the catalog zone is
+         itself a DNS zone, this means configuration changes can be
+         propagated to slaves using the standard AXFR/IXFR update
+         mechanism.
+        </para>
+        <para>
+          This feature should be considered experimental. It currently
+         supports only basic features; more advanced features such as
+         ACLs and TSIG keys are not yet supported. Example catalog
+         zone configurations can be found in the Chapter 9 of the
+         BIND Administrator Reference Manual.
+        </para>
+      </listitem>
       <listitem>
        <para>
          Added rndc python module.
index ee40594ed800f3edd7bd9c27ab81fd9dadcdc074..335d2f74da63ff5b8d9fe6d32a96fd0b92a016b7 100644 (file)
@@ -65,11 +65,11 @@ DNSTAPOBJS = dnstap.@O@ dnstap.pb-c.@O@
 
 # Alphabetically
 DNSOBJS =      acache.@O@ acl.@O@ adb.@O@ badcache.@O@ byaddr.@O@ \
-               cache.@O@ callbacks.@O@ clientinfo.@O@ compress.@O@ \
+               cache.@O@ callbacks.@O@ catz.@O@ clientinfo.@O@ compress.@O@ \
                db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
                dlz.@O@ dns64.@O@ dnssec.@O@ ds.@O@ dyndb.@O@ forward.@O@ \
-               iptable.@O@ journal.@O@ keydata.@O@ keytable.@O@ \
-               lib.@O@ log.@O@ lookup.@O@ \
+               ipkeylist.@O@ iptable.@O@ journal.@O@ keydata.@O@ \
+               keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \
                master.@O@ masterdump.@O@ message.@O@ \
                name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ nta.@O@ \
                order.@O@ peer.@O@ portlist.@O@ private.@O@ \
@@ -108,8 +108,8 @@ DNSSRCS =   acache.c acl.c adb.c badcache. byaddr.c \
                cache.c callbacks.c clientinfo.c compress.c \
                db.c dbiterator.c dbtable.c diff.c dispatch.c \
                dlz.c dns64.c dnssec.c ds.c dyndb.c forward.c \
-               iptable.c journal.c keydata.c keytable.c lib.c log.c \
-               lookup.c master.c masterdump.c message.c \
+               ipkeylist.c iptable.c journal.c keydata.c keytable.c lib.c \
+               log.c lookup.c master.c masterdump.c message.c \
                name.c ncache.c nsec.c nsec3.c nta.c \
                order.c peer.c portlist.c \
                rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c rdatalist.c \
index 41c865585a498134920a876d7c1176229014e99e..d2d51395307d271c9b86d258c368025cb4918838 100644 (file)
@@ -583,7 +583,7 @@ initialize_action(void) {
 }
 
 /*
- * Called via isc_radix_walk() to find IP table nodes that are
+ * Called via isc_radix_process() to find IP table nodes that are
  * insecure.
  */
 static void
diff --git a/lib/dns/catz.c b/lib/dns/catz.c
new file mode 100644 (file)
index 0000000..4866a5a
--- /dev/null
@@ -0,0 +1,1474 @@
+/*
+ * Copyright (C) 2015, 2016  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.
+ */
+
+/*! \file */
+
+#include <isc/hex.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/task.h>
+
+#include <dns/catz.h>
+#include <dns/dbiterator.h>
+#include <dns/events.h>
+#include <dns/rdatasetiter.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <isc/sha2.h>
+#include <isc/parseint.h>
+
+/*%
+ * Single member zone in a catalog
+ */
+struct dns_catz_entry {
+       dns_name_t              name;
+       dns_catz_options_t      opts;
+       isc_refcount_t          refs;
+};
+
+/*%
+ * Catalog zone
+ */
+struct dns_catz_zone {
+       dns_name_t              name;
+       dns_catz_zones_t        *catzs;
+       dns_rdata_t             soa;
+       /* key in entries is 'mhash', not domain name! */
+       isc_ht_t                *entries;
+       /*
+        * defoptions are taken from named.conf
+        * zoneoptions are global options from zone
+        */
+       dns_catz_options_t      defoptions;
+       dns_catz_options_t      zoneoptions;
+       isc_time_t              lastupdated;
+       isc_boolean_t           updatepending;
+       isc_uint32_t            version;
+
+       dns_db_t                *db;
+       dns_dbversion_t         *dbversion;
+
+       isc_timer_t             *updatetimer;
+       isc_event_t             updateevent;
+
+       isc_boolean_t           active;
+
+       isc_refcount_t          refs;
+};
+
+/*%
+ * Collection of catalog zones for a view
+ */
+struct dns_catz_zones {
+       isc_ht_t                        *zones;
+       isc_mem_t                       *mctx;
+       isc_refcount_t                  refs;
+       isc_mutex_t                     lock;
+       dns_catz_zonemodmethods_t       *zmm;
+       isc_taskmgr_t                   *taskmgr;
+       isc_timermgr_t                  *timermgr;
+       dns_view_t                      *view;
+       isc_task_t                      *updater;
+};
+
+void
+dns_catz_options_init(dns_catz_options_t *options) {
+       options->masters.addrs = NULL;
+       options->masters.dscps = NULL;
+       options->masters.keys = NULL;
+       options->masters.count = 0;
+
+       options->in_memory = ISC_FALSE;
+       options->min_update_interval = 5;
+}
+
+void
+dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) {
+       if (options->masters.count > 0)
+               dns_ipkeylist_clear(mctx, &options->masters);
+}
+
+isc_result_t
+dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
+                     dns_catz_options_t *dst)
+{
+       /* TODO error handling */
+       REQUIRE(dst != NULL);
+       REQUIRE(dst->masters.count == 0);
+
+       if (src->masters.count) {
+               dns_ipkeylist_copy(mctx, &src->masters, &dst->masters);
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
+                           dns_catz_options_t *opts)
+{
+       if (opts->masters.count == 0)
+               dns_catz_options_copy(mctx, defaults, opts);
+
+       /* This option is always taken from config, so it's always 'default' */
+       opts->in_memory = defaults->in_memory;
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain,
+                  dns_catz_entry_t **nentryp)
+{
+       dns_catz_entry_t *nentry;
+       isc_result_t res;
+
+       REQUIRE(nentryp != NULL && *nentryp == NULL);
+       REQUIRE(domain != NULL);
+
+       nentry = isc_mem_get(mctx, sizeof(dns_catz_entry_t));
+       if (nentry == NULL)
+               return (ISC_R_NOMEMORY);
+
+       dns_name_init(&nentry->name, NULL);
+       res = dns_name_dup(domain, mctx, &nentry->name);
+       if (res != ISC_R_SUCCESS)
+               goto cleanup;
+
+       dns_catz_options_init(&nentry->opts);
+       isc_refcount_init(&nentry->refs, 1);
+       *nentryp = nentry;
+       return (ISC_R_SUCCESS);
+
+cleanup:
+       isc_mem_put(mctx, nentry, sizeof(dns_catz_entry_t));
+       return (res);
+}
+
+dns_name_t *
+dns_catz_entry_getname(dns_catz_entry_t *entry) {
+       return (&entry->name);
+}
+
+isc_result_t
+dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry,
+                   dns_catz_entry_t **nentryp)
+{
+       isc_result_t res;
+       dns_catz_entry_t *nentry = NULL;
+
+       res = dns_catz_entry_new(zone->catzs->mctx, &entry->name, &nentry);
+       if (res != ISC_R_SUCCESS)
+               return (res);
+
+       res = dns_catz_options_copy(zone->catzs->mctx, &entry->opts,
+                                   &nentry->opts);
+       if (res != ISC_R_SUCCESS)
+               dns_catz_entry_detach(zone, &nentry);
+
+       *nentryp = nentry;
+       return (res);
+}
+
+void
+dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp) {
+       REQUIRE(entryp != NULL && *entryp == NULL);
+       isc_refcount_increment(&entry->refs, NULL);
+       *entryp = entry;
+}
+
+void
+dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp) {
+       dns_catz_entry_t *entry;
+       isc_mem_t *mctx;
+       unsigned int refs;
+
+       REQUIRE(entryp != NULL && *entryp != NULL);
+
+       entry = *entryp;
+       *entryp = NULL;
+
+       mctx = zone->catzs->mctx;
+
+       isc_refcount_decrement(&entry->refs, &refs);
+       if (refs == 0) {
+               dns_catz_options_free(&entry->opts, mctx);
+               dns_name_free(&entry->name, mctx);
+               isc_refcount_destroy(&entry->refs);
+               isc_mem_put(mctx, entry, sizeof(dns_catz_entry_t));
+       }
+}
+
+isc_boolean_t
+dns_catz_entry_validate(const dns_catz_entry_t *entry) {
+       UNUSED(entry);
+
+       return (ISC_TRUE);
+}
+
+isc_boolean_t
+dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
+       if (ea->opts.masters.count != eb->opts.masters.count)
+               return (ISC_FALSE);
+
+       if (memcmp(ea->opts.masters.addrs, eb->opts.masters.addrs,
+                  ea->opts.masters.count * sizeof(isc_sockaddr_t)))
+               return (ISC_FALSE);
+
+       /* xxxwpk TODO compare dscps/keys! */
+       return (ISC_TRUE);
+}
+
+
+dns_name_t *
+dns_catz_zone_getname(dns_catz_zone_t *zone) {
+       REQUIRE(zone != NULL);
+
+       return (&zone->name);
+}
+
+dns_catz_options_t *
+dns_catz_zone_getdefoptions(dns_catz_zone_t *zone) {
+       REQUIRE(zone != NULL);
+
+       return (&zone->defoptions);
+}
+
+void
+dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone) {
+       REQUIRE(zone != NULL);
+
+       dns_catz_options_free(&zone->defoptions, zone->catzs->mctx);
+       dns_catz_options_init(&zone->defoptions);
+}
+
+static isc_result_t
+newzonewalk(void *udata, const unsigned char *key, isc_uint32_t keysize,
+           void *data)
+{
+       isc_result_t result;
+       dns_catz_zone_t *zone = udata;
+       dns_catz_entry_t *nentry = (dns_catz_entry_t *) data;
+       dns_catz_entry_t *oentry;
+       char cznamebuf[DNS_NAME_FORMATSIZE];
+       char znamebuf[DNS_NAME_FORMATSIZE];
+       isc_buffer_t czname;
+       isc_buffer_t zname;
+
+       REQUIRE(zone != NULL);
+       REQUIRE(zone->catzs->view != NULL);
+
+       dns_catz_options_setdefault(zone->catzs->mctx, &zone->zoneoptions,
+                                   &nentry->opts);
+       isc_buffer_init(&czname, cznamebuf, DNS_NAME_FORMATSIZE);
+       isc_buffer_init(&zname, znamebuf, DNS_NAME_FORMATSIZE);
+       dns_name_totext(&zone->name, ISC_TRUE, &czname);
+       isc_buffer_putuint8(&czname, 0);
+       dns_name_totext(&nentry->name, ISC_TRUE, &zname);
+       isc_buffer_putuint8(&zname, 0);
+
+       result = isc_ht_find(zone->entries, key, keysize, (void **) &oentry);
+       if (result != ISC_R_SUCCESS) {
+               result = zone->catzs->zmm->addzone(nentry, zone,
+                                                  zone->catzs->view,
+                                                  zone->catzs->taskmgr,
+                                                  zone->catzs->zmm->udata);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                             "catz: adding zone '%s' from catalog '%s' - %s",
+                             znamebuf, cznamebuf, isc_result_totext(result));
+               return (ISC_R_SUCCESS);
+       }
+
+       if (dns_catz_entry_cmp(oentry, nentry) != ISC_TRUE) {
+               result = zone->catzs->zmm->modzone(nentry, zone,
+                                                  zone->catzs->view,
+                                                  zone->catzs->taskmgr,
+                                                  zone->catzs->zmm->udata);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                             "catz: modifying zone '%s' from catalog "
+                             "'%s' - %s",
+                             znamebuf, cznamebuf,
+                             isc_result_totext(result));
+       }
+
+       dns_catz_entry_detach(zone, &oentry);
+       result = isc_ht_delete(zone->entries, key, keysize);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+oldzonewalk(void *udata, const unsigned char *key, isc_uint32_t keysize,
+           void *data)
+{
+       isc_result_t result;
+       dns_catz_zone_t *zone = udata;
+       dns_catz_entry_t *entry = (dns_catz_entry_t *) data;
+       char cznamebuf[DNS_NAME_FORMATSIZE];
+       char znamebuf[DNS_NAME_FORMATSIZE];
+       isc_buffer_t czname;
+       isc_buffer_t zname;
+
+       UNUSED(key);
+       UNUSED(keysize);
+
+       REQUIRE(zone != NULL);
+       REQUIRE(zone->catzs->view != NULL);
+
+       isc_buffer_init(&czname, cznamebuf, DNS_NAME_MAXTEXT);
+       isc_buffer_init(&zname, znamebuf, DNS_NAME_MAXTEXT);
+       dns_name_totext(&zone->name, ISC_TRUE, &czname);
+       isc_buffer_putuint8(&czname, 0);
+       dns_name_totext(&entry->name, ISC_TRUE, &zname);
+       isc_buffer_putuint8(&czname, 0);
+       result = zone->catzs->zmm->delzone(entry, zone, zone->catzs->view,
+                                 zone->catzs->taskmgr,
+                                 zone->catzs->zmm->udata);
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                     DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                     "catz: deleting zone '%s' from catalog '%s' - %s",
+                     znamebuf, cznamebuf, isc_result_totext(result));
+       dns_catz_entry_detach(zone, &entry);
+
+       return (ISC_R_EXISTS);
+}
+
+isc_result_t
+dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
+       isc_result_t res;
+
+       REQUIRE(target != NULL);
+       REQUIRE(newzone != NULL);
+
+       /* TODO verify the new zone first! */
+
+       /* Copy zoneoptions from newzone into target */
+       dns_catz_options_free(&target->zoneoptions, target->catzs->mctx);
+       dns_catz_options_copy(target->catzs->mctx, &newzone->zoneoptions,
+                             &target->zoneoptions);
+       dns_catz_options_setdefault(target->catzs->mctx, &target->defoptions,
+                                   &target->zoneoptions);
+
+       /*
+        * first - walk the new zone and find all nodes that are not in the
+        * old zone, or are in both zones and are modified
+        */
+       res = isc_ht_walk(newzone->entries, newzonewalk, target);
+       /* newzonewalk always returns ISC_R_SUCCESS */
+       RUNTIME_CHECK(res == ISC_R_SUCCESS);
+
+       /*
+        * then - walk the old zone; only deleted entries should remain
+        * return (ISC_R_SUCCESS);
+        */
+       res = isc_ht_walk(target->entries, oldzonewalk, target);
+       /*
+        * oldzonewalk always returns ISC_R_EXISTS, so walk should return
+        * ISC_R_SUCCESS
+        */
+       RUNTIME_CHECK(res == ISC_R_SUCCESS);
+
+       /* at this moment target->entries has to be be empty */
+       INSIST(isc_ht_count(target->entries) == 0);
+       isc_ht_destroy(&target->entries);
+
+       target->entries = newzone->entries;
+       newzone->entries = NULL;
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
+                  isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+                  isc_timermgr_t *timermgr)
+{
+       dns_catz_zones_t *new_zones;
+       isc_result_t result;
+
+       REQUIRE(catzsp != NULL && *catzsp == NULL);
+       REQUIRE(zmm != NULL);
+
+       new_zones = isc_mem_get(mctx, sizeof(*new_zones));
+       if (new_zones == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(new_zones, 0, sizeof(*new_zones));
+
+       result = isc_refcount_init(&new_zones->refs, 1);
+       if (result != ISC_R_SUCCESS) {
+               isc_mem_put(mctx, new_zones, sizeof(*new_zones));
+               return (result);
+       }
+
+       result = isc_ht_init(&new_zones->zones, mctx, 4);
+       if (result != ISC_R_SUCCESS) {
+               isc_refcount_destroy(&new_zones->refs);
+               isc_mem_put(mctx, new_zones, sizeof(*new_zones));
+               return (result);
+       }
+
+       isc_mem_attach(mctx, &new_zones->mctx);
+       isc_mutex_init(&new_zones->lock);
+       new_zones->zmm = zmm;
+       new_zones->taskmgr = taskmgr;
+       new_zones->timermgr = timermgr;
+       isc_task_create(new_zones->taskmgr, 0, &new_zones->updater);
+       *catzsp = new_zones;
+
+       return (ISC_R_SUCCESS);
+}
+
+void
+dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
+       REQUIRE(catzs != NULL);
+       REQUIRE(view != NULL);
+       /* either it's a new one or it's being reconfigured */
+       REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name));
+       catzs->view = view;
+}
+
+isc_result_t
+dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
+                 const dns_name_t *name) {
+       isc_result_t res;
+       dns_catz_zone_t *new_zone;
+
+       REQUIRE(zonep != NULL && *zonep == NULL);
+
+       new_zone = isc_mem_get(catzs->mctx, sizeof(*new_zone));
+       if (new_zone == NULL) {
+               return (ISC_R_NOMEMORY);
+       }
+
+       memset(new_zone, 0, sizeof(*new_zone));
+
+       dns_name_init(&new_zone->name, NULL);
+
+       res = dns_name_dup(name, catzs->mctx, &new_zone->name);
+       if (res != ISC_R_SUCCESS) {
+               isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone));
+               return (res);
+       }
+
+       res = isc_ht_init(&new_zone->entries, catzs->mctx, 4);
+       if (res != ISC_R_SUCCESS) {
+               dns_name_free(&new_zone->name, catzs->mctx);
+               isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone));
+               return (res);
+       }
+
+       isc_time_settoepoch(&new_zone->lastupdated);
+       new_zone->updatepending = ISC_FALSE;
+       new_zone->db = NULL;
+       new_zone->dbversion = NULL;
+       new_zone->updatetimer = NULL;
+       new_zone->catzs = catzs;
+       dns_catz_options_init(&new_zone->defoptions);
+       dns_catz_options_init(&new_zone->zoneoptions);
+       new_zone->active = ISC_TRUE;
+       new_zone->version = (isc_uint32_t)(-1);
+       isc_refcount_init(&new_zone->refs, 1);
+
+       *zonep = new_zone;
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name,
+                 dns_catz_zone_t **zonep)
+{
+       dns_catz_zone_t *new_zone = NULL;
+       isc_result_t res;
+
+       REQUIRE(catzs != NULL);
+       REQUIRE(name != NULL);
+       REQUIRE(zonep != NULL && *zonep == NULL);
+
+       LOCK(&catzs->lock);
+
+       res = dns_catz_new_zone(catzs, &new_zone, name);
+       if (res != ISC_R_SUCCESS)
+               goto cleanup;
+
+       res = isc_ht_add(catzs->zones, new_zone->name.ndata,
+                       new_zone->name.length, new_zone);
+       if (res != ISC_R_SUCCESS) {
+               dns_catz_zone_detach(&new_zone);
+               if (res != ISC_R_EXISTS)
+                       goto cleanup;
+       }
+
+       if (res == ISC_R_EXISTS) {
+               res = isc_ht_find(catzs->zones, name->ndata,
+                                    name->length, (void **) &new_zone);
+               INSIST(res == ISC_R_SUCCESS && !new_zone->active);
+               new_zone->active = ISC_TRUE;
+       }
+
+       *zonep = new_zone;
+
+ cleanup:
+       UNLOCK(&catzs->lock);
+
+       return (res);
+}
+
+dns_catz_zone_t *
+dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name) {
+       isc_result_t result;
+       dns_catz_zone_t *found;
+
+       result = isc_ht_find(catzs->zones, name->ndata, name->length,
+                            (void **) &found);
+       if (result != ISC_R_SUCCESS)
+               return (NULL);
+
+       return (found);
+}
+
+void
+dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp) {
+       REQUIRE(catzsp != NULL && *catzsp == NULL);
+
+       isc_refcount_increment(&catzs->refs, NULL);
+       *catzsp = catzs;
+}
+
+static isc_result_t
+freewalk(void *udata, const unsigned char *key, isc_uint32_t keysize,
+        void *data)
+{
+       dns_catz_zone_t *zone = (dns_catz_zone_t *) udata;
+       dns_catz_entry_t *entry = (dns_catz_entry_t *) data;
+
+       UNUSED(key);
+       UNUSED(keysize);
+
+       dns_catz_entry_detach(zone, &entry);
+
+       return (ISC_R_EXISTS);
+}
+
+void
+dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep) {
+       REQUIRE(zonep != NULL && *zonep == NULL);
+       isc_refcount_increment(&zone->refs, NULL);
+       *zonep = zone;
+}
+
+void
+dns_catz_zone_detach(dns_catz_zone_t **zonep) {
+       dns_catz_zone_t *zone;
+       isc_mem_t *mctx;
+       unsigned int refs;
+
+       REQUIRE(zonep != NULL && *zonep != NULL);
+
+       zone = *zonep;
+       *zonep = NULL;
+       isc_refcount_decrement(&zone->refs, &refs);
+       if (refs == 0) {
+               if (zone->entries != NULL) {
+                       isc_result_t res;
+
+                       /*
+                        * freewalk always returns ISC_R_EXISTS, triggering
+                        * isc_ht_walk to delete the node.  If isc_ht_walk
+                        * returns an error, it is a critical condition
+                        */
+                       res = isc_ht_walk(zone->entries, freewalk, zone);
+                       INSIST(res == ISC_R_SUCCESS);
+
+                       /* the hashtable has to be empty now */
+                       INSIST(isc_ht_count(zone->entries) == 0);
+                       isc_ht_destroy(&zone->entries);
+               }
+               mctx = zone->catzs->mctx;
+               isc_refcount_destroy(&zone->refs);
+               dns_name_free(&zone->name, mctx);
+               dns_catz_options_free(&zone->defoptions, mctx);
+               dns_catz_options_free(&zone->zoneoptions, mctx);
+               zone->catzs = NULL;
+               isc_mem_put(mctx, zone, sizeof(dns_catz_zone_t));
+       }
+}
+
+static isc_result_t
+catzsfreewalk(void *udata, const unsigned char *key, isc_uint32_t keysize,
+             void *data)
+{
+       dns_catz_zones_t *catzs = (dns_catz_zones_t *) udata;
+       dns_catz_zone_t *zone = (dns_catz_zone_t *) data;
+
+       UNUSED(key);
+       UNUSED(keysize);
+       UNUSED(catzs);
+
+       dns_catz_zone_detach(&zone);
+
+       return (ISC_R_EXISTS);
+}
+
+void
+dns_catz_catzs_detach(dns_catz_zones_t ** catzsp) {
+       dns_catz_zones_t *catzs;
+       isc_result_t res;
+       unsigned int refs;
+
+       REQUIRE(catzsp != NULL);
+       catzs = *catzsp;
+       REQUIRE(catzs != NULL);
+
+       *catzsp = NULL;
+       isc_refcount_decrement(&catzs->refs, &refs);
+
+       if (refs == 0) {
+               DESTROYLOCK(&catzs->lock);
+               if (catzs->zones != NULL) {
+                       res = isc_ht_walk(catzs->zones, catzsfreewalk, catzs);
+                       INSIST(res == ISC_R_SUCCESS);
+                       INSIST(isc_ht_count(catzs->zones) == 0);
+                       isc_ht_destroy(&catzs->zones);
+               }
+               isc_refcount_destroy(&catzs->refs);
+               isc_task_destroy(&catzs->updater);
+               isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs));
+       }
+}
+
+typedef enum {
+       CATZ_OPT_NONE,
+       CATZ_OPT_ZONES,
+       CATZ_OPT_MASTERS,
+       CATZ_OPT_ALLOW_QUERY,
+       CATZ_OPT_ALLOW_TRANSFER,
+       CATZ_OPT_VERSION,
+} catz_opt_t;
+
+static isc_boolean_t
+catz_opt_cmp(const dns_label_t *option, const char *opt) {
+       unsigned int l = strlen(opt);
+       if (option->length - 1 == l &&
+           memcmp(opt, option->base + 1, l - 1) == 0)
+               return (ISC_TRUE);
+       else
+               return (ISC_FALSE);
+}
+
+static catz_opt_t
+catz_get_option(const dns_label_t *option) {
+       if (catz_opt_cmp(option, "zones"))
+               return (CATZ_OPT_ZONES);
+       else if (catz_opt_cmp(option, "masters"))
+               return (CATZ_OPT_MASTERS);
+       else if (catz_opt_cmp(option, "allow-query"))
+               return (CATZ_OPT_ALLOW_QUERY);
+       else if (catz_opt_cmp(option, "allow-transfer"))
+               return (CATZ_OPT_ALLOW_TRANSFER);
+       else if (catz_opt_cmp(option, "version"))
+               return (CATZ_OPT_VERSION);
+       else
+               return (CATZ_OPT_NONE);
+}
+
+static isc_result_t
+catz_process_global_list(dns_catz_zone_t *zone, catz_opt_t opt,
+                        dns_rdataset_t *value, dns_label_t *mhash)
+{
+       isc_result_t res;
+       dns_rdata_t rdata;
+       dns_rdata_ptr_t ptr;
+       dns_catz_entry_t *entry = NULL;
+       dns_name_t emptyname;
+
+       REQUIRE(zone != NULL);
+       REQUIRE(DNS_RDATASET_VALID(value));
+       REQUIRE(mhash != NULL);
+
+       dns_name_init(&emptyname, NULL);
+       switch (opt) {
+       case CATZ_OPT_ZONES:
+               if (value->rdclass != dns_rdataclass_in ||
+                   value->type != dns_rdatatype_ptr) {
+                       break;
+               }
+
+               /*
+                * We only take -first- value, as mhash must be
+                * different
+                */
+               dns_rdataset_first(value);
+               dns_rdata_init(&rdata);
+               dns_rdataset_current(value, &rdata);
+
+               res = dns_rdata_tostruct(&rdata, &ptr, zone->catzs->mctx);
+
+               res = isc_ht_find(zone->entries, mhash->base,
+                                 mhash->length, (void **) &entry);
+               if (res == ISC_R_SUCCESS) {
+                       if (dns_name_countlabels(&entry->name) != 0) {
+                               /* we have a duplicate */
+                               dns_rdata_freestruct(&ptr);
+                               return (ISC_R_FAILURE);
+                       } else {
+                               res = dns_name_dup(&ptr.ptr, zone->catzs->mctx,
+                                                  &entry->name);
+                               if (res != ISC_R_SUCCESS) {
+                                       dns_rdata_freestruct(&ptr);
+                                       return (res);
+                               }
+                       }
+               } else {
+                       res = dns_catz_entry_new(zone->catzs->mctx, &ptr.ptr,
+                                                &entry);
+                       if (res != ISC_R_SUCCESS) {
+                               dns_rdata_freestruct(&ptr);
+                               return (res);
+                       }
+
+                       res = isc_ht_add(zone->entries, mhash->base,
+                                        mhash->length, entry);
+                       if (res != ISC_R_SUCCESS) {
+                               dns_rdata_freestruct(&ptr);
+                               dns_catz_entry_detach(zone, &entry);
+                               return (res);
+                       }
+               }
+
+               dns_rdata_freestruct(&ptr);
+               break;
+       default:
+               return (ISC_R_FAILURE);
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
+       isc_result_t res;
+       dns_rdata_t rdata;
+       dns_rdata_txt_t rdatatxt;
+       dns_rdata_txt_string_t rdatastr;
+       isc_uint32_t tversion;
+       char t[16];
+
+       REQUIRE(zone != NULL);
+       REQUIRE(DNS_RDATASET_VALID(value));
+
+       if (value->rdclass != dns_rdataclass_in ||
+           value->type != dns_rdatatype_txt)
+               return (ISC_R_FAILURE);
+
+       dns_rdataset_first(value);
+       dns_rdata_init(&rdata);
+       dns_rdataset_current(value, &rdata);
+
+       res = dns_rdata_tostruct(&rdata, &rdatatxt, zone->catzs->mctx);
+       if (res != ISC_R_SUCCESS)
+               return (res);
+
+       res = dns_rdata_txt_first(&rdatatxt);
+       if (res != ISC_R_SUCCESS)
+               goto cleanup;
+
+       res = dns_rdata_txt_current(&rdatatxt, &rdatastr);
+       if (res != ISC_R_SUCCESS)
+               goto cleanup;
+       res = dns_rdata_txt_next(&rdatatxt);
+       if (res != ISC_R_NOMORE) {
+               res = ISC_R_FAILURE;
+               goto cleanup;
+       }
+       if (rdatastr.length > 15) {
+               res = ISC_R_BADNUMBER;
+               goto cleanup;
+       }
+       memcpy(t, rdatastr.data, rdatastr.length);
+       t[rdatastr.length] = 0;
+       res = isc_parse_uint32(&tversion, t, 10);
+       if (res != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+       zone->version = tversion;
+       res = ISC_R_SUCCESS;
+
+cleanup:
+       dns_rdata_freestruct(&rdatatxt);
+       return (res);
+}
+
+static isc_result_t
+catz_process_ipkl(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl,
+                 dns_rdataset_t *value)
+{
+       isc_result_t res;
+       dns_rdata_t rdata;
+       dns_rdata_in_a_t rdataa;
+
+       REQUIRE(zone != NULL);
+       REQUIRE(ipkl != NULL);
+       REQUIRE(DNS_RDATASET_VALID(value));
+
+       if (value->rdclass != dns_rdataclass_in ||
+           value->type != dns_rdatatype_a)
+               return (ISC_R_FAILURE);
+
+       dns_rdataset_first(value);
+       do {
+               dns_rdata_init(&rdata);
+               dns_rdataset_current(value, &rdata);
+
+               res = dns_rdata_tostruct(&rdata, &rdataa, zone->catzs->mctx);
+               if (res != ISC_R_SUCCESS)
+                       return (res);
+
+               if (ipkl->count == 0) {
+                       ipkl->addrs = isc_mem_allocate(zone->catzs->mctx,
+                                                      sizeof(isc_sockaddr_t));
+                       ipkl->keys = isc_mem_allocate(zone->catzs->mctx,
+                                                     sizeof(dns_name_t *));
+               } else {
+                       ipkl->addrs =
+                               isc_mem_reallocate(zone->catzs->mctx,
+                                                  ipkl->addrs,
+                                                  ((ipkl->count + 1) *
+                                                   sizeof(isc_sockaddr_t)));
+                       ipkl->keys =
+                               isc_mem_reallocate(zone->catzs->mctx,
+                                                  ipkl->keys,
+                                                  ((ipkl->count + 1) *
+                                                   sizeof(dns_name_t *)));
+               }
+
+               /*
+                *
+                * port 0 == take the default
+                */
+               isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
+                                   &rdataa.in_addr, 0);
+               ipkl->keys[ipkl->count] = NULL;
+               ipkl->count++;
+               dns_rdata_freestruct(&rdataa);
+       } while (dns_rdataset_next(value) == ISC_R_SUCCESS);
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+catz_process_suboption(dns_catz_zone_t *zone, dns_label_t *mhash,
+                      catz_opt_t subopt, dns_rdataset_t *value)
+{
+       isc_result_t res;
+       dns_catz_entry_t *entry = NULL;
+
+       REQUIRE(zone != NULL);
+       REQUIRE(mhash != NULL);
+       REQUIRE(DNS_RDATASET_VALID(value));
+
+       /*
+        * we're adding this entry now, in case the option is invalid we'll get
+        * rid of in verification phase
+        */
+       res = isc_ht_find(zone->entries, mhash->base,
+                         mhash->length, (void **) &entry);
+       if (res != ISC_R_SUCCESS) {
+               res = dns_catz_entry_new(zone->catzs->mctx, NULL, &entry);
+               if (res != ISC_R_SUCCESS)
+                       return (res);
+               res = isc_ht_add(zone->entries, mhash->base, mhash->length,
+                                entry);
+               if (res != ISC_R_SUCCESS) {
+                       dns_catz_entry_detach(zone, &entry);
+                       return (res);
+               }
+       }
+
+       switch (subopt) {
+       case CATZ_OPT_MASTERS:
+               return (catz_process_ipkl(zone, &entry->opts.masters, value));
+               break;
+       case CATZ_OPT_ALLOW_QUERY:
+#if 0
+               return (process_apl(zone, &entry->opts))
+#endif
+       case CATZ_OPT_ALLOW_TRANSFER:
+       default:
+               return (ISC_R_FAILURE);
+       }
+
+       return (ISC_R_FAILURE);
+}
+
+static isc_result_t
+catz_process_global_option(dns_catz_zone_t *zone, catz_opt_t option,
+                          dns_rdataset_t *value)
+{
+       REQUIRE(zone != NULL);
+       REQUIRE(DNS_RDATASET_VALID(value));
+
+       switch (option) {
+       case CATZ_OPT_MASTERS:
+               return (catz_process_ipkl(zone, &zone->zoneoptions.masters,
+                                         value));
+               break;
+       case CATZ_OPT_VERSION:
+               return (catz_process_version(zone, value));
+               break;
+       case CATZ_OPT_ALLOW_QUERY:
+#if 0
+               return (process_apl(zone, &entry->opts))
+#endif
+       case CATZ_OPT_ALLOW_TRANSFER:
+       default:
+               return (ISC_R_FAILURE);
+       }
+       return (ISC_R_FAILURE);
+}
+
+static isc_result_t
+catz_process_value(dns_catz_zone_t *zone, dns_name_t *value,
+                  dns_rdataset_t *rdataset)
+{
+       dns_label_t suboption, option, mhash;
+       catz_opt_t opt, subopt;
+
+       REQUIRE(zone != NULL);
+       REQUIRE(value != NULL);
+       REQUIRE(DNS_RDATASET_VALID(rdataset));
+
+       switch (value->labels) {
+       case 1:
+               /* Catalog zone-wide option */
+               dns_name_getlabel(value, 0, &option);
+               opt = catz_get_option(&option);
+               return (catz_process_global_option(zone, opt, rdataset));
+
+       case 2:
+               /* Global list (eg. 'zones') */
+               dns_name_getlabel(value, 0, &mhash);
+               dns_name_getlabel(value, 1, &option);
+               opt = catz_get_option(&option);
+               return (catz_process_global_list(zone, opt, rdataset, &mhash));
+
+       case 3:
+               /* Zone option */
+               dns_name_getlabel(value, 0, &suboption);
+               dns_name_getlabel(value, 1, &mhash);
+               dns_name_getlabel(value, 2, &option);
+               opt = catz_get_option(&option);
+               subopt = catz_get_option(&suboption);
+               if (opt == CATZ_OPT_ZONES)
+                       return (catz_process_suboption(zone, &mhash, subopt,
+                                                      rdataset));
+               break;
+
+       default:
+               break;
+       }
+
+       return (ISC_R_FAILURE);
+}
+
+isc_result_t
+dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone,
+                       dns_name_t *src_name, dns_rdataset_t *rdataset)
+{
+       isc_result_t res;
+       int order;
+       unsigned int nlabels;
+       dns_namereln_t nrres;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdata_soa_t soa;
+       dns_name_t prefix;
+
+       REQUIRE(catzs != NULL);
+       REQUIRE(zone != NULL);
+
+       nrres = dns_name_fullcompare(src_name, &zone->name, &order, &nlabels);
+       if (nrres == dns_namereln_equal) {
+               if (rdataset->type == dns_rdatatype_soa) {
+                       res = dns_rdataset_first(rdataset);
+                       if (res != ISC_R_SUCCESS)
+                               return (res);
+                       dns_rdataset_current(rdataset, &rdata);
+                       res = dns_rdata_tostruct(&rdata, &soa, NULL);
+                       /*
+                        * xxxwpk TODO do we want to save something from SOA?
+                        */
+                       return (res);
+
+               } else if (rdataset->type == dns_rdatatype_ns) {
+                       return (ISC_R_SUCCESS);
+               } else {
+                       return (ISC_R_UNEXPECTED);
+               }
+       } else if (nrres != dns_namereln_subdomain) {
+               return (ISC_R_UNEXPECTED);
+       }
+
+       dns_name_init(&prefix, NULL);
+       dns_name_split(src_name, zone->name.labels, &prefix, NULL);
+       res = catz_process_value(zone, &prefix, rdataset);
+
+       return (res);
+}
+
+isc_result_t
+dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
+                                isc_buffer_t **buffer)
+{
+       isc_buffer_t *tbuf = NULL;
+       isc_sha256_t sha256;
+       isc_region_t r;
+       isc_result_t res;
+
+       REQUIRE(zone != NULL);
+       REQUIRE(entry != NULL);
+       REQUIRE(buffer != NULL && *buffer != NULL);
+
+       isc_buffer_allocate(zone->catzs->mctx, &tbuf,
+                           strlen(zone->catzs->view->name) +
+                           2*DNS_NAME_FORMATSIZE + 2);
+       INSIST(tbuf != NULL);
+       isc_buffer_putstr(tbuf, zone->catzs->view->name);
+       isc_buffer_putstr(tbuf, "_");
+       res = dns_name_totext(&zone->name, ISC_TRUE, tbuf);
+       if (res != ISC_R_SUCCESS)
+               goto cleanup;
+       isc_buffer_putstr(tbuf, "_");
+       res = dns_name_totext(&entry->name, ISC_TRUE, tbuf);
+       if (res != ISC_R_SUCCESS)
+               goto cleanup;
+
+       res = isc_buffer_reserve(buffer, strlen("__catz__") +
+                                ISC_SHA256_DIGESTSTRINGLENGTH +
+                                strlen(".db"));
+       if (res != ISC_R_SUCCESS)
+               goto cleanup;
+
+       isc_buffer_usedregion(tbuf, &r);
+       isc_buffer_putstr(*buffer, "__catz__");
+       if (tbuf->used > ISC_SHA256_DIGESTSTRINGLENGTH) {
+               isc_sha256_init(&sha256);
+               isc_sha256_update(&sha256, r.base, r.length);
+               /* we can do that because digest string < 2*DNS_NAME */
+               isc_sha256_end(&sha256, (char *) r.base);
+               isc_buffer_putstr(*buffer, (char *) r.base);
+       } else {
+               isc_buffer_copyregion(*buffer, &r);
+       }
+       isc_buffer_putstr(*buffer, ".db");
+       res = ISC_R_SUCCESS;
+
+cleanup:
+       isc_buffer_free(&tbuf);
+       return (res);
+}
+
+isc_result_t
+dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
+                         isc_buffer_t **buf)
+{
+       /* We have to generate a text buffer with regular zone config:
+        * zone foo.bar {
+        *      type slave;
+        *      masters { ip1 port1; ip2 port2; };
+        * }
+        */
+       isc_buffer_t *buffer = NULL;
+       isc_result_t res;
+       isc_uint32_t i;
+       isc_netaddr_t netaddr;
+
+       REQUIRE(zone != NULL);
+       REQUIRE(entry != NULL);
+       REQUIRE(buf != NULL && *buf == NULL);
+
+       /*
+        * The buffer will be reallocated if something won't fit, ISC_BUFFER_INC
+        * seems like a good start.
+        */
+       res = isc_buffer_allocate(zone->catzs->mctx, &buffer, ISC_BUFFER_INCR);
+       if (res != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+
+       isc_buffer_setautorealloc(buffer, ISC_TRUE);
+       isc_buffer_putstr(buffer, "zone ");
+       dns_name_totext(&entry->name, ISC_TRUE, buffer);
+       isc_buffer_putstr(buffer, " { type slave; masters { ");
+       for (i = 0; i < entry->opts.masters.count; i++) {
+               /* TODO port and DSCP */
+               isc_netaddr_fromsockaddr(&netaddr,
+                                        &entry->opts.masters.addrs[i]);
+               isc_buffer_reserve(&buffer, INET6_ADDRSTRLEN);
+               res = isc_netaddr_totext(&netaddr, buffer);
+               RUNTIME_CHECK(res == ISC_R_SUCCESS);
+
+               if (entry->opts.masters.keys[i] != NULL) {
+                       isc_buffer_putstr(buffer, " key ");
+                       res = dns_name_totext(entry->opts.masters.keys[i],
+                                             ISC_TRUE, buffer);
+                       if (res != ISC_R_SUCCESS)
+                               goto cleanup;
+               }
+               isc_buffer_putstr(buffer, "; ");
+       }
+       isc_buffer_putstr(buffer, "};");
+       if (entry->opts.in_memory == ISC_FALSE) {
+               isc_buffer_putstr(buffer, "file \"");
+               dns_catz_generate_masterfilename(zone, entry, &buffer);
+               isc_buffer_putstr(buffer, "\";");
+
+       }
+       isc_buffer_putstr(buffer, "};");
+
+       *buf = buffer;
+       return (ISC_R_SUCCESS);
+
+cleanup:
+       if (buffer)
+               isc_buffer_free(&buffer);
+       return (res);
+}
+
+
+void
+dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event) {
+       isc_result_t res;
+       dns_catz_zone_t * zone;
+       (void) task;
+
+       REQUIRE(event != NULL);
+       zone = event->ev_arg;
+       REQUIRE(zone != NULL);
+
+       LOCK(&zone->catzs->lock);
+       zone->updatepending = ISC_FALSE;
+       dns_catz_update_from_db(zone->db, zone->catzs);
+       dns_db_detach(&zone->db);
+       if (zone->updatetimer) {
+               isc_timer_detach(&zone->updatetimer);
+       }
+       isc_event_free(&event);
+       res = isc_time_now(&zone->lastupdated);
+       RUNTIME_CHECK(res == ISC_R_SUCCESS);
+       UNLOCK(&zone->catzs->lock);
+}
+
+isc_result_t
+dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
+       dns_catz_zones_t *catzs;
+       dns_catz_zone_t *zone = NULL;
+       isc_time_t now;
+       isc_uint64_t tdiff;
+       isc_result_t res = ISC_R_SUCCESS;
+       isc_region_t r;
+
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(fn_arg != NULL);
+       catzs = (dns_catz_zones_t*) fn_arg;
+
+       dns_name_toregion(&db->origin, &r);
+
+       LOCK(&catzs->lock);
+       res = isc_ht_find(catzs->zones, r.base, r.length, (void **) &zone);
+       INSIST(res == ISC_R_SUCCESS);
+
+       if (zone->updatepending == ISC_FALSE) {
+               isc_time_now(&now);
+               tdiff = isc_time_microdiff(&now, &zone->lastupdated)/1000000;
+               if (tdiff < zone->defoptions.min_update_interval) {
+                       isc_interval_t interval;
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                                     "catz: new zone version came too soon, "
+                                     "deferring update");
+                       isc_interval_set(&interval, 5 - tdiff, 0);
+                       zone->updatepending = ISC_TRUE;
+                       dns_db_attach(db, &zone->db);
+                       dns_db_currentversion(db, &zone->dbversion);
+                       isc_timer_create(catzs->timermgr, isc_timertype_once,
+                                        NULL, &interval, catzs->updater,
+                                        dns_catz_update_taskaction,
+                                        zone, &zone->updatetimer);
+               } else {
+                       isc_event_t *event;
+                       zone->updatepending = ISC_TRUE;
+                       dns_db_attach(db, &zone->db);
+                       dns_db_currentversion(db, &zone->dbversion);
+                       ISC_EVENT_INIT(&zone->updateevent,
+                                      sizeof(zone->updateevent), 0, NULL,
+                                      DNS_EVENT_CATZUPDATED,
+                                      dns_catz_update_taskaction,
+                                      zone, zone, NULL, NULL);
+                       event = &zone->updateevent;
+                       isc_task_send(catzs->updater, &event);
+               }
+       } else {
+               INSIST(db == zone->db);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+                             "catz: update already queued");
+               dns_db_closeversion(zone->db, &zone->dbversion, ISC_FALSE);
+               dns_db_currentversion(zone->db, &zone->dbversion);
+       }
+
+       UNLOCK(&catzs->lock);
+
+       return (res);
+}
+
+void
+dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
+       dns_catz_zone_t *oldzone = NULL, *newzone = NULL;
+       isc_result_t res;
+       isc_region_t r;
+       dns_dbnode_t *node = NULL;
+       dns_dbiterator_t *it = NULL;
+       dns_fixedname_t fixname;
+       dns_name_t *name;
+       dns_rdatasetiter_t *rdsiter = NULL;
+       dns_rdataset_t rdataset;
+       char bname[DNS_NAME_FORMATSIZE];
+       isc_buffer_t ibname;
+       isc_uint32_t vers;
+
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(catzs != NULL);
+
+       /*
+        * Create a new catz in the same context as current catz
+        */
+       dns_name_toregion(&db->origin, &r);
+       res = isc_ht_find(catzs->zones, r.base, r.length, (void **) &oldzone);
+       if (res != ISC_R_SUCCESS) {
+               /* this can happen if we remove the zone in the meantime */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "catz: zone '%s' not in config",
+                             bname);
+               return;
+       }
+
+       isc_buffer_init(&ibname, bname, DNS_NAME_FORMATSIZE);
+       res = dns_name_totext(&db->origin, ISC_TRUE, &ibname);
+       INSIST(res == ISC_R_SUCCESS);
+
+       res = dns_db_getsoaserial(db, oldzone->dbversion, &vers);
+       if (res != ISC_R_SUCCESS) {
+               /* A zone without SOA record?!? */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "catz: zone '%s' has no SOA record (%s)",
+                             bname, isc_result_totext(res));
+               return;
+       }
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                     DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                     "catz: updating catalog zone '%s' with serial %d",
+                     bname, vers);
+
+       res = dns_catz_new_zone(catzs, &newzone, &db->origin);
+       if (res != ISC_R_SUCCESS) {
+               dns_db_closeversion(db, &oldzone->dbversion, ISC_FALSE);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "catz: failed to create new zone - %s",
+                             isc_result_totext(res));
+               return;
+       }
+
+       res = dns_db_createiterator(db, DNS_DB_NONSEC3, &it);
+       if (res != ISC_R_SUCCESS) {
+               dns_catz_zone_detach(&newzone);
+               dns_db_closeversion(db, &oldzone->dbversion, ISC_FALSE);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "catz: failed to create DB iterator - %s",
+                             isc_result_totext(res));
+               return;
+       }
+
+       dns_fixedname_init(&fixname);
+       name = dns_fixedname_name(&fixname);
+
+       /*
+        * Iterate over database to fill the new zone
+        */
+       res = dns_dbiterator_first(it);
+       if (res != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "catz: failed to get db iterator - %s",
+                             isc_result_totext(res));
+       }
+
+       while (res == ISC_R_SUCCESS) {
+               res = dns_dbiterator_current(it, &node, name);
+               if (res != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                                     "catz: failed to get db iterator - %s",
+                                     isc_result_totext(res));
+                       break;
+               }
+
+               res = dns_db_allrdatasets(db, node, oldzone->dbversion, 0,
+                                         &rdsiter);
+               if (res != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                                     "catz: failed to fetch rrdatasets - %s",
+                                     isc_result_totext(res));
+                       dns_db_detachnode(db, &node);
+                       break;
+               }
+
+               dns_rdataset_init(&rdataset);
+               res = dns_rdatasetiter_first(rdsiter);
+               while (res == ISC_R_SUCCESS) {
+                       dns_rdatasetiter_current(rdsiter, &rdataset);
+                       res = dns_catz_update_process(catzs, newzone, name,
+                                                     &rdataset);
+                       if (res != ISC_R_SUCCESS) {
+                               char cname[DNS_NAME_FORMATSIZE];
+                               dns_name_format(name, cname,
+                                               DNS_NAME_FORMATSIZE);
+                               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                             DNS_LOGMODULE_MASTER,
+                                             ISC_LOG_WARNING,
+                                             "catz: unknown record in catalog "
+                                             "zone - %s (%s) - ignoring",
+                                             cname,
+                                             isc_result_totext(res));
+                       }
+                       dns_rdataset_disassociate(&rdataset);
+                       if (res != ISC_R_SUCCESS) {
+                               break;
+                       }
+
+                       res = dns_rdatasetiter_next(rdsiter);
+               }
+
+               dns_rdatasetiter_destroy(&rdsiter);
+
+               dns_db_detachnode(db, &node);
+               res = dns_dbiterator_next(it);
+       }
+
+       dns_dbiterator_destroy(&it);
+       dns_db_closeversion(db, &oldzone->dbversion, ISC_FALSE);
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                     DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+                     "catz: update_from_db: iteration finished");
+
+       /*
+        * Finally merge new zone into old zone
+        */
+       res = dns_catz_zones_merge(oldzone, newzone);
+       if (res != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER,
+                             ISC_LOG_ERROR,
+                             "catz: failed merging zones: %s",
+                             isc_result_totext(res));
+
+               return;
+       }
+
+       dns_catz_zone_detach(&newzone);
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                     DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+                     "catz: update_from_db: new zone merged");
+}
+
+
+static isc_result_t
+resetactivebitwalk(void *udata, const unsigned char *key, isc_uint32_t keysize,
+                  void *data)
+{
+       dns_catz_zone_t *zone = (dns_catz_zone_t *) data;
+
+       UNUSED(udata);
+       UNUSED(key);
+       UNUSED(keysize);
+
+       zone->active = ISC_FALSE;
+       return (ISC_R_SUCCESS);
+}
+
+
+void
+dns_catz_prereconfig(dns_catz_zones_t *catzs) {
+       isc_ht_walk(catzs->zones, resetactivebitwalk, NULL);
+}
+
+static isc_result_t
+postreconfigwalk(void *udata, const unsigned char *key, isc_uint32_t keysize,
+                void *data)
+{
+       isc_result_t res;
+       dns_catz_zone_t *newzone = NULL;
+       dns_catz_zones_t *catzs = (dns_catz_zones_t *) udata;
+       dns_catz_zone_t *zone = (dns_catz_zone_t *) data;
+
+       UNUSED(key);
+       UNUSED(keysize);
+
+       REQUIRE(catzs != NULL);
+       REQUIRE(zone != NULL);
+
+       if (zone->active == ISC_FALSE) {
+               char cname[DNS_NAME_FORMATSIZE];
+               dns_name_format(&zone->name, cname, DNS_NAME_FORMATSIZE);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER,
+                             ISC_LOG_WARNING,
+                             "catz: removing catalog zone %s", cname);
+
+               /* Merge the old zone with an empty one to remove all members */
+               res = dns_catz_new_zone(catzs, &newzone, &zone->name);
+               INSIST(res == ISC_R_SUCCESS);
+               dns_catz_zones_merge(zone, newzone);
+               dns_catz_zone_detach(&newzone);
+
+               /* Make sure that we have an empty catalog zone */
+               INSIST(isc_ht_count(zone->entries) == 0);
+
+               dns_catz_zone_detach(&zone);
+               return (ISC_R_EXISTS);
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+
+void
+dns_catz_postreconfig(dns_catz_zones_t *catzs) {
+       isc_ht_walk(catzs->zones, postreconfigwalk, catzs);
+}
+
+isc_result_t
+dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) {
+       return (isc_ht_iter_create(catz->entries, itp));
+}
index 4193b2f506160e8ed0ec1173265f7f5bee27c823..2d4e9197e45108eb6b03befd769ccb0e30ae3030 100644 (file)
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 /*! \file */
 
 /***
@@ -307,6 +305,8 @@ dns_db_beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
 
 isc_result_t
 dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+       dns_dbonupdatelistener_t *listener;
+
        /*
         * Finish loading 'db'.
         */
@@ -315,6 +315,11 @@ dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
        REQUIRE(DNS_CALLBACK_VALID(callbacks));
        REQUIRE(callbacks->add_private != NULL);
 
+       for (listener = ISC_LIST_HEAD(db->update_listeners);
+            listener != NULL;
+            listener = ISC_LIST_NEXT(listener, link))
+               listener->onupdate(db, listener->onupdate_arg);
+
        return ((db->methods->endload)(db, callbacks));
 }
 
@@ -330,7 +335,8 @@ dns_db_load2(dns_db_t *db, const char *filename, dns_masterformat_t format) {
 
 isc_result_t
 dns_db_load3(dns_db_t *db, const char *filename, dns_masterformat_t format,
-            unsigned int options) {
+            unsigned int options)
+{
        isc_result_t result, eresult;
        dns_rdatacallbacks_t callbacks;
 
@@ -445,6 +451,7 @@ void
 dns_db_closeversion(dns_db_t *db, dns_dbversion_t **versionp,
                    isc_boolean_t commit)
 {
+       dns_dbonupdatelistener_t *listener;
 
        /*
         * Close version '*versionp'.
@@ -456,6 +463,13 @@ dns_db_closeversion(dns_db_t *db, dns_dbversion_t **versionp,
 
        (db->methods->closeversion)(db, versionp, commit);
 
+       if (commit == ISC_TRUE) {
+               for (listener = ISC_LIST_HEAD(db->update_listeners);
+                    listener != NULL;
+                    listener = ISC_LIST_NEXT(listener, link))
+                       listener->onupdate(db, listener->onupdate_arg);
+       }
+
        ENSURE(*versionp == NULL);
 }
 
@@ -1043,3 +1057,55 @@ dns_db_rpz_ready(dns_db_t *db) {
                return (ISC_R_SUCCESS);
        return ((db->methods->rpz_ready)(db));
 }
+
+/**
+ * Attach a notify-on-update function the database
+ */
+isc_result_t
+dns_db_updatenotify_register(dns_db_t *db,
+                            dns_dbupdate_callback_t fn,
+                            void *fn_arg)
+{
+       dns_dbonupdatelistener_t *listener;
+
+       REQUIRE(db != NULL);
+       REQUIRE(fn != NULL);
+
+       listener = isc_mem_get(db->mctx, sizeof(dns_dbonupdatelistener_t));
+       if (listener == NULL)
+               return (ISC_R_NOMEMORY);
+
+       listener->onupdate = fn;
+       listener->onupdate_arg = fn_arg;
+
+       ISC_LINK_INIT(listener, link);
+       ISC_LIST_APPEND(db->update_listeners, listener, link);
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_db_updatenotify_unregister(dns_db_t *db,
+                              dns_dbupdate_callback_t fn,
+                              void *fn_arg)
+{
+       dns_dbonupdatelistener_t *listener;
+
+       REQUIRE(db != NULL);
+
+       for (listener = ISC_LIST_HEAD(db->update_listeners);
+            listener != NULL;
+            listener = ISC_LIST_NEXT(listener, link))
+       {
+               if ((listener->onupdate == fn) &&
+                   (listener->onupdate_arg == fn_arg))
+               {
+                       ISC_LIST_UNLINK(db->update_listeners, listener, link);
+                       isc_mem_put(db->mctx, listener,
+                                   sizeof(dns_dbonupdatelistener_t));
+                       return (ISC_R_SUCCESS);
+               }
+       }
+
+       return (ISC_R_NOTFOUND);
+}
diff --git a/lib/dns/include/dns/catz.h b/lib/dns/include/dns/catz.h
new file mode 100644 (file)
index 0000000..917d958
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2015-2016  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.
+ */
+
+
+#ifndef DNS_CATZ_H
+#define DNS_CATZ_H 1
+
+#include <isc/ht.h>
+#include <isc/lang.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+
+#include <dns/db.h>
+#include <dns/fixedname.h>
+#include <dns/ipkeylist.h>
+#include <dns/rdata.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_CATZ_ERROR_LEVEL   ISC_LOG_WARNING
+#define DNS_CATZ_INFO_LEVEL    ISC_LOG_INFO
+#define DNS_CATZ_DEBUG_LEVEL1  ISC_LOG_DEBUG(1)
+#define DNS_CATZ_DEBUG_LEVEL2  ISC_LOG_DEBUG(2)
+#define DNS_CATZ_DEBUG_LEVEL3  ISC_LOG_DEBUG(3)
+#define DNS_CATZ_DEBUG_QUIET   (DNS_CATZ_DEBUG_LEVEL3+1)
+
+/*
+ * Catalog Zones functions and structures.
+ */
+
+/*
+ * Options for a member zone in a catalog
+ */
+struct dns_catz_entry_options {
+       /*
+        * Options that can be overriden in catalog zone
+        */
+       /* masters definition */
+       dns_ipkeylist_t masters;
+
+       /*
+        * Options that are only set in named.conf
+        */
+       /* zone should not be stored on disk (no 'file' statement in def */
+       isc_boolean_t in_memory;
+       /*
+        * Minimal interval between catalog zone updates, if a new version
+        * of catalog zone is received before this time the update will be
+        * postponed. This is a global option for the whole catalog zone.
+        */
+       isc_uint32_t min_update_interval;
+};
+
+void
+dns_catz_options_init(dns_catz_options_t *options);
+/*%<
+ * Initialize 'options' to NULL values.
+ *
+ * Requires:
+ * \li options to be non NULL
+ */
+
+void
+dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx);
+/*%<
+ * Free 'options' contents into 'mctx'. ('options' itself is not freed.)
+ *
+ * Requires:
+ * \li options to be non NULL
+ * \li mctx to be a valid memory context
+ */
+
+isc_result_t
+dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *opts,
+                     dns_catz_options_t *nopts);
+/*%<
+ * Duplicate 'opts' into 'nopts', allocating space from 'mctx'
+ *
+ * Requires:
+ * \li 'mctx' to be a valid memory context
+ * \li 'options' to be non NULL and valid options
+ * \li 'nopts' to be non NULL
+ */
+
+isc_result_t
+dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
+                           dns_catz_options_t *opts);
+/*%<
+ * Replace empty values in 'opts' with values from 'defaults'
+ *
+ * Requires:
+ * \li mctx to be a valid memory context
+ * \li defaults to be non NULL and valid options
+ * \li opts to be non NULL
+ */
+
+dns_name_t *
+dns_catz_entry_getname(dns_catz_entry_t *entry);
+/*%<
+ * Get domain name for 'entry'
+ *
+ * Requires:
+ * \li entry to be non NULL
+ *
+ * Returns:
+ * \li domain name for entry
+ */
+
+isc_result_t
+dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain,
+                  dns_catz_entry_t **nentryp);
+/*%<
+ * Allocate a new catz_entry on 'mctx', with the name 'domain'
+ *
+ * Requires:
+ * \li mctx to be a valid memory context
+ * \li domain to be valid dns_name or NULL
+ * \li nentryp to be non NULL, *nentryp to be NULL
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS on success
+ * \li ISC_R_NOMEMORY on allocation failure
+ */
+
+isc_result_t
+dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry,
+                   dns_catz_entry_t **nentryp);
+/*%<
+ * Allocate a new catz_entry and deep copy 'entry' into 'nentryp'.
+ *
+ * Requires:
+ * \li mctx to be a valid memory context
+ * \li entry to be non NULL
+ * \li nentryp to be non NULL, *nentryp to be NULL
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS on success
+ * \li ISC_R_NOMEMORY on allocation failure
+ */
+
+void
+dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp);
+/*%<
+ * Attach an entry
+ *
+ * Requires:
+ * \li entry is not NULL
+ * \li entryp is not NULL, *entryp is NULL
+ */
+
+void
+dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp);
+/*%<
+ * Detach an entry, free if no further references
+ *
+ * Requires:
+ * \li zone is not NULL
+ * \li entryp is not NULL, *entryp is not NULL
+ */
+
+isc_result_t
+dns_catz_entry_validate(const dns_catz_entry_t *entry);
+/*%<
+ * Validate whether entry is correct.
+ * (NOT YET IMPLEMENTED: always returns true)
+ */
+
+isc_boolean_t
+dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb);
+/*%<
+ * Deep compare two entries
+ *
+ * Requires:
+ * \li ea is not NULL
+ * \li eb is not NULL
+ *
+ * Returns:
+ * \li ISC_TRUE if entries are the same
+ * \li ISC_FALSE if the entries differ
+ */
+
+void
+dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep);
+/*%<
+ * Attach a catzone
+ *
+ * Requires:
+ * \li zone is not NULL
+ * \li zonep is not NULL, *zonep is NULL
+ */
+
+void
+dns_catz_zone_detach(dns_catz_zone_t** zonep);
+/*%<
+ * Detach a zone, free if no further references
+ *
+ * Requires:
+ * \li zonep is not NULL, *zonep is not NULL
+ */
+
+isc_result_t
+dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
+               const dns_name_t *name);
+/*%<
+ * Allocate a new catz zone on catzs mctx
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li zonep is not NULL, *zonep is NULL
+ * \li name is not NULL
+ *
+ */
+
+dns_name_t *
+dns_catz_zone_getname(dns_catz_zone_t *zone);
+/*%<
+ * Get catalog zone name
+ *
+ * Requires:
+ * \li zone is not NULL
+ */
+
+dns_catz_options_t *
+dns_catz_zone_getdefoptions(dns_catz_zone_t *zone);
+/*%<
+ * Get default member zone options for catalog zone 'zone'
+ *
+ * Requires:
+ * \li zone is not NULL
+ */
+
+void
+dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone);
+/*%<
+ * Reset the default member zone options for catalog zone 'zone' to
+ * the default values.
+ *
+ * Requires:
+ * \li zone is not NULL
+ */
+
+isc_result_t
+dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone);
+/*%<
+ * Merge 'newzone' into 'target', calling addzone/delzone/modzone
+ * (from zone->catzs->zmm) for appropriate member zones.
+ *
+ * Requires:
+ * \li orig is not NULL
+ * \li newzone is not NULL, *newzone is not NULL
+ *
+ */
+
+isc_result_t
+dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone,
+                        dns_name_t *src_name, dns_rdataset_t *rdataset);
+/*%<
+ * Process a single rdataset from a catalog zone 'zone' update, src_name is the
+ * record name.
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li zone is not NULL
+ * \li src_name is not NULL
+ * \li rdataset is valid
+ */
+
+isc_result_t
+dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
+                                isc_buffer_t **buffer);
+/*%<
+ * Generate master file name and put it into *buffer (might be reallocated).
+ * The general format of the file name is:
+ * __catz__catalog.zone.name__member_zone_name.db
+ * But if it's too long it's shortened to:
+ * __catz__unique_hash_generated_from_the_above.db
+ *
+ * Requires:
+ * \li zone is not NULL
+ * \li entry is not NULL
+ * \li buffer is not NULL and *buffer is not NULL
+ */
+
+isc_result_t
+dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
+                         isc_buffer_t **buf);
+/*%<
+ * Generate a zone config entry (in text form) from dns_catz_entry and puts
+ * it into *buf. buf might be reallocated.
+ *
+ * Requires:
+ * \li zone is not NULL
+ * \li entry is not NULL
+ * \li buf is not NULL
+ * \li *buf is NULL
+ *
+ */
+
+
+/* Methods provided by named to dynamically modify the member zones */
+/* xxxwpk TODO config! */
+struct dns_catz_zonemodmethods {
+       isc_result_t (*addzone)(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+                       dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata);
+       isc_result_t (*modzone)(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+                       dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata);
+       isc_result_t (*delzone)(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+                       dns_view_t *view, isc_taskmgr_t *task, void *udata);
+       void * udata;
+};
+
+
+isc_result_t
+dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
+                  isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+                  isc_timermgr_t *timermgr);
+/*%<
+ * Allocate a new catz_zones object, a collection storing all catalog zones
+ * for a view.
+ *
+ * Requires:
+ * \li catzsp is not NULL, *catzsp is NULL
+ * \li zmm is not NULL
+ *
+ */
+
+isc_result_t
+dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name,
+                 dns_catz_zone_t **catzp);
+/*%<
+ * Allocate a new catz named 'name' and put it in 'catzs' collection.
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li name is not NULL
+ * \li zonep is not NULL, *zonep is NULL
+ *
+ */
+
+dns_catz_zone_t *
+dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name);
+/*%<
+ * Returns a zone named 'name' from collection 'catzs'
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li name is not NULL
+ */
+
+void
+dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp);
+/*%<
+ * Attach 'catzs' to 'catzsp'
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li catzsp is not NULL, *catzsp is NULL
+ */
+
+void
+dns_catz_catzs_detach(dns_catz_zones_t **catzsp);
+/*%<
+ * Detach 'catzsp', free if no further references
+ *
+ * Requires:
+ * \li catzsp is not NULL, *catzsp is not NULL
+ */
+
+void
+dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view);
+/*%<
+ * Set a view for catzs
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li catzs->view is NULL or catzs->view == view
+ */
+
+
+isc_result_t
+dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg);
+/*%<
+ * Callback for update of catalog zone database.
+ * If there was no catalog zone update recently it launches an
+ * update_taskaction immediately.
+ * If there was an update recently it schedules update_taskaction for some time
+ * in the future.
+ * If there is an update scheduled it replaces old db version with a new one.
+ *
+ * Requires:
+ * \li db is a valid database
+ * \li fn_arg is not NULL (casted to dns_catz_zones_t*)
+ */
+
+void
+dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event);
+/*%<
+ * Task that launches dns_catz_update_from_db
+ *
+ * Requires:
+ * \li event is not NULL
+ */
+
+void
+dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs);
+/*%<
+ * Process an updated database for a catalog zone.
+ * It creates a new catz, iterates over database to fill it with content, and
+ * then merges new catz into old catz.
+ *
+ * Requires:
+ * \li db is a valid DB
+ * \li catzs is not NULL
+ *
+ */
+
+void
+dns_catz_prereconfig(dns_catz_zones_t *catzs);
+/*%<
+ * Called before reconfig, clears 'active' flag on all the zones in set
+ *
+ * Requires:
+ * \li catzs is not NULL
+ *
+ */
+
+void
+dns_catz_postreconfig(dns_catz_zones_t *catzs);
+/*%<
+ * Called after reconfig, walks through all zones in set, removes those
+ * inactive and force reload of those with changed configuration.
+ *
+ * Requires:
+ * \li catzs is not NULL
+ */
+
+isc_result_t
+dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp);
+/*%<
+ * Get the hashtable iterator on catalog zone members, point '*itp' to it.
+ * 
+ * Returns:
+ * \li #ISC_R_SUCCESS          -- success
+ * \li Any other value         -- failure
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_CATZ_H_ */
index 9715249f9aa64f84f6bc8404490662c6b006ad58..dfb25d406ea89362e5a7c23af7385e626df2c9be 100644 (file)
@@ -203,6 +203,9 @@ typedef isc_result_t
                      unsigned int argc, char *argv[], void *driverarg,
                      dns_db_t **dbp);
 
+typedef isc_result_t
+(*dns_dbupdate_callback_t)(dns_db_t *db, void *fn_arg);
+
 #define DNS_DB_MAGIC           ISC_MAGIC('D','N','S','D')
 #define DNS_DB_VALID(db)       ISC_MAGIC_VALID(db, DNS_DB_MAGIC)
 
@@ -216,19 +219,26 @@ typedef isc_result_t
  * invariants.
  */
 struct dns_db {
-       unsigned int                    magic;
-       unsigned int                    impmagic;
-       dns_dbmethods_t *               methods;
-       isc_uint16_t                    attributes;
-       dns_rdataclass_t                rdclass;
-       dns_name_t                      origin;
-       isc_ondestroy_t                 ondest;
-       isc_mem_t *                     mctx;
+       unsigned int                            magic;
+       unsigned int                            impmagic;
+       dns_dbmethods_t *                       methods;
+       isc_uint16_t                            attributes;
+       dns_rdataclass_t                        rdclass;
+       dns_name_t                              origin;
+       isc_ondestroy_t                         ondest;
+       isc_mem_t *                             mctx;
+       ISC_LIST(dns_dbonupdatelistener_t)      update_listeners;
 };
 
 #define DNS_DBATTR_CACHE               0x01
 #define DNS_DBATTR_STUB                        0x02
 
+struct dns_dbonupdatelistener {
+       dns_dbupdate_callback_t                 onupdate;
+       void *                                  onupdate_arg;
+       ISC_LINK(dns_dbonupdatelistener_t)      link;
+};
+
 /*@{*/
 /*%
  * Options that can be specified for dns_db_find().
@@ -1624,6 +1634,35 @@ dns_db_rpz_ready(dns_db_t *db);
  * Finish loading a response policy zone.
  */
 
+isc_result_t
+dns_db_updatenotify_register(dns_db_t *db,
+                            dns_dbupdate_callback_t fn,
+                            void *fn_arg);
+/*%<
+ * Register a notify-on-update callback function to a database.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database
+ * \li 'db' does not have an update callback registered
+ * \li 'fn' is not NULL
+ *
+ */
+
+isc_result_t
+dns_db_updatenotify_unregister(dns_db_t *db,
+                              dns_dbupdate_callback_t fn,
+                              void *fn_arg);
+/*%<
+ * Unregister a notify-on-update callback.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database
+ * \li 'db' has update callback registered
+ *
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_DB_H */
index f43d40bacc6c060bead86cec6fcb6ac0a8962198..45a1709a9e18fa986f3d200686fa806b19a9c2d3 100644 (file)
 #define DNS_EVENT_KEYDONE                      (ISC_EVENTCLASS_DNS + 50)
 #define DNS_EVENT_SETNSEC3PARAM                        (ISC_EVENTCLASS_DNS + 51)
 #define DNS_EVENT_SETSERIAL                    (ISC_EVENTCLASS_DNS + 52)
+#define DNS_EVENT_CATZUPDATED                  (ISC_EVENTCLASS_DNS + 53)
+#define DNS_EVENT_CATZADDZONE                  (ISC_EVENTCLASS_DNS + 54)
+#define DNS_EVENT_CATZMODZONE                  (ISC_EVENTCLASS_DNS + 55)
+#define DNS_EVENT_CATZDELZONE                  (ISC_EVENTCLASS_DNS + 56)
 
 #define DNS_EVENT_FIRSTEVENT                   (ISC_EVENTCLASS_DNS + 0)
 #define DNS_EVENT_LASTEVENT                    (ISC_EVENTCLASS_DNS + 65535)
diff --git a/lib/dns/include/dns/ipkeylist.h b/lib/dns/include/dns/ipkeylist.h
new file mode 100644 (file)
index 0000000..5066181
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016  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.
+ */
+
+#ifndef DNS_IPKEYLIST_H
+#define DNS_IPKEYLIST_H 1
+
+#include <isc/types.h>
+#include <dns/types.h>
+
+/*%
+ * A structure holding a list of addresses, dscps and keys.  Used to
+ * store masters for a slave zone, created by parsing config options.
+ */
+struct dns_ipkeylist {
+       isc_sockaddr_t          *addrs;
+       isc_dscp_t              *dscps;
+       dns_name_t              **keys;
+       isc_uint32_t            count;
+};
+
+void
+dns_ipkeylist_clear(isc_mem_t *mctx, dns_ipkeylist_t *ipkl);
+/*%<
+ * Free `ipkl` contents using `mctx`.
+ *
+ * After this call, `ipkl` is a freshly cleared structure with all
+ * pointers set to `NULL` and count set to 0.
+ *
+ * Requires:
+ *\li  'mctx' to be a valid memory context.
+ *\li  'ipkl' to be non NULL and have its members `addrs` and `keys`
+ *      allocated. 'dscps' might be NULL.
+ */
+
+void
+dns_ipkeylist_copy(isc_mem_t *mctx, const dns_ipkeylist_t *src,
+                  dns_ipkeylist_t *dst);
+/*%<
+ * Deep copy `src` into empty `dst`, allocating `dst`'s contents.
+ *
+ * Requires:
+ *\li  'mctx' to be a valid memory context.
+ *\li  'src' to be non NULL
+ *\li  'dst' to be non NULL and point to an empty \ref dns_ipkeylist_t
+ *       with all pointers set to `NULL` and count set to 0.
+ */
+
+#endif
index ef45840d12dbcc6ece1e4ae52f725ecb58fe8a56..8129450d5b532eda008c738a74a264f8b51b607c 100644 (file)
@@ -391,7 +391,7 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep);
  */
 
 isc_result_t
-dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
+dns_rbt_findname(dns_rbt_t *rbt, const dns_name_t *name, unsigned int options,
                 dns_name_t *foundname, void **data);
 /*%<
  * Get the data pointer associated with 'name'.
@@ -430,7 +430,7 @@ dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
  */
 
 isc_result_t
-dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
+dns_rbt_findnode(dns_rbt_t *rbt, const dns_name_t *name, dns_name_t *foundname,
                 dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
                 unsigned int options, dns_rbtfindcallback_t callback,
                 void *callback_arg);
index 54fe547e15950af143d738f876f7885990372a3a..e29d28d359b8df6a6d9a0c2ba1a20f4fa306ab07 100644 (file)
@@ -45,6 +45,12 @@ typedef struct dns_adbfind                   dns_adbfind_t;
 typedef ISC_LIST(dns_adbfind_t)                        dns_adbfindlist_t;
 typedef struct dns_badcache                    dns_badcache_t;
 typedef struct dns_byaddr                      dns_byaddr_t;
+typedef struct dns_catz_zonemodmethods         dns_catz_zonemodmethods_t;
+typedef struct dns_catz_entry_options          dns_catz_options_t;
+typedef struct dns_catz_entry                  dns_catz_entry_t;
+typedef struct dns_catz_zone                   dns_catz_zone_t;
+typedef struct dns_catz_changed                        dns_catz_changed_t;
+typedef struct dns_catz_zones                  dns_catz_zones_t;
 typedef struct dns_client                      dns_client_t;
 typedef void                                   dns_clientrestrans_t;
 typedef void                                   dns_clientreqtrans_t;
@@ -57,6 +63,7 @@ typedef struct dns_dbimplementation           dns_dbimplementation_t;
 typedef struct dns_dbiterator                  dns_dbiterator_t;
 typedef void                                   dns_dbload_t;
 typedef void                                   dns_dbnode_t;
+typedef struct dns_dbonupdatelistener          dns_dbonupdatelistener_t;
 typedef struct dns_dbtable                     dns_dbtable_t;
 typedef void                                   dns_dbversion_t;
 typedef struct dns_dlzimplementation           dns_dlzimplementation_t;
@@ -146,6 +153,7 @@ typedef struct dns_zone                             dns_zone_t;
 typedef ISC_LIST(dns_zone_t)                   dns_zonelist_t;
 typedef struct dns_zonemgr                     dns_zonemgr_t;
 typedef struct dns_zt                          dns_zt_t;
+typedef struct dns_ipkeylist                   dns_ipkeylist_t;
 
 /*
  * If we are not using GSSAPI, define the types we use as opaque types here.
index 9156e82e0a55b1cb600ae263a1e571c9301a9ce1..c2e7e8d5574399dfb8a309bc03e67cec33fba102 100644 (file)
@@ -70,6 +70,7 @@
 #include <isc/stdtime.h>
 
 #include <dns/acl.h>
+#include <dns/catz.h>
 #include <dns/clientinfo.h>
 #include <dns/dnstap.h>
 #include <dns/fixedname.h>
@@ -178,6 +179,7 @@ struct dns_view {
        dns_dns64list_t                 dns64;
        unsigned int                    dns64cnt;
        dns_rpz_zones_t                 *rpzs;
+       dns_catz_zones_t                *catzs;
        dns_dlzdblist_t                 dlz_searched;
        dns_dlzdblist_t                 dlz_unsearched;
        isc_uint32_t                    fail_ttl;
index 8837ea655e3ee01dc6280c7204cdab57c2c8da40..b992047d028f21e21a4669dea77959d89d623859 100644 (file)
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 #ifndef DNS_ZONE_H
 #define DNS_ZONE_H 1
 
@@ -32,6 +30,7 @@
 #include <isc/lang.h>
 #include <isc/rwlock.h>
 
+#include <dns/catz.h>
 #include <dns/master.h>
 #include <dns/masterdump.h>
 #include <dns/rdatastruct.h>
@@ -2393,6 +2392,30 @@ dns_zone_rpz_enable_db(dns_zone_t *zone, dns_db_t *db);
 dns_rpz_num_t
 dns_zone_get_rpz_num(dns_zone_t *zone);
 
+void
+dns_zone_catz_enable(dns_zone_t *zone, dns_catz_zones_t *catzs);
+/*%<
+ * Enable zone as catalog zone.
+ *
+ * Requires:
+ *
+ * \li 'zone' is a valid zone object
+ * \li 'catzs' is not NULL
+ * \li prior to calling, zone->catzs is NULL or is equal to 'catzs'
+ */
+
+void
+dns_zone_catz_enable_db(dns_zone_t *zone, dns_db_t *db);
+/*%<
+ * If 'zone' is a catalog zone, then set up a notify-on-update trigger
+ * in its database. (If not a catalog zone, this function has no effect.)
+ *
+ * Requires:
+ *
+ * \li 'zone' is a valid zone object
+ * \li 'db' is not NULL
+ */
+
 void
 dns_zone_setstatlevel(dns_zone_t *zone, dns_zonestat_level_t level);
 
index f91d7e8dbe0f97308c89014682c5dddd428d42c8..6e5868ce711051a98865d78a638670655a6bf7f4 100644 (file)
@@ -90,7 +90,7 @@ dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone);
  */
 
 isc_result_t
-dns_zt_find(dns_zt_t *zt, dns_name_t *name, unsigned int options,
+dns_zt_find(dns_zt_t *zt, const dns_name_t *name, unsigned int options,
            dns_name_t *foundname, dns_zone_t **zone);
 /*%<
  * Find the best match for 'name' in 'zt'.  If foundname is non NULL
diff --git a/lib/dns/ipkeylist.c b/lib/dns/ipkeylist.c
new file mode 100644 (file)
index 0000000..a3bef8a
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016  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.
+ */
+
+#include <isc/mem.h>
+#include <isc/sockaddr.h>
+#include <isc/util.h>
+
+#include <dns/ipkeylist.h>
+#include <dns/name.h>
+
+void
+dns_ipkeylist_clear(isc_mem_t *mctx, dns_ipkeylist_t *ipkl) {
+       isc_uint32_t i;
+
+       REQUIRE(ipkl != NULL);
+       REQUIRE(ipkl->addrs != NULL);
+       REQUIRE(ipkl->keys != NULL);
+
+       if (ipkl->count == 0)
+               return;
+
+       isc_mem_put(mctx, ipkl->addrs, ipkl->count * sizeof(isc_sockaddr_t));
+       if (ipkl->dscps != NULL)
+               isc_mem_put(mctx, ipkl->dscps,
+                           ipkl->count * sizeof(isc_dscp_t));
+       for (i = 0; i < ipkl->count; i++) {
+               if (ipkl->keys[i] == NULL)
+                       continue;
+               if (dns_name_dynamic(ipkl->keys[i]))
+                       dns_name_free(ipkl->keys[i], mctx);
+               isc_mem_put(mctx, ipkl->keys[i], sizeof(dns_name_t));
+       }
+       isc_mem_put(mctx, ipkl->keys, ipkl->count * sizeof(dns_name_t *));
+       ipkl->count = 0;
+       ipkl->addrs = NULL;
+       ipkl->dscps = NULL;
+       ipkl->keys = NULL;
+}
+
+void
+dns_ipkeylist_copy(isc_mem_t *mctx, const dns_ipkeylist_t *src,
+                  dns_ipkeylist_t *dst)
+{
+       isc_uint32_t i;
+
+       REQUIRE(dst != NULL);
+       REQUIRE(dst->count == 0 &&
+               dst->addrs == NULL && dst->keys == NULL && dst->dscps == NULL);
+
+       if (src->count == 0)
+               return;
+
+       dst->count = src->count;
+
+       dst->addrs = isc_mem_get(mctx,
+                                src->count * sizeof(isc_sockaddr_t));
+       memmove(dst->addrs, src->addrs,
+               src->count * sizeof(isc_sockaddr_t));
+
+       if (src->dscps != NULL) {
+               dst->dscps = isc_mem_get(mctx,
+                                        src->count * sizeof(isc_dscp_t));
+               memmove(dst->dscps, src->dscps,
+                       src->count * sizeof(isc_dscp_t));
+       }
+
+       if (src->keys != NULL) {
+               dst->keys = isc_mem_get(mctx, src->count * sizeof(dns_name_t *));
+               for (i = 0; i < src->count; i++) {
+                       if (src->keys[i] != NULL) {
+                               dst->keys[i] = isc_mem_get(mctx,
+                                                          sizeof(dns_name_t));
+                               dns_name_dup(src->keys[i], mctx, dst->keys[i]);
+                       } else {
+                               dst->keys[i] = NULL;
+                       }
+               }
+       }
+}
index dcb7ad6e6fa5887b0006affeb753c46131ab4eee..85345bcb0731377f1629fbc82e563e1a22d5ee9b 100644 (file)
@@ -1566,9 +1566,13 @@ dns_name_totext2(const dns_name_t *name, unsigned int options,
        if (nlen != 0 && trem == 0)
                return (ISC_R_NOSPACE);
 
-       if (!saw_root || omit_final_dot)
+       if (!saw_root || omit_final_dot) {
                trem++;
-
+               tdata--;
+       }
+       if (trem > 0) {
+               *tdata = 0;
+       }
        isc_buffer_add(target, tlen - trem);
 
 #ifdef ISC_PLATFORM_USETHREADS
index 8b6a6992163843638a483d2fd59572bd1c688375..92d4aca8851335358d07c5289b47aba3a996e0c7 100644 (file)
@@ -1457,7 +1457,7 @@ dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) {
  * Find the node for "name" in the tree of trees.
  */
 isc_result_t
-dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
+dns_rbt_findnode(dns_rbt_t *rbt, const dns_name_t *name, dns_name_t *foundname,
                 dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
                 unsigned int options, dns_rbtfindcallback_t callback,
                 void *callback_arg)
@@ -1988,7 +1988,7 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
  * Get the data pointer associated with 'name'.
  */
 isc_result_t
-dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
+dns_rbt_findname(dns_rbt_t *rbt, const dns_name_t *name, unsigned int options,
                 dns_name_t *foundname, void **data) {
        dns_rbtnode_t *node = NULL;
        isc_result_t result;
index cb98eeac900dd7e948eb21813c047cd13fb97098..e6954f51cdb0756a919552b8347f7158487518a4 100644 (file)
@@ -1159,6 +1159,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
        char buf[DNS_NAME_FORMATSIZE];
        dns_rbt_t **treep;
        isc_time_t start;
+       dns_dbonupdatelistener_t *listener, *listener_next;
 
        if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in)
                overmem((dns_db_t *)rbtdb, (isc_boolean_t)-1);
@@ -1317,6 +1318,16 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
                isc_file_munmap(rbtdb->mmap_location,
                                (size_t) rbtdb->mmap_size);
 
+       for (listener = ISC_LIST_HEAD(rbtdb->common.update_listeners);
+            listener != NULL;
+            listener = listener_next)
+       {
+               listener_next = ISC_LIST_NEXT(listener, link);
+               ISC_LIST_UNLINK(rbtdb->common.update_listeners, listener, link);
+               isc_mem_put(rbtdb->common.mctx, listener,
+                           sizeof(dns_dbonupdatelistener_t));
+       }
+
        isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb));
        isc_ondestroy_notify(&ondest, rbtdb);
 }
@@ -8166,6 +8177,8 @@ dns_rbtdb_create
        rbtdb->common.rdclass = rdclass;
        rbtdb->common.mctx = NULL;
 
+       ISC_LIST_INIT(rbtdb->common.update_listeners);
+
        result = RBTDB_INITLOCK(&rbtdb->lock);
        if (result != ISC_R_SUCCESS)
                goto cleanup_rbtdb;
index 402c89b63bfed65f321c5893978614cf552b7664..bbce16c16bbec5966dfab0cba98c7dee68344c02 100644 (file)
@@ -229,6 +229,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
        view->v6_aaaa = dns_aaaa_ok;
        view->aaaa_acl = NULL;
        view->rpzs = NULL;
+       view->catzs = NULL;
        dns_fixedname_init(&view->dlv_fixed);
        view->managed_keys = NULL;
        view->redirect = NULL;
@@ -379,6 +380,8 @@ destroy(dns_view_t *view) {
        dns_rrl_view_destroy(view);
        if (view->rpzs != NULL)
                dns_rpz_detach_rpzs(&view->rpzs);
+       if (view->catzs != NULL)
+               dns_catz_catzs_detach(&view->catzs);
        for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
             dlzdb != NULL;
             dlzdb = ISC_LIST_HEAD(view->dlz_searched)) {
@@ -578,6 +581,9 @@ view_flushanddetach(dns_view_t **viewp, isc_boolean_t flush) {
                        if (view->flush)
                                dns_zone_flush(rdzone);
                }
+               if (view->catzs != NULL) {
+                       dns_catz_catzs_detach(&view->catzs);
+               }
                done = all_done(view);
                UNLOCK(&view->lock);
 
index 4c8230e24aa2daf20b37a8ed064dcc67960f7175..03651e5fcafb0528d92a26814ae9c562df3318a2 100644 (file)
@@ -109,6 +109,28 @@ dns_cache_setcachesize
 dns_cache_setcleaninginterval
 dns_cache_setfilename
 dns_cache_updatestats
+dns_catz_catzs_attach
+dns_catz_catzs_detach
+dns_catz_catzs_set_view
+dns_catz_dbupdate_callback
+dns_catz_entry_attach
+dns_catz_entry_cmp
+dns_catz_entry_detach
+dns_catz_entry_getname
+dns_catz_entry_validate
+dns_catz_get_iterator
+dns_catz_get_zone
+dns_catz_options_clear
+dns_catz_options_init
+dns_catz_postreconfig
+dns_catz_prereconfig
+dns_catz_update_from_db
+dns_catz_update_taskaction
+dns_catz_zone_attach
+dns_catz_zone_detach
+dns_catz_zone_getdefoptions
+dns_catz_zone_getname
+dns_catz_zones_merge
 dns_cert_fromtext
 dns_cert_totext
 dns_client_addtrustedkey
@@ -209,6 +231,8 @@ dns_db_settask
 dns_db_subtractrdataset
 dns_db_transfernode
 dns_db_unregister
+dns_db_updatenotify_register
+dns_db_updatenotify_unregister
 dns_dbiterator_current
 dns_dbiterator_destroy
 dns_dbiterator_first
index 63fd960edf901b933dc4ee942a9c337e09de1bbf..f49d1d8a135219b4ee336c10db86ba5a394414b0 100644 (file)
@@ -30,6 +30,7 @@
 #include <isc/util.h>
 
 #include <dns/callbacks.h>
+#include <dns/catz.h>
 #include <dns/db.h>
 #include <dns/diff.h>
 #include <dns/events.h>
@@ -279,8 +280,10 @@ axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) {
                               xfr->rdclass,
                               0, NULL, /* XXX guess */
                               dbp);
-       if (result == ISC_R_SUCCESS)
+       if (result == ISC_R_SUCCESS) {
                dns_zone_rpz_enable_db(xfr->zone, *dbp);
+               dns_zone_catz_enable_db(xfr->zone, *dbp);
+       }
        return (result);
 }
 
index b09df67e62b98eaadb9f56914ef6d7be66f5bc35..74ae734f8e2a81ae706aad1f885bd434a33ceb31 100644 (file)
@@ -43,6 +43,7 @@
 #include <dns/acl.h>
 #include <dns/adb.h>
 #include <dns/callbacks.h>
+#include <dns/catz.h>
 #include <dns/db.h>
 #include <dns/dbiterator.h>
 #include <dns/dlz.h>
@@ -380,6 +381,11 @@ struct dns_zone {
        dns_rpz_zones_t         *rpzs;
        dns_rpz_num_t           rpz_num;
 
+       /*%
+        * catalog zone data
+        */
+       dns_catz_zones_t                *catzs;
+
        /*%
         * Serial number update method.
         */
@@ -1036,6 +1042,9 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
        zone->automatic = ISC_FALSE;
        zone->rpzs = NULL;
        zone->rpz_num = DNS_RPZ_INVALID_NUM;
+
+       zone->catzs = NULL;
+
        ISC_LIST_INIT(zone->forwards);
        zone->raw = NULL;
        zone->secure = NULL;
@@ -1170,6 +1179,9 @@ zone_free(dns_zone_t *zone) {
                dns_rpz_detach_rpzs(&zone->rpzs);
                zone->rpz_num = DNS_RPZ_INVALID_NUM;
        }
+       if (zone->catzs != NULL) {
+               dns_catz_catzs_detach(&zone->catzs);
+       }
        zone_freedbargs(zone);
        RUNTIME_CHECK(dns_zone_setmasterswithkeys(zone, NULL, NULL, 0)
                      == ISC_R_SUCCESS);
@@ -1748,6 +1760,34 @@ dns_zone_rpz_enable_db(dns_zone_t *zone, dns_db_t *db) {
        }
 }
 
+void
+dns_zone_catz_enable(dns_zone_t *zone, dns_catz_zones_t *catzs) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+       REQUIRE(catzs != NULL);
+
+       LOCK_ZONE(zone);
+       INSIST(zone->catzs == NULL || zone->catzs == catzs);
+       dns_catz_catzs_set_view(catzs, zone->view);
+       if (zone->catzs == NULL)
+               dns_catz_catzs_attach(catzs, &zone->catzs);
+       UNLOCK_ZONE(zone);
+}
+
+/*
+ * If a zone is a catalog zone, attach it to update notification in database.
+ */
+void
+dns_zone_catz_enable_db(dns_zone_t *zone, dns_db_t *db) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+       REQUIRE(db != NULL);
+
+       if (zone->catzs != NULL) {
+               dns_db_updatenotify_register(db, dns_catz_dbupdate_callback,
+                                            zone->catzs);
+       }
+}
+
+
 static isc_boolean_t
 zone_touched(dns_zone_t *zone) {
        isc_result_t result;
@@ -2344,6 +2384,7 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) {
        unsigned int options;
 
        dns_zone_rpz_enable_db(zone, db);
+       dns_zone_catz_enable_db(zone, db);
        options = get_master_options(zone);
        if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MANYERRORS))
                options |= DNS_MASTER_MANYERRORS;
index ffc76bbeb5ff9ed676e6dd54ae0ffc6bd1c06dc2..9079d367f70001a0c32dc80af8566216fcbbe8d6 100644 (file)
@@ -155,7 +155,7 @@ dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
 }
 
 isc_result_t
-dns_zt_find(dns_zt_t *zt, dns_name_t *name, unsigned int options,
+dns_zt_find(dns_zt_t *zt, const dns_name_t *name, unsigned int options,
            dns_name_t *foundname, dns_zone_t **zonep)
 {
        isc_result_t result;
index d3a5de82992f6009246df8eff40bc9d08c6dae34..26a1954545536c7df388f814aea108491effded4 100644 (file)
@@ -57,8 +57,8 @@ OBJS =                @ISC_EXTRA_OBJS@ @ISC_PK11_O@ @ISC_PK11_RESULT_O@ \
                aes.@O@ assertions.@O@ backtrace.@O@ base32.@O@ base64.@O@ \
                bind9.@O@ buffer.@O@ bufferlist.@O@ \
                commandline.@O@ counter.@O@ crc64.@O@ error.@O@ event.@O@ \
-               hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \
-               httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \
+               hash.@O@ ht.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ \
+               hmacsha.@O@ httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \
                lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \
                md5.@O@ mem.@O@ mutexblock.@O@ \
                netaddr.@O@ netscope.@O@ pool.@O@ ondestroy.@O@ \
@@ -77,8 +77,8 @@ CHACHASRCS =  chacha_private.h
 SRCS =         @ISC_EXTRA_SRCS@ @ISC_PK11_C@ @ISC_PK11_RESULT_C@ \
                aes.c assertions.c backtrace.c base32.c base64.c bind9.c \
                buffer.c bufferlist.c commandline.c counter.c crc64.c \
-               error.c event.c heap.c hex.c hmacmd5.c hmacsha.c \
-               httpd.c inet_aton.c iterated_hash.c \
+               error.c event.c hash.c ht.c heap.c hex.c hmacmd5.c \
+               hmacsha.c httpd.c inet_aton.c iterated_hash.c \
                lex.c lfsr.c lib.c log.c \
                md5.c mem.c mutexblock.c \
                netaddr.c netscope.c pool.c ondestroy.c \
index b23601c387c10d0dc88a1e9c52db4eda1c548228..2cf128f9a19383d9a4733ee106f2634345c0aa9f 100644 (file)
@@ -60,6 +60,7 @@ isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) {
         */
        REQUIRE(b->length <= length);
        REQUIRE(base != NULL);
+       REQUIRE(b->autore = ISC_FALSE);
 
        (void)memmove(base, b->base, b->length);
        b->base = base;
@@ -79,6 +80,13 @@ isc__buffer_invalidate(isc_buffer_t *b) {
        ISC__BUFFER_INVALIDATE(b);
 }
 
+void
+isc_buffer_setautorealloc(isc_buffer_t *b, isc_boolean_t enable) {
+       REQUIRE(ISC_BUFFER_VALID(b));
+       REQUIRE(b->mctx != NULL);
+       b->autore = enable;
+}
+
 void
 isc__buffer_region(isc_buffer_t *b, isc_region_t *r) {
        /*
@@ -279,8 +287,13 @@ isc_buffer_getuint8(isc_buffer_t *b) {
 
 void
 isc__buffer_putuint8(isc_buffer_t *b, isc_uint8_t val) {
+       isc_result_t result;
        REQUIRE(ISC_BUFFER_VALID(b));
-       REQUIRE(b->used + 1 <= b->length);
+       if (b->autore) {
+               result = isc_buffer_reserve(&b, 1);
+               REQUIRE(result == ISC_R_SUCCESS);
+       }
+       REQUIRE(isc_buffer_availablelength(b) >= 1);
 
        ISC__BUFFER_PUTUINT8(b, val);
 }
@@ -308,16 +321,26 @@ isc_buffer_getuint16(isc_buffer_t *b) {
 
 void
 isc__buffer_putuint16(isc_buffer_t *b, isc_uint16_t val) {
+       isc_result_t result;
        REQUIRE(ISC_BUFFER_VALID(b));
-       REQUIRE(b->used + 2 <= b->length);
+       if (b->autore) {
+               result = isc_buffer_reserve(&b, 2);
+               REQUIRE(result == ISC_R_SUCCESS);
+       }
+       REQUIRE(isc_buffer_availablelength(b) >= 2);
 
        ISC__BUFFER_PUTUINT16(b, val);
 }
 
 void
 isc__buffer_putuint24(isc_buffer_t *b, isc_uint32_t val) {
+       isc_result_t result;
        REQUIRE(ISC_BUFFER_VALID(b));
-       REQUIRE(b->used + 3 <= b->length);
+       if (b->autore) {
+               result = isc_buffer_reserve(&b, 3);
+               REQUIRE(result == ISC_R_SUCCESS);
+       }
+       REQUIRE(isc_buffer_availablelength(b) >= 3);
 
        ISC__BUFFER_PUTUINT24(b, val);
 }
@@ -347,8 +370,13 @@ isc_buffer_getuint32(isc_buffer_t *b) {
 
 void
 isc__buffer_putuint32(isc_buffer_t *b, isc_uint32_t val) {
+       isc_result_t result;
        REQUIRE(ISC_BUFFER_VALID(b));
-       REQUIRE(b->used + 4 <= b->length);
+       if (b->autore == ISC_TRUE) {
+               result = isc_buffer_reserve(&b, 4);
+               REQUIRE(result == ISC_R_SUCCESS);
+       }
+       REQUIRE(isc_buffer_availablelength(b) >= 4);
 
        ISC__BUFFER_PUTUINT32(b, val);
 }
@@ -380,11 +408,16 @@ isc_buffer_getuint48(isc_buffer_t *b) {
 
 void
 isc__buffer_putuint48(isc_buffer_t *b, isc_uint64_t val) {
+       isc_result_t result;
        isc_uint16_t valhi;
        isc_uint32_t vallo;
 
        REQUIRE(ISC_BUFFER_VALID(b));
-       REQUIRE(b->used + 6 <= b->length);
+       if (b->autore == ISC_TRUE) {
+               result = isc_buffer_reserve(&b, 6);
+               REQUIRE(result == ISC_R_SUCCESS);
+       }
+       REQUIRE(isc_buffer_availablelength(b) >= 6);
 
        valhi = (isc_uint16_t)(val >> 32);
        vallo = (isc_uint32_t)(val & 0xFFFFFFFF);
@@ -396,8 +429,13 @@ void
 isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base,
                   unsigned int length)
 {
+       isc_result_t result;
        REQUIRE(ISC_BUFFER_VALID(b));
-       REQUIRE(b->used + length <= b->length);
+       if (b->autore == ISC_TRUE) {
+               result = isc_buffer_reserve(&b, length);
+               REQUIRE(result == ISC_R_SUCCESS);
+       }
+       REQUIRE(isc_buffer_availablelength(b) >= length);
 
        ISC__BUFFER_PUTMEM(b, base, length);
 }
@@ -406,6 +444,7 @@ void
 isc__buffer_putstr(isc_buffer_t *b, const char *source) {
        unsigned int l;
        unsigned char *cp;
+       isc_result_t result;
 
        REQUIRE(ISC_BUFFER_VALID(b));
        REQUIRE(source != NULL);
@@ -414,8 +453,11 @@ isc__buffer_putstr(isc_buffer_t *b, const char *source) {
         * Do not use ISC__BUFFER_PUTSTR(), so strlen is only done once.
         */
        l = strlen(source);
-
-       REQUIRE(l <= isc_buffer_availablelength(b));
+       if (b->autore == ISC_TRUE) {
+               result = isc_buffer_reserve(&b, l);
+               REQUIRE(result == ISC_R_SUCCESS);
+       }
+       REQUIRE(isc_buffer_availablelength(b) >= l);
 
        cp = isc_buffer_used(b);
        memmove(cp, source, l);
@@ -448,16 +490,21 @@ isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer,
                    unsigned int length)
 {
        isc_buffer_t *dbuf;
-
+       unsigned char * bdata;
        REQUIRE(dynbuffer != NULL);
        REQUIRE(*dynbuffer == NULL);
 
-       dbuf = isc_mem_get(mctx, length + sizeof(isc_buffer_t));
+       dbuf = isc_mem_get(mctx, sizeof(isc_buffer_t));
        if (dbuf == NULL)
                return (ISC_R_NOMEMORY);
 
-       isc_buffer_init(dbuf, ((unsigned char *)dbuf) + sizeof(isc_buffer_t),
-                       length);
+       bdata = isc_mem_get(mctx, length);
+       if (bdata == NULL) {
+               isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t));
+               return (ISC_R_NOMEMORY);
+       }
+
+       isc_buffer_init(dbuf, bdata, length);
        dbuf->mctx = mctx;
 
        ENSURE(ISC_BUFFER_VALID(dbuf));
@@ -469,7 +516,7 @@ isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer,
 
 isc_result_t
 isc_buffer_reallocate(isc_buffer_t **dynbuffer, unsigned int length) {
-       isc_buffer_t *dbuf;
+       unsigned char *bdata;
 
        REQUIRE(dynbuffer != NULL);
        REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
@@ -483,18 +530,16 @@ isc_buffer_reallocate(isc_buffer_t **dynbuffer, unsigned int length) {
         * it doesn't remap pages, but does ordinary copy. So is
         * isc_mem_reallocate(), which has additional issues.
         */
-       dbuf = isc_mem_get((*dynbuffer)->mctx, length + sizeof(isc_buffer_t));
-       if (dbuf == NULL)
+       bdata = isc_mem_get((*dynbuffer)->mctx, length);
+       if (bdata == NULL)
                return (ISC_R_NOMEMORY);
 
-       memmove(dbuf, *dynbuffer, (*dynbuffer)->length + sizeof(isc_buffer_t));
-       isc_mem_put(dbuf->mctx, *dynbuffer,
-                   (*dynbuffer)->length + sizeof(isc_buffer_t));
+       memmove(bdata, (*dynbuffer)->base, (*dynbuffer)->length);
+       isc_mem_put((*dynbuffer)->mctx, (*dynbuffer)->base,
+                   (*dynbuffer)->length);
 
-       dbuf->base = ((unsigned char *)dbuf) + sizeof(isc_buffer_t);
-       dbuf->length = length;
-
-       *dynbuffer = dbuf;
+       (*dynbuffer)->base = bdata;
+       (*dynbuffer)->length = length;
 
        return (ISC_R_SUCCESS);
 }
@@ -530,7 +575,6 @@ isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) {
 
 void
 isc_buffer_free(isc_buffer_t **dynbuffer) {
-       unsigned int real_length;
        isc_buffer_t *dbuf;
        isc_mem_t *mctx;
 
@@ -540,11 +584,10 @@ isc_buffer_free(isc_buffer_t **dynbuffer) {
 
        dbuf = *dynbuffer;
        *dynbuffer = NULL;      /* destroy external reference */
-
-       real_length = dbuf->length + sizeof(isc_buffer_t);
        mctx = dbuf->mctx;
        dbuf->mctx = NULL;
-       isc_buffer_invalidate(dbuf);
 
-       isc_mem_put(mctx, dbuf, real_length);
+       isc_mem_put(mctx, dbuf->base, dbuf->length);
+       isc_buffer_invalidate(dbuf);
+       isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t));
 }
diff --git a/lib/isc/ht.c b/lib/isc/ht.c
new file mode 100644 (file)
index 0000000..6d710cb
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2015  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.
+ */
+
+#include <string.h>
+
+#include <isc/hash.h>
+#include <isc/ht.h>
+#include <isc/types.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+
+typedef struct isc_ht_node isc_ht_node_t;
+
+#define ISC_HT_MAGIC                   ISC_MAGIC('H', 'T', 'a', 'b')
+#define ISC_HT_VALID(ht)               ISC_MAGIC_VALID(ht, ISC_HT_MAGIC)
+
+struct isc_ht_node {
+       void *value;
+       isc_ht_node_t *next;
+       size_t keysize;
+       unsigned char key[];
+};
+
+struct isc_ht {
+       unsigned int magic;
+       isc_mem_t *mctx;
+       size_t size;
+       size_t mask;
+       unsigned int count;
+       isc_ht_node_t **table;
+};
+
+struct isc_ht_iter {
+       isc_ht_t *ht;
+       isc_uint32_t i;
+       isc_ht_node_t *cur;
+};
+
+isc_result_t
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits) {
+       isc_ht_t *ht = NULL;
+       isc_uint32_t i;
+
+       REQUIRE(htp != NULL && *htp == NULL);
+       REQUIRE(mctx != NULL);
+       REQUIRE(bits >= 1 && bits <= (sizeof(size_t)*8 - 1));
+
+       ht = isc_mem_get(mctx, sizeof(struct isc_ht));
+       if (ht == NULL) {
+               return (ISC_R_NOMEMORY);
+       }
+
+       ht->mctx = NULL;
+       isc_mem_attach(mctx, &ht->mctx);
+
+       ht->size = (1<<bits);
+       ht->mask = (1<<bits)-1;
+       ht->count = 0;
+
+       ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t*));
+       if (ht->table == NULL) {
+               isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht));
+               return (ISC_R_NOMEMORY);
+       }
+
+       for (i = 0; i < ht->size; i++) {
+               ht->table[i] = NULL;
+       }
+
+       ht->magic = ISC_HT_MAGIC;
+
+       *htp = ht;
+       return (ISC_R_SUCCESS);
+}
+
+void
+isc_ht_destroy(isc_ht_t **htp) {
+       isc_ht_t *ht;
+       isc_uint32_t i;
+
+       REQUIRE(htp != NULL);
+
+       ht = *htp;
+       REQUIRE(ISC_HT_VALID(ht));
+
+       ht->magic = 0;
+
+       for (i = 0; i < ht->size; i++) {
+               isc_ht_node_t *node = ht->table[i];
+               while (node != NULL) {
+                       isc_ht_node_t *next = node->next;
+                       ht->count--;
+                       isc_mem_put(ht->mctx, node,
+                                       sizeof(isc_ht_node_t) + node->keysize);
+                       node = next;
+               }
+       }
+
+       INSIST(ht->count == 0);
+
+       isc_mem_put(ht->mctx, ht->table, ht->size * sizeof(isc_ht_node_t*));
+       isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht));
+
+       *htp = NULL;
+}
+
+isc_result_t
+isc_ht_add(isc_ht_t *ht, const unsigned char *key,
+          isc_uint32_t keysize, void *value)
+{
+       isc_ht_node_t *node;
+       isc_uint32_t hash;
+
+       REQUIRE(ISC_HT_VALID(ht));
+       REQUIRE(key != NULL && keysize > 0);
+
+       hash = isc_hash_function(key, keysize, ISC_TRUE, NULL);
+       node = ht->table[hash & ht->mask];
+       while (node != NULL) {
+               if (keysize == node->keysize &&
+                   memcmp(key, node->key, keysize) == 0)
+               {
+                       return (ISC_R_EXISTS);
+               }
+               node = node->next;
+       }
+
+       node = isc_mem_get(ht->mctx, sizeof(isc_ht_node_t) + keysize);
+       if (node == NULL) {
+               return (ISC_R_NOMEMORY);
+       }
+
+       memmove(node->key, key, keysize);
+       node->keysize = keysize;
+       node->next = ht->table[hash & ht->mask];
+       node->value = value;
+
+       ht->count++;
+       ht->table[hash & ht->mask] = node;
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
+           isc_uint32_t keysize, void **valuep)
+{
+       isc_ht_node_t *node;
+       isc_uint32_t hash;
+
+       REQUIRE(ISC_HT_VALID(ht));
+       REQUIRE(key != NULL && keysize > 0);
+       REQUIRE(valuep != NULL);
+
+       hash = isc_hash_function(key, keysize, ISC_TRUE, NULL);
+       node = ht->table[hash & ht->mask];
+       while (node != NULL) {
+               if (keysize == node->keysize &&
+                   memcmp(key, node->key, keysize) == 0)
+               {
+                       *valuep = node->value;
+                       return (ISC_R_SUCCESS);
+               }
+               node = node->next;
+       }
+
+       return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize) {
+       isc_ht_node_t *node, *prev;
+       isc_uint32_t hash;
+
+       REQUIRE(ISC_HT_VALID(ht));
+       REQUIRE(key != NULL && keysize > 0);
+
+       prev = NULL;
+       hash = isc_hash_function(key, keysize, ISC_TRUE, NULL);
+       node = ht->table[hash & ht->mask];
+       while (node != NULL) {
+               if (keysize == node->keysize &&
+                   memcmp(key, node->key, keysize) == 0)
+               {
+                       if (prev == NULL) {
+                               ht->table[hash & ht->mask] = node->next;
+                       } else {
+                               prev->next = node->next;
+                       }
+                       isc_mem_put(ht->mctx, node,
+                                       sizeof(isc_ht_node_t) + node->keysize);
+                       ht->count--;
+
+                       return (ISC_R_SUCCESS);
+               }
+
+               prev = node;
+               node = node->next;
+       }
+       return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
+       isc_ht_iter_t *it;
+
+       REQUIRE(ISC_HT_VALID(ht));
+       REQUIRE(itp != NULL && *itp == NULL);
+
+       if (ht->count == 0)
+               return (ISC_R_NOMORE);
+
+       it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t));
+       if (it == NULL)
+               return (ISC_R_NOMEMORY);
+
+       it->ht = ht;
+       it->i = 0;
+       it->cur = NULL;
+
+       *itp = it;
+
+       return (ISC_R_SUCCESS);
+}
+
+void
+isc_ht_iter_destroy(isc_ht_iter_t **itp) {
+       isc_ht_iter_t *it;
+       isc_ht_t *ht;
+
+       REQUIRE(itp != NULL && *itp != NULL);
+
+       it = *itp;
+       ht = it->ht;
+       isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t));
+
+       *itp = NULL;
+}
+
+isc_result_t
+isc_ht_iter_first(isc_ht_iter_t *it) {
+       REQUIRE(it != NULL);
+
+       it->i = 0;
+       while (it->i < it->ht->size && it->ht->table[it->i] == NULL)
+               it->i++;
+
+       if(it->i == it->ht->size)
+               return (ISC_R_NOMORE);
+
+       it->cur = it->ht->table[it->i];
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_ht_iter_next(isc_ht_iter_t *it) {
+       REQUIRE(it != NULL);
+       REQUIRE(it->cur != NULL);
+
+       it->cur = it->cur->next;
+       if (it->cur == NULL) {
+               while (it->i < it->ht->size && it->ht->table[it->i] == NULL)
+                       it->i++;
+               if (it->i < it->ht->size)
+                       return (ISC_R_NOMORE);
+               it->cur = it->ht->table[it->i];
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+void
+isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) {
+       REQUIRE(it != NULL);
+       REQUIRE(it->cur != NULL);
+       *valuep = it->cur->value;
+}
+
+unsigned int
+isc_ht_count(isc_ht_t *ht) {
+       REQUIRE(ISC_HT_VALID(ht));
+
+       return(ht->count);
+}
+
+isc_result_t
+isc_ht_walk(isc_ht_t *ht, isc_ht_walkfn walkfn, void *udata) {
+       isc_uint32_t i;
+       isc_result_t res;
+
+       REQUIRE(ISC_HT_VALID(ht));
+
+       for (i = 0; i < ht->size; i++) {
+               isc_ht_node_t *cur = ht->table[i];
+               isc_ht_node_t *prev = NULL;
+               while (cur != NULL) {
+                       if (walkfn == NULL) {
+                               continue;
+                       }
+                       res = walkfn(udata, cur->key, cur->keysize, cur->value);
+                       if (res != ISC_R_SUCCESS && res != ISC_R_EXISTS) {
+                               return (res);
+                       }
+                       if (res == ISC_R_EXISTS) { /* remove this node */
+                               isc_ht_node_t *tmp = cur;
+                               cur = cur->next;
+                               if (prev == NULL) {
+                                       ht->table[i] = cur;
+                               } else {
+                                       prev->next = cur;
+                               }
+                               isc_mem_put(ht->mctx, tmp,
+                                       sizeof(isc_ht_node_t) + tmp->keysize);
+                               ht->count--;
+                       } else {
+                               prev = cur;
+                               cur = cur->next;
+                       }
+               }
+       }
+
+       return (ISC_R_SUCCESS);
+}
index a6193a53c24f864b084292ad0fbd482a8a9a3efc..275d7ec6644a0a9127284196f1d94668d985a6bd 100644 (file)
@@ -184,6 +184,8 @@ struct isc_buffer {
        ISC_LINK(isc_buffer_t)  link;
        /*! private internal elements */
        isc_mem_t              *mctx;
+       /* automatically realloc buffer at put* */
+       isc_boolean_t           autore;
 };
 
 /***
@@ -316,6 +318,16 @@ isc__buffer_invalidate(isc_buffer_t *b);
  *     calling isc_buffer_init() on it will cause an assertion failure.
  */
 
+void
+isc_buffer_setautorealloc(isc_buffer_t *b, isc_boolean_t enable);
+/*!<
+ * \brief Enable or disable autoreallocation on 'b'.
+ *
+ * Requires:
+ *\li  'b' is a valid dynamic buffer (b->mctx != NULL).
+ *
+ */
+
 void
 isc__buffer_region(isc_buffer_t *b, isc_region_t *r);
 /*!<
@@ -530,7 +542,8 @@ isc__buffer_putuint8(isc_buffer_t *b, isc_uint8_t val);
  * Requires:
  *\li  'b' is a valid buffer.
  *
- *\li  The length of the unused region of 'b' is at least 1.
+ *\li  The length of the unused region of 'b' is at least 1
+ *     or the buffer has autoreallocation enabled.
  *
  * Ensures:
  *\li  The used pointer in 'b' is advanced by 1.
@@ -546,7 +559,8 @@ isc_buffer_getuint16(isc_buffer_t *b);
  *
  *\li  'b' is a valid buffer.
  *
- *\li  The length of the available region of 'b' is at least 2.
+ *\li  The length of the available region of 'b' is at least 2
+ *     or the buffer has autoreallocation enabled.
  *
  * Ensures:
  *
@@ -566,7 +580,8 @@ isc__buffer_putuint16(isc_buffer_t *b, isc_uint16_t val);
  * Requires:
  *\li  'b' is a valid buffer.
  *
- *\li  The length of the unused region of 'b' is at least 2.
+ *\li  The length of the unused region of 'b' is at least 2
+ *     or the buffer has autoreallocation enabled.
  *
  * Ensures:
  *\li  The used pointer in 'b' is advanced by 2.
@@ -602,7 +617,8 @@ isc__buffer_putuint32(isc_buffer_t *b, isc_uint32_t val);
  * Requires:
  *\li  'b' is a valid buffer.
  *
- *\li  The length of the unused region of 'b' is at least 4.
+ *\li  The length of the unused region of 'b' is at least 4
+ *     or the buffer has autoreallocation enabled.
  *
  * Ensures:
  *\li  The used pointer in 'b' is advanced by 4.
@@ -638,7 +654,8 @@ isc__buffer_putuint48(isc_buffer_t *b, isc_uint64_t val);
  * Requires:
  *\li  'b' is a valid buffer.
  *
- *\li  The length of the unused region of 'b' is at least 6.
+ *\li  The length of the unused region of 'b' is at least 6
+ *     or the buffer has autoreallocation enabled.
  *
  * Ensures:
  *\li  The used pointer in 'b' is advanced by 6.
@@ -653,7 +670,8 @@ isc__buffer_putuint24(isc_buffer_t *b, isc_uint32_t val);
  * Requires:
  *\li  'b' is a valid buffer.
  *
- *     The length of the unused region of 'b' is at least 3.
+ *     The length of the unused region of 'b' is at least 3
+ *     or the buffer has autoreallocation enabled.
  *
  * Ensures:
  *\li  The used pointer in 'b' is advanced by 3.
@@ -666,7 +684,8 @@ isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base,
  * \brief Copy 'length' bytes of memory at 'base' into 'b'.
  *
  * Requires:
- *\li  'b' is a valid buffer.
+ *\li  'b' is a valid buffer, and it has at least 'length'
+ *     or the buffer has autoreallocation enabled.
  *
  *\li  'base' points to 'length' bytes of valid memory.
  *
@@ -682,7 +701,7 @@ isc__buffer_putstr(isc_buffer_t *b, const char *source);
  *
  *\li  'source' to be a valid NULL terminated string.
  *
- *\li  strlen(source) <= isc_buffer_available(b)
+ *\li  strlen(source) <= isc_buffer_available(b) || b->mctx != NULL
  */
 
 isc_result_t
@@ -738,6 +757,7 @@ ISC_LANG_ENDDECLS
                (_b)->mctx = NULL; \
                ISC_LINK_INIT(_b, link); \
                (_b)->magic = ISC_BUFFER_MAGIC; \
+               (_b)->autore = ISC_FALSE; \
        } while (0)
 
 #define ISC__BUFFER_INITNULL(_b) ISC__BUFFER_INIT(_b, NULL, 0)
index 571e74fd5c3460257a5c96f6396ca6c51c3711e4..d9b76bac2d1c6ab61ae985f92286d71b3c431be5 100644 (file)
@@ -19,6 +19,7 @@
 #define ISC_HASH_H 1
 
 #include <isc/types.h>
+#include <isc/util.h>
 
 /*****
  ***** Module Info
@@ -105,7 +106,8 @@ isc_hash_create(isc_mem_t *mctx, isc_entropy_t *entropy, size_t limit);
  */
 
 void
-isc_hash_ctxattach(isc_hash_t *hctx, isc_hash_t **hctxp);
+isc_hash_ctxattach(isc_hash_t *hctx, isc_hash_t **hctxp)
+       ISC_DEPRECATED;
 /*!<
  * \brief Attach to a hash object.
  *
@@ -113,7 +115,8 @@ isc_hash_ctxattach(isc_hash_t *hctx, isc_hash_t **hctxp);
  */
 
 void
-isc_hash_ctxdetach(isc_hash_t **hctxp);
+isc_hash_ctxdetach(isc_hash_t **hctxp)
+       ISC_DEPRECATED;
 /*!<
  * \brief Detach from a hash object.
  *
@@ -158,10 +161,12 @@ isc_hash_init(void);
 /*@{*/
 unsigned int
 isc_hash_ctxcalc(isc_hash_t *hctx, const unsigned char *key,
-                unsigned int keylen, isc_boolean_t case_sensitive);
+                unsigned int keylen, isc_boolean_t case_sensitive)
+       ISC_DEPRECATED;
 unsigned int
 isc_hash_calc(const unsigned char *key, unsigned int keylen,
-             isc_boolean_t case_sensitive);
+             isc_boolean_t case_sensitive)
+       ISC_DEPRECATED;
 /*!<
  * \brief Calculate a hash value.
  *
@@ -183,7 +188,8 @@ isc_hash_calc(const unsigned char *key, unsigned int keylen,
 /*@}*/
 
 void
-isc__hash_setvec(const isc_uint16_t *vec);
+isc__hash_setvec(const isc_uint16_t *vec)
+       ISC_DEPRECATED;
 
 /*!<
  * \brief Set the contents of the random vector used in hashing.
diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h
new file mode 100644 (file)
index 0000000..2d9d60c
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015  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.
+ */
+
+/* ! \file */
+
+#ifndef ISC_HT_H
+#define ISC_HT_H 1
+
+#include <string.h>
+#include <isc/types.h>
+#include <isc/result.h>
+
+typedef struct isc_ht isc_ht_t;
+typedef struct isc_ht_iter isc_ht_iter_t;
+
+/*%
+ * Initialize hashtable at *htp, using memory context and size of (1<<bits)
+ *
+ * Requires:
+ *\li  htp is not NULL
+ *\li  *htp is NULL
+ *\li  mctx is a valid memory context
+ *\li  bits >=1 && bits <=32
+ *
+ * Returns:
+ *\li  #ISC_R_NOMEMORY         -- not enough memory to create pool
+ *\li  #ISC_R_SUCCESS          -- all is well.
+ */
+isc_result_t
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits);
+
+/*%
+ * Destroy hashtable, freeing everything
+ *
+ * Requires:
+ * \li *htp is valid hashtable
+ */
+void
+isc_ht_destroy(isc_ht_t **htp);
+
+/*%
+ * Add a node to hashtable, pointed by binary key 'key' of size 'keysize';
+ * set its value to 'value'
+ *
+ * Requires:
+ *\li  ht is a valid hashtable
+ *
+ * Returns:
+ *\li  #ISC_R_NOMEMORY         -- not enough memory to create pool
+ *\li  #ISC_R_EXISTS           -- node of the same key already exists
+ *\li  #ISC_R_SUCCESS          -- all is well.
+ */
+isc_result_t
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize,
+                  void *value);
+
+/*%
+ * Find a node matching 'key'/'keysize' in hashtable 'ht';
+ * if found, set 'value' to its value
+ *
+ * Requires:
+ * \li 'ht' is a valid hashtable
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS          -- success
+ * \li #ISC_R_NOTFOUND         -- key not found
+ */
+isc_result_t
+isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
+           isc_uint32_t keysize, void **valuep);
+
+/*%
+ * Delete node from hashtable
+ * Requires:
+ *\li  ht is a valid hashtable
+ *
+ * Returns:
+ *\li  #ISC_R_NOTFOUND         -- key not found
+ *\li  #ISC_R_SUCCESS          -- all is well
+ */
+isc_result_t
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize);
+
+
+typedef isc_result_t (*isc_ht_walkfn)(void *udata, const unsigned char *key,
+               isc_uint32_t keysize, void *data);
+
+/*%
+ * Create an iterator for the hashtable; point '*itp' to it.
+ */
+isc_result_t
+isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp);
+
+/*%
+ * Destroy the iterator '*itp', set it to NULL
+ */
+void
+isc_ht_iter_destroy(isc_ht_iter_t **itp);
+
+/*%
+ * Set an iterator to the first entry.
+ *
+ * Returns:
+ * \li         #ISC_R_SUCCESS  -- success
+ * \li #ISC_R_NOMORE   -- no data in the hashtable
+ */
+isc_result_t
+isc_ht_iter_first(isc_ht_iter_t *it);
+
+/*%
+ * Set an iterator to the next entry.
+ *
+ * Returns:
+ * \li         #ISC_R_SUCCESS  -- success
+ * \li #ISC_R_NOMORE   -- end of hashtable reached
+ */
+isc_result_t
+isc_ht_iter_next(isc_ht_iter_t *it);
+
+/*%
+ * Set 'value' to the current value under the iterator
+ */
+void
+isc_ht_iter_current(isc_ht_iter_t *it, void **valuep);
+
+/*%
+ * Walks the hashtable, calling 'walkfn' on each node
+ *
+ * \li If 'walkfn' returns ISC_R_SUCCESS, walk is continued
+ * \li If 'walkfn' returns ISC_R_EXISTS, walk is continued but the
+ *     node is removed
+ * \li If 'walkfn' returns anything else, walk is aborted and function returns
+ *     this value
+ *
+ * Requires:
+ * \li 'ht' is a valid hashtable
+ * \li  'walkfn' is not NULL
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS          -- all is well
+ * \li Any other, as returned by 'walkfn'
+ */
+isc_result_t
+isc_ht_walk(isc_ht_t *ht, isc_ht_walkfn walkfn, void *udata);
+
+/*%
+ * Returns the number of items in the hashtable.
+ *
+ * Requires:
+ *\li  'ht' is a valid hashtable
+ */
+unsigned int
+isc_ht_count(isc_ht_t *ht);
+#endif
index fc2e1882641af953dff9c9c51411a5503a1078d0..0c7733b4a97d6281de0d14b0ae1e771fe5be0d46 100644 (file)
  */
 #define TIME_NOW(tp)   RUNTIME_CHECK(isc_time_now((tp)) == ISC_R_SUCCESS)
 
+/*%
+ * Misc.
+ */
+#ifdef __GNUC__
+#define ISC_DEPRECATED                 __attribute__((deprecated))
+#else
+#define ISC_DEPRECATED                 /* none */
+#endif /* __GNUC __ */
+
 #endif /* ISC_UTIL_H */
index e190d452d14124432043cb0c4528af0c2a40e4eb..ee77f9caa21a14d99b3f6dc712194c2836cb7faa 100644 (file)
@@ -675,7 +675,6 @@ mem_getunlocked(isc__mem_t *ctx, size_t size) {
                new_size = size;
                goto done;
        }
-
        /*
         * If there are no blocks in the free list for this size, get a chunk
         * of memory and then break it up into "new_size"-sized blocks, adding
@@ -687,9 +686,11 @@ mem_getunlocked(isc__mem_t *ctx, size_t size) {
        /*
         * The free list uses the "rounded-up" size "new_size".
         */
+
        ret = ctx->freelists[new_size];
        ctx->freelists[new_size] = ctx->freelists[new_size]->next;
 
+
        /*
         * The stats[] uses the _actual_ "size" requested by the
         * caller, with the caveat (in the code above) that "size" >= the
index ac049a7552840e493713164738fa6fea54bc2af0..26b259a8849fa1f62b3a857a397a2b9dd09e9a0b 100644 (file)
@@ -1723,7 +1723,10 @@ isc__task_beginexclusive(isc_task_t *task0) {
        isc__taskmgr_t *manager = task->manager;
 
        REQUIRE(task->state == task_state_running);
-       /* XXX: Require task == manager->excl? */
+/*
+ *  TODO REQUIRE(task == task->manager->excl);
+ *  it should be here, it fails on shutdown server->task
+ */
 
        LOCK(&manager->lock);
        if (manager->exclusive_requested) {
index b4183665549d07be5c0ce77d3ec4398c4b7a975a..f918c29efb5a4b937ad099fad324cdd90d859ad8 100644 (file)
@@ -41,7 +41,7 @@ SRCS =                isctest.c taskpool_test.c socket_test.c hash_test.c \
                parse_test.c pool_test.c print_test.c regex_test.c \
                socket_test.c safe_test.c time_test.c aes_test.c \
                file_test.c buffer_test.c counter_test.c mem_test.c \
-               result_test.c
+               result_test.c ht_test.c
 
 SUBDIRS =
 TARGETS =      taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \
@@ -51,7 +51,7 @@ TARGETS =     taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \
                print_test@EXEEXT@ regex_test@EXEEXT@ socket_test@EXEEXT@ \
                safe_test@EXEEXT@ time_test@EXEEXT@ aes_test@EXEEXT@ \
                file_test@EXEEXT@ buffer_test@EXEEXT@ counter_test@EXEEXT@ \
-               mem_test@EXEEXT@ result_test@EXEEXT@
+               mem_test@EXEEXT@ result_test@EXEEXT@ ht_test@EXEEXT@
 
 @BIND9_MAKE_RULES@
 
@@ -144,6 +144,10 @@ result_test@EXEEXT@: result_test.@O@ ${ISCDEPLIBS}
        ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
                        result_test.@O@ ${ISCLIBS} ${LIBS}
 
+ht_test@EXEEXT@: ht_test.@O@ ${ISCDEPLIBS}
+       ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+                       ht_test.@O@ ${ISCLIBS} ${LIBS}
+
 unit::
        sh ${top_srcdir}/unit/unittest.sh
 
index 51a9b4cec31b80da101ef00a78925b1395603ebd..af0b76a62855bf1bc6e2f7b2101bb287c7f3cd72 100644 (file)
@@ -135,11 +135,74 @@ ATF_TC_BODY(isc_buffer_reallocate, tc) {
        isc_test_end();
 }
 
+ATF_TC(isc_buffer_dynamic);
+ATF_TC_HEAD(isc_buffer_dynamic, tc) {
+       atf_tc_set_md_var(tc, "descr", "dynamic buffer automatic reallocation");
+}
+
+ATF_TC_BODY(isc_buffer_dynamic, tc) {
+       isc_result_t result;
+       isc_buffer_t *b;
+       size_t last_length = 10;
+       int i;
+
+       result = isc_test_begin(NULL, ISC_TRUE);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       b = NULL;
+       result = isc_buffer_allocate(mctx, &b, last_length);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_REQUIRE(b != NULL);
+       ATF_CHECK_EQ(b->length, last_length);
+
+       isc_buffer_setautorealloc(b, ISC_TRUE);
+
+       isc_buffer_putuint8(b, 1);
+
+       for (i = 0; i < 1000; i++) {
+               isc_buffer_putstr(b, "thisisa24charslongstring");
+       }
+       ATF_CHECK(b->length-last_length >= 1000*24);
+       last_length+=1000*24;
+
+       for (i = 0; i < 10000; i++) {
+               isc_buffer_putuint8(b, 1);
+       }
+
+       ATF_CHECK(b->length-last_length >= 10000*1);
+       last_length += 10000*1;
+
+       for (i = 0; i < 10000; i++) {
+               isc_buffer_putuint16(b, 1);
+       }
+
+       ATF_CHECK(b->length-last_length >= 10000*2);
+
+       last_length += 10000*2;
+       for (i = 0; i < 10000; i++) {
+               isc_buffer_putuint24(b, 1);
+       }
+       ATF_CHECK(b->length-last_length >= 10000*3);
+
+       last_length+=10000*3;
+
+       for (i = 0; i < 10000; i++) {
+               isc_buffer_putuint32(b, 1);
+       }
+       ATF_CHECK(b->length-last_length >= 10000*4);
+
+
+       isc_buffer_free(&b);
+
+       isc_test_end();
+}
+
 /*
  * Main
  */
 ATF_TP_ADD_TCS(tp) {
        ATF_TP_ADD_TC(tp, isc_buffer_reserve);
        ATF_TP_ADD_TC(tp, isc_buffer_reallocate);
+       ATF_TP_ADD_TC(tp, isc_buffer_dynamic);
        return (atf_no_error());
 }
diff --git a/lib/isc/tests/ht_test.c b/lib/isc/tests/ht_test.c
new file mode 100644 (file)
index 0000000..cc5fe3e
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016  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.
+ */
+
+/* ! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <isc/hash.h>
+#include <isc/ht.h>
+#include <isc/mem.h>
+#include <isc/util.h>
+
+static void *
+default_memalloc(void *arg, size_t size) {
+       UNUSED(arg);
+       if (size == 0U)
+               size = 1;
+       return (malloc(size));
+}
+
+static void
+default_memfree(void *arg, void *ptr) {
+       UNUSED(arg);
+       free(ptr);
+}
+
+
+static void test_ht_full(int bits, int count) {
+       isc_ht_t *ht = NULL;
+       isc_result_t result;
+       isc_mem_t *mctx = NULL;
+       isc_int64_t i;
+
+       result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
+                                 NULL, &mctx, 0);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       isc_ht_init(&ht, mctx, bits);
+       for (i = 1; i < count; i++) {
+               /*
+                * Note that the string we're snprintfing is always > 16 bytes
+                * so we are always filling the key.
+                */
+               unsigned char key[16];
+               snprintf((char *)key, 16, "%lld key of a raw hashtable!!", i);
+               result = isc_ht_add(ht, key, 16, (void *) i);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       }
+
+       for (i = 1; i < count; i++) {
+               unsigned char key[16];
+               void *f = NULL;
+               snprintf((char *)key, 16, "%lld key of a raw hashtable!!", i);
+               result = isc_ht_find(ht, key, 16, &f);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+               ATF_REQUIRE_EQ(i, (isc_int64_t) f);
+       }
+
+       for (i = 1; i < count; i++) {
+               unsigned char key[16];
+               snprintf((char *)key, 16, "%lld key of a raw hashtable!!", i);
+               result = isc_ht_add(ht, key, 16, (void *) i);
+               ATF_REQUIRE_EQ(result, ISC_R_EXISTS);
+       }
+
+       for (i = 1; i < count; i++) {
+               char key[64];
+               snprintf((char *)key, 64, "%lld key of a str hashtable!!", i);
+               result = isc_ht_add(ht, (const unsigned char *) key,
+                                   strlen(key), (void *) i);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       }
+
+       for (i = 1; i < count; i++) {
+               unsigned char key[16];
+               void *f = NULL;
+               snprintf((char *)key, 16, "%lld KEY of a raw hashtable!!", i);
+               result = isc_ht_find(ht, key, 16, &f);
+               ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+               ATF_REQUIRE_EQ(f, NULL);
+       }
+
+       for (i = 1; i < count; i++) {
+               char key[64];
+               void *f = NULL;
+               snprintf((char *)key, 64, "%lld key of a str hashtable!!", i);
+               result = isc_ht_find(ht, (const unsigned char *) key,
+                                    strlen(key), &f);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+               ATF_REQUIRE_EQ(f, (void *) i);
+       }
+
+       for (i = 1; i < count; i++) {
+               unsigned char key[16];
+               void *f = NULL;
+               snprintf((char *)key, 16, "%lld key of a raw hashtable!!", i);
+               result = isc_ht_delete(ht, key, 16);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+               result = isc_ht_find(ht, key, 16, &f);
+               ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+               ATF_REQUIRE_EQ(f, NULL);
+       }
+
+       for (i = 1; i < count; i++) {
+               unsigned char key[16];
+               snprintf((char *)key, 16, "%lld KEY of a raw hashtable!!", i);
+               result = isc_ht_add(ht, key, 16, (void *) i);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       }
+
+       for (i = 1; i < count; i++) {
+               char key[64];
+               void *f = NULL;
+               snprintf((char *)key, 64, "%lld key of a str hashtable!!", i);
+               result = isc_ht_delete(ht, (const unsigned char *) key,
+                                      strlen(key));
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+               result = isc_ht_find(ht, (const unsigned char *) key,
+                                    strlen(key), &f);
+               ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+               ATF_REQUIRE_EQ(f, NULL);
+       }
+
+
+       for (i = 1; i < count; i++) {
+               unsigned char key[16];
+               void *f = NULL;
+               snprintf((char *)key, 16, "%lld KEY of a raw hashtable!!", i);
+               result = isc_ht_find(ht, key, 16, &f);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+               ATF_REQUIRE_EQ(i, (isc_int64_t) f);
+       }
+
+       for (i = 1; i < count; i++) {
+               unsigned char key[16];
+               void *f = NULL;
+               snprintf((char *)key, 16, "%lld key of a raw hashtable!!", i);
+               result = isc_ht_find(ht, key, 16, &f);
+               ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+               ATF_REQUIRE_EQ(f, NULL);
+       }
+
+       isc_ht_destroy(&ht);
+       ATF_REQUIRE_EQ(ht, NULL);
+}
+
+static isc_uint32_t walked = 0;
+
+typedef enum {
+       REGULAR,
+       ERASEEVEN,
+       ERASEODD,
+       CRASH
+} walkmode_t;
+
+static isc_result_t walker(void *udata, const unsigned char *key,
+                          isc_uint32_t keysize, void *data)
+{
+       char mykey[16];
+       isc_uint64_t ii = (isc_uint64_t) data;
+       walkmode_t mode = (isc_uint64_t) udata;
+       ATF_REQUIRE_EQ(keysize, 16);
+
+       snprintf(mykey, 16, "%lld key of a raw hashtable!!", ii);
+       ATF_REQUIRE_EQ(memcmp(mykey, key, 16), 0);
+
+       walked++;
+       switch (mode) {
+       case REGULAR:
+               break;
+       case ERASEEVEN:
+               if (ii % 2 == 0) {
+                       return (ISC_R_EXISTS);
+               }
+               break;
+       case ERASEODD:
+               if (ii % 2 != 0) {
+                       return (ISC_R_EXISTS);
+               }
+               break;
+       case CRASH:
+               if (walked == 100) {
+                       /* something as odd as possible */
+                       return (ISC_R_HOSTUNREACH);
+               }
+               break;
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+static void test_ht_walk() {
+       isc_ht_t *ht = NULL;
+       isc_result_t result;
+       isc_mem_t *mctx = NULL;
+       isc_int64_t i;
+       isc_uint32_t count = 10000;
+
+       result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
+                                 NULL, &mctx, 0);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       result = isc_ht_init(&ht, mctx, 16);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       ATF_REQUIRE(ht != NULL);
+       for (i = 1; i <= count; i++) {
+               /*
+                * Note that the string we're snprintfing is always > 16 bytes
+                * so we are always filling the key.
+                */
+               unsigned char key[16];
+               snprintf((char *)key, 16, "%lld key of a raw hashtable!!", i);
+               result = isc_ht_add(ht, key, 16, (void *) i);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       }
+
+       walked = 0;
+       result = isc_ht_walk(ht, walker, (void *) REGULAR);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(walked, count);
+
+       walked = 0;
+       result = isc_ht_walk(ht, walker, (void *) CRASH);
+       ATF_REQUIRE_EQ(result, ISC_R_HOSTUNREACH);
+       ATF_REQUIRE_EQ(walked, 100);
+
+       walked = 0;
+       result = isc_ht_walk(ht, walker, (void *) ERASEODD);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(walked, count);
+
+       walked = 0;
+       result = isc_ht_walk(ht, walker, (void *) ERASEEVEN);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(walked, count/2);
+
+       walked = 0;
+       result = isc_ht_walk(ht, walker, (void *) REGULAR);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(walked, 0);
+
+       isc_ht_destroy(&ht);
+       ATF_REQUIRE_EQ(ht, NULL);
+}
+
+ATF_TC(isc_ht_20);
+ATF_TC_HEAD(isc_ht_20, tc) {
+       atf_tc_set_md_var(tc, "descr", "20 bit, 2M elements test");
+}
+
+ATF_TC_BODY(isc_ht_20, tc) {
+       UNUSED(tc);
+       test_ht_full(20, 2000000);
+}
+
+
+ATF_TC(isc_ht_8);
+ATF_TC_HEAD(isc_ht_8, tc) {
+       atf_tc_set_md_var(tc, "descr", "8 bit, 20000 elements crowded test");
+}
+
+ATF_TC_BODY(isc_ht_8, tc) {
+       UNUSED(tc);
+       test_ht_full(8, 20000);
+}
+
+ATF_TC(isc_ht_1);
+ATF_TC_HEAD(isc_ht_1, tc) {
+       atf_tc_set_md_var(tc, "descr", "1 bit, 100 elements corner case test");
+}
+
+ATF_TC_BODY(isc_ht_1, tc) {
+       UNUSED(tc);
+       test_ht_full(1, 100);
+}
+
+ATF_TC(isc_ht_32);
+ATF_TC_HEAD(isc_ht_32, tc) {
+       atf_tc_set_md_var(tc, "descr", "32 bit, 10000 elements corner case test");
+}
+
+ATF_TC_BODY(isc_ht_32, tc) {
+       UNUSED(tc);
+       test_ht_full(32, 10000);
+}
+
+ATF_TC(isc_ht_walk);
+ATF_TC_HEAD(isc_ht_walk, tc) {
+       atf_tc_set_md_var(tc, "descr", "hashtable walking");
+}
+
+ATF_TC_BODY(isc_ht_walk, tc) {
+       UNUSED(tc);
+       test_ht_walk();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+       ATF_TP_ADD_TC(tp, isc_ht_20);
+       ATF_TP_ADD_TC(tp, isc_ht_8);
+       ATF_TP_ADD_TC(tp, isc_ht_1);
+       ATF_TP_ADD_TC(tp, isc_ht_32);
+       ATF_TP_ADD_TC(tp, isc_ht_walk);
+       return (atf_no_error());
+}
+
index 3084078d4d5a2cde374fa57c9e2c3cc4e4b883b5..c339fc76d1c5fb90c90d029f38c02a766d25a32c 100644 (file)
@@ -308,6 +308,13 @@ isc_hmacsha512_invalidate
 isc_hmacsha512_sign
 isc_hmacsha512_update
 isc_hmacsha512_verify
+isc_ht_init
+isc_ht_destroy
+isc_ht_find
+isc_ht_delete
+isc_ht_iter_get
+isc_ht_iter_inc
+isc_ht_iter_value
 isc_httpd_addheader
 isc_httpd_addheaderuint
 isc_httpd_response
index d23365ba27e0e896b9348232056f9d28225f7fbc..a3ca7c8163bee260dd26f7883701410bc11e73d5 100644 (file)
@@ -1472,6 +1472,42 @@ static cfg_type_t cfg_type_rpz = {
        rpz_fields
 };
 
+/*
+ * Catalog zones
+ */
+static cfg_type_t cfg_type_catz_zone = {
+       "zone", parse_keyvalue, print_keyvalue,
+       doc_keyvalue, &cfg_rep_string,
+       &zone_kw
+};
+
+static cfg_tuplefielddef_t catz_zone_fields[] = {
+       { "zone name", &cfg_type_catz_zone, 0 },
+       { "default-masters", &cfg_type_namesockaddrkeylist, 0 },
+       { "in-memory", &cfg_type_boolean, 0 },
+       { "min-update-interval", &cfg_type_uint32, 0 },
+       { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_catz_tuple = {
+       "catz tuple", cfg_parse_kv_tuple,
+       cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple,
+       catz_zone_fields
+};
+static cfg_type_t cfg_type_catz_list = {
+       "zone list", cfg_parse_bracketed_list, cfg_print_bracketed_list,
+       cfg_doc_bracketed_list, &cfg_rep_list,
+       &cfg_type_catz_tuple
+};
+static cfg_tuplefielddef_t catz_fields[] = {
+       { "zone list", &cfg_type_catz_list, 0 },
+       { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_catz = {
+       "catz", cfg_parse_kv_tuple, cfg_print_kv_tuple,
+       cfg_doc_kv_tuple, &cfg_rep_tuple, catz_fields
+};
+
+
 
 /*
  * rate-limit
@@ -1627,6 +1663,7 @@ view_clauses[] = {
        { "attach-cache", &cfg_type_astring, 0 },
        { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
        { "cache-file", &cfg_type_qstring, 0 },
+       { "catalog-zones", &cfg_type_catz, 0 },
        { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
        { "cleaning-interval", &cfg_type_uint32, 0 },
        { "clients-per-query", &cfg_type_uint32, 0 },