diff --git a/.gitignore b/.gitignore
index e8d7aa4873..65540e2f90 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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 8641170c45..e69d2f8d5d 100644
--- 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]
diff --git a/bin/named/config.c b/bin/named/config.c
index e74ebbfef5..fb061e7d3d 100644
--- a/bin/named/config.c
+++ b/bin/named/config.c
@@ -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];
diff --git a/bin/named/include/named/config.h b/bin/named/include/named/config.h
index 5302c9b29f..f051bfeca3 100644
--- a/bin/named/include/named/config.h
+++ b/bin/named/include/named/config.h
@@ -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 char* name,
+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 char* name,
+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);
diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook
index b919a628c7..932afea19d 100644
--- a/bin/named/named.conf.docbook
+++ b/bin/named/named.conf.docbook
@@ -283,6 +283,16 @@ options {
check-mx-cname ( fail | warn | ignore );
check-srv-cname ( fail | warn | ignore );
cache-file quoted_string; // test option
+ catalog-zones {
+ zone quoted_string
+ default-masters
+ port ip_port
+ dscp ip_dscp
+ { ( masters_list | ip_addr port ip_port key key ) ; ... }
+ in-memory yes_or_no
+ min-update-interval interval
+ ; ... };
+ ;
suppress-initial-notify boolean; // not yet implemented
preferred-glue string;
dual-stack-servers port integer {
diff --git a/bin/named/server.c b/bin/named/server.c
index f06678d078..58ab7c06be 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -65,11 +65,13 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -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);
diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
index 335519a139..2d297a7c80 100644
--- a/bin/named/zoneconf.c
+++ b/bin/named/zoneconf.c
@@ -27,6 +27,7 @@
#include
#include
+#include
#include
#include
#include
@@ -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
index 0000000000..4e4bdc4316
--- /dev/null
+++ b/bin/tests/system/catz/clean.sh
@@ -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
index 0000000000..53013fe837
--- /dev/null
+++ b/bin/tests/system/catz/ns1/catalog.example.db.in
@@ -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
index 0000000000..db67cbfffa
--- /dev/null
+++ b/bin/tests/system/catz/ns1/named.conf
@@ -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
index 0000000000..9df04aeee0
--- /dev/null
+++ b/bin/tests/system/catz/ns2/named.conf
@@ -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
index 0000000000..4cbd501e07
--- /dev/null
+++ b/bin/tests/system/catz/ns3/dom4.example.db
@@ -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
index 0000000000..d4cb7954a4
--- /dev/null
+++ b/bin/tests/system/catz/ns3/named.conf
@@ -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
index 0000000000..ece0f4f9e6
--- /dev/null
+++ b/bin/tests/system/catz/setup.sh
@@ -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
index 0000000000..77d54414b7
--- /dev/null
+++ b/bin/tests/system/catz/tests.sh
@@ -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 <> 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 <> 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 <> 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 <> 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 <> 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
diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in
index 3e924723d1..3963447ed4 100644
--- a/bin/tests/system/conf.sh.in
+++ b/bin/tests/system/conf.sh.in
@@ -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
diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
index d14538e79b..25c81fd6de 100644
--- a/doc/arm/Bv9ARM-book.xml
+++ b/doc/arm/Bv9ARM-book.xml
@@ -2373,6 +2373,8 @@ options {
+
+
IPv6 Support in BIND 9
BIND 9 fully supports all currently
@@ -4674,6 +4676,16 @@ badresp:1,adberr:0,findfail:0,valfail:0]
qname-wait-recurse yes_or_no
automatic-interface-scan yes_or_no
;
+ catalog-zones {
+ zone quoted_string
+ default-masters
+ port ip_port
+ dscp ip_dscp
+ { ( masters_list | ip_addr port ip_port key key ) ; ... }
+ in-memory yes_or_no
+ min-update-interval interval
+ ; ... };
+ ;
v6-bias number ;
};
diff --git a/doc/arm/Bv9ARM.pdf b/doc/arm/Bv9ARM.pdf
index 1569804414..a537c7664b 100644
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
index 0000000000..9316ee8c6d
--- /dev/null
+++ b/doc/arm/catz.xml
@@ -0,0 +1,229 @@
+
+Catalog Zones
+
+
+ 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.
+
+
+ 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/
+
+
+ Principle of Operation
+
+ Normally, if a zone is to be served by a slave server, the
+ named.conf file on the server must list the
+ zone, or the zone must be added using rndc addzone.
+ 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.
+
+
+ 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.
+
+
+ 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 list
+ in the or statement
+ in named.conf. (This is comparable to the way
+ a policy zone is configured as a normal zone and also listed in
+ a statement.)
+
+
+ To use the catalog zone feature to serve a new member zone:
+
+
+
+ Set up the the member zone to be served on the master as normal.
+ This could be done by editing named.conf,
+ or by running rndc addzone.
+
+
+
+
+ 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 rndc reload, or by updating
+ the zone using nsupdate.
+
+
+
+ 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 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.
+
+
+ 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
+ rndc delzone.)
+
+
+
+ Configuring Catalog Zones
+
+ Catalog zones are configured with a catalog-zones
+ statement in the options or view
+ section of named.conf. For example,
+
+
+catalog-zones {
+ zone "catalog.example" default-masters { 10.53.0.1; } in-memory true min-update-interval 10;
+};
+
+
+ This statement specifies that the zone
+ catalog.example is a catalog zone. This zone must be
+ properly configured in the same view. In most configurations, it would
+ be a slave zone.
+
+
+ The 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.
+
+
+ The option, if set to yes,
+ causes member zones to be stored only in memory. This is functionally
+ equivalent to configuring a slave zone without a .
+ option. The default is no; 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.
+
+
+ The 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 seconds after the most
+ recent update, then the changes will not be carried out until this
+ interval has elapsed. The default is 5 seconds.
+
+
+ Catalog zones are defined on a per-view basis. Configuring a non-empty
+ statement in a view will automatically
+ turn on for that view. (Note: this
+ means rndc addzone and rndc delzone
+ will also work in any view that supports catalog zones.)
+
+
+
+ Catalog Zone format
+
+ A catalog zone is a regular DNS zone; therefore, it has to have a
+ single SOA and at least one NS
+ record.
+
+
+ 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.
+
+
+catalog.example. IN SOA . . 2016022901 900 600 86400 1
+catalog.example. IN NS nsexample.
+version.catalog.example. IN TXT "1"
+
+
+ Note that this record must have the domain name
+ version.catalog-zone-name. 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.
+
+
+ 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:
+
+
+
+masters.catalog.example IN A 192.0.2.1
+masters.catalog.example IN AAAA 2001:db8::1
+
+
+ (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
+
+ or with
+ .
+ There is currently no way to force a particular ordering.)
+
+
+ A member zone is added by including a PTR
+ resource record in the zones sub-domain of the
+ catalog zone. The record label is a SHA-1 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 domain.example:
+
+
+5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example IN PTR domain.example.
+
+
+ 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:
+
+
+masters.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example IN A 192.0.2.2
+masters.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example IN AAAA 2001:db8::2
+
+
+ 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 catalog-zones
+ statement in the configuration file.
+
+
+ (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 masters record of type
+ A but not AAAA, then it would not inherit the
+ type AAAA record from the global option.)
+
+
+
diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml
index 593cbf3879..b0993c4697 100644
--- a/doc/arm/notes.xml
+++ b/doc/arm/notes.xml
@@ -54,6 +54,35 @@
New Features
+
+
+ A new method of provisioning secondary servers called
+ "Catalog Zones" has been added. This is an implementation of
+
+ draft-muks-dnsop-dns-catalog-zones/
+ .
+
+
+ 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.
+
+
+ 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.
+
+
Added rndc python module.
diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in
index ee40594ed8..335d2f74da 100644
--- a/lib/dns/Makefile.in
+++ b/lib/dns/Makefile.in
@@ -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 \
diff --git a/lib/dns/acl.c b/lib/dns/acl.c
index 41c865585a..d2d5139530 100644
--- a/lib/dns/acl.c
+++ b/lib/dns/acl.c
@@ -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
index 0000000000..4866a5a1b5
--- /dev/null
+++ b/lib/dns/catz.c
@@ -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
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*%
+ * 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));
+}
diff --git a/lib/dns/db.c b/lib/dns/db.c
index 4193b2f506..2d4e9197e4 100644
--- a/lib/dns/db.c
+++ b/lib/dns/db.c
@@ -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
index 0000000000..917d958613
--- /dev/null
+++ b/lib/dns/include/dns/catz.h
@@ -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
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+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_ */
diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h
index 9715249f9a..dfb25d406e 100644
--- a/lib/dns/include/dns/db.h
+++ b/lib/dns/include/dns/db.h
@@ -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 */
diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h
index f43d40bacc..45a1709a9e 100644
--- a/lib/dns/include/dns/events.h
+++ b/lib/dns/include/dns/events.h
@@ -80,6 +80,10 @@
#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
index 0000000000..5066181c6e
--- /dev/null
+++ b/lib/dns/include/dns/ipkeylist.h
@@ -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
+#include
+
+/*%
+ * 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
diff --git a/lib/dns/include/dns/rbt.h b/lib/dns/include/dns/rbt.h
index ef45840d12..8129450d5b 100644
--- a/lib/dns/include/dns/rbt.h
+++ b/lib/dns/include/dns/rbt.h
@@ -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);
diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h
index 54fe547e15..e29d28d359 100644
--- a/lib/dns/include/dns/types.h
+++ b/lib/dns/include/dns/types.h
@@ -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.
diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h
index 9156e82e0a..c2e7e8d557 100644
--- a/lib/dns/include/dns/view.h
+++ b/lib/dns/include/dns/view.h
@@ -70,6 +70,7 @@
#include
#include
+#include
#include
#include
#include
@@ -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;
diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h
index 8837ea655e..b992047d02 100644
--- a/lib/dns/include/dns/zone.h
+++ b/lib/dns/include/dns/zone.h
@@ -15,8 +15,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id$ */
-
#ifndef DNS_ZONE_H
#define DNS_ZONE_H 1
@@ -32,6 +30,7 @@
#include
#include
+#include
#include
#include
#include
@@ -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);
diff --git a/lib/dns/include/dns/zt.h b/lib/dns/include/dns/zt.h
index f91d7e8dbe..6e5868ce71 100644
--- a/lib/dns/include/dns/zt.h
+++ b/lib/dns/include/dns/zt.h
@@ -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
index 0000000000..a3bef8ac9f
--- /dev/null
+++ b/lib/dns/ipkeylist.c
@@ -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
+#include
+#include
+
+#include
+#include
+
+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;
+ }
+ }
+ }
+}
diff --git a/lib/dns/name.c b/lib/dns/name.c
index dcb7ad6e6f..85345bcb07 100644
--- a/lib/dns/name.c
+++ b/lib/dns/name.c
@@ -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
diff --git a/lib/dns/rbt.c b/lib/dns/rbt.c
index 8b6a699216..92d4aca885 100644
--- a/lib/dns/rbt.c
+++ b/lib/dns/rbt.c
@@ -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;
diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
index cb98eeac90..e6954f51cd 100644
--- a/lib/dns/rbtdb.c
+++ b/lib/dns/rbtdb.c
@@ -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;
diff --git a/lib/dns/view.c b/lib/dns/view.c
index 402c89b63b..bbce16c16b 100644
--- a/lib/dns/view.c
+++ b/lib/dns/view.c
@@ -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);
diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in
index 4c8230e24a..03651e5fca 100644
--- a/lib/dns/win32/libdns.def.in
+++ b/lib/dns/win32/libdns.def.in
@@ -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
diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
index 63fd960edf..f49d1d8a13 100644
--- a/lib/dns/xfrin.c
+++ b/lib/dns/xfrin.c
@@ -30,6 +30,7 @@
#include
#include
+#include
#include
#include
#include
@@ -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);
}
diff --git a/lib/dns/zone.c b/lib/dns/zone.c
index b09df67e62..74ae734f8e 100644
--- a/lib/dns/zone.c
+++ b/lib/dns/zone.c
@@ -43,6 +43,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -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;
diff --git a/lib/dns/zt.c b/lib/dns/zt.c
index ffc76bbeb5..9079d367f7 100644
--- a/lib/dns/zt.c
+++ b/lib/dns/zt.c
@@ -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;
diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in
index d3a5de8299..26a1954545 100644
--- a/lib/isc/Makefile.in
+++ b/lib/isc/Makefile.in
@@ -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 \
diff --git a/lib/isc/buffer.c b/lib/isc/buffer.c
index b23601c387..2cf128f9a1 100644
--- a/lib/isc/buffer.c
+++ b/lib/isc/buffer.c
@@ -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
index 0000000000..6d710cb4ba
--- /dev/null
+++ b/lib/isc/ht.c
@@ -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
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+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<mask = (1<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);
+}
diff --git a/lib/isc/include/isc/buffer.h b/lib/isc/include/isc/buffer.h
index a6193a53c2..275d7ec664 100644
--- a/lib/isc/include/isc/buffer.h
+++ b/lib/isc/include/isc/buffer.h
@@ -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)
diff --git a/lib/isc/include/isc/hash.h b/lib/isc/include/isc/hash.h
index 571e74fd5c..d9b76bac2d 100644
--- a/lib/isc/include/isc/hash.h
+++ b/lib/isc/include/isc/hash.h
@@ -19,6 +19,7 @@
#define ISC_HASH_H 1
#include
+#include
/*****
***** 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
index 0000000000..2d9d60c616
--- /dev/null
+++ b/lib/isc/include/isc/ht.h
@@ -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
+#include
+#include
+
+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<=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
diff --git a/lib/isc/include/isc/util.h b/lib/isc/include/isc/util.h
index fc2e188264..0c7733b4a9 100644
--- a/lib/isc/include/isc/util.h
+++ b/lib/isc/include/isc/util.h
@@ -248,4 +248,13 @@
*/
#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 */
diff --git a/lib/isc/mem.c b/lib/isc/mem.c
index e190d452d1..ee77f9caa2 100644
--- a/lib/isc/mem.c
+++ b/lib/isc/mem.c
@@ -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
diff --git a/lib/isc/task.c b/lib/isc/task.c
index ac049a7552..26b259a884 100644
--- a/lib/isc/task.c
+++ b/lib/isc/task.c
@@ -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) {
diff --git a/lib/isc/tests/Makefile.in b/lib/isc/tests/Makefile.in
index b418366554..f918c29efb 100644
--- a/lib/isc/tests/Makefile.in
+++ b/lib/isc/tests/Makefile.in
@@ -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
diff --git a/lib/isc/tests/buffer_test.c b/lib/isc/tests/buffer_test.c
index 51a9b4cec3..af0b76a628 100644
--- a/lib/isc/tests/buffer_test.c
+++ b/lib/isc/tests/buffer_test.c
@@ -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
index 0000000000..cc5fe3eed6
--- /dev/null
+++ b/lib/isc/tests/ht_test.c
@@ -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
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+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());
+}
+
diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in
index 3084078d4d..c339fc76d1 100644
--- a/lib/isc/win32/libisc.def.in
+++ b/lib/isc/win32/libisc.def.in
@@ -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
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index d23365ba27..a3ca7c8163 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -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 },