]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
4576. [func] The RPZ implementation has been substantially refactored...
authorWitold Krecicki <wpk@isc.org>
Mon, 20 Feb 2017 10:57:28 +0000 (11:57 +0100)
committerWitold Krecicki <wpk@isc.org>
Mon, 20 Feb 2017 10:57:28 +0000 (11:57 +0100)
21 files changed:
CHANGES
bin/named/server.c
bin/tests/system/dyndb/driver/db.c
bin/tests/system/rpz/ns3/named.conf
bin/tests/system/rpz/ns6/named.conf
bin/tests/system/rpz/ns7/named.conf
bin/tests/system/rpzrecurse/tests.sh
doc/arm/Bv9ARM-book.xml
doc/arm/notes.xml
doc/misc/options
lib/dns/db.c
lib/dns/include/dns/db.h
lib/dns/include/dns/events.h
lib/dns/include/dns/rpz.h
lib/dns/rbtdb.c
lib/dns/rpz.c
lib/dns/win32/libdns.def.in
lib/dns/zone.c
lib/isc/ht.c
lib/isc/include/isc/ht.h
lib/isccfg/namedconf.c

diff --git a/CHANGES b/CHANGES
index dea792b4084fbe560dc0ec9ec70bb47eb9ca35fb..13987f4f78a691d563d3ae35125e12ab80b2388d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+4576.  [func]          The RPZ implementation has been substantially
+                       refactored for improved performance and reliability.
+                       [RT #43449]
+
 4575.  [security]      DNS64 with "break-dnssec yes;" can result in an
                        assertion failure. (CVE-2017-3136) [RT #44653]
 
index f7c8cc19fddca4294b247f0c716c87849080676a..b512b5ddfb718c288645af09c02030e4f9193522 100644 (file)
@@ -1872,11 +1872,12 @@ configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
 static isc_result_t
 configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
                   isc_boolean_t recursive_only_def, dns_ttl_t ttl_def,
-                  const dns_rpz_zone_t *old, isc_boolean_t *old_rpz_okp)
+                  isc_uint32_t minupdateint_def, const dns_rpz_zone_t *old,
+                  isc_boolean_t *old_rpz_okp)
 {
        const cfg_obj_t *rpz_obj, *obj;
        const char *str;
-       dns_rpz_zone_t *new;
+       dns_rpz_zone_t *zone = NULL;
        isc_result_t result;
        dns_rpz_num_t rpz_num;
 
@@ -1891,127 +1892,118 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
                return (ISC_R_FAILURE);
        }
 
-       new = isc_mem_get(view->rpzs->mctx, sizeof(*new));
-       if (new == NULL) {
-               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                           "no memory for response policy zones");
-               return (ISC_R_NOMEMORY);
-       }
-
-       memset(new, 0, sizeof(*new));
-       result = isc_refcount_init(&new->refs, 1);
+       result = dns_rpz_new_zone(view->rpzs, &zone);
        if (result != ISC_R_SUCCESS) {
-               isc_mem_put(view->rpzs->mctx, new, sizeof(*new));
+               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                           "Error creating new RPZ zone : %s",
+                           isc_result_totext(result));
                return (result);
        }
-       dns_name_init(&new->origin, NULL);
-       dns_name_init(&new->client_ip, NULL);
-       dns_name_init(&new->ip, NULL);
-       dns_name_init(&new->nsdname, NULL);
-       dns_name_init(&new->nsip, NULL);
-       dns_name_init(&new->passthru, NULL);
-       dns_name_init(&new->drop, NULL);
-       dns_name_init(&new->tcp_only, NULL);
-       dns_name_init(&new->cname, NULL);
-       new->num = view->rpzs->p.num_zones++;
-       view->rpzs->zones[new->num] = new;
 
        obj = cfg_tuple_get(rpz_obj, "recursive-only");
        if (cfg_obj_isvoid(obj) ? recursive_only_def : cfg_obj_asboolean(obj)) {
-               view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(new->num);
+               view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(zone->num);
        } else {
-               view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(new->num);
+               view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(zone->num);
        }
 
        obj = cfg_tuple_get(rpz_obj, "log");
        if (!cfg_obj_isvoid(obj) && !cfg_obj_asboolean(obj)) {
-               view->rpzs->p.no_log |= DNS_RPZ_ZBIT(new->num);
+               view->rpzs->p.no_log |= DNS_RPZ_ZBIT(zone->num);
        } else {
-               view->rpzs->p.no_log &= ~DNS_RPZ_ZBIT(new->num);
+               view->rpzs->p.no_log &= ~DNS_RPZ_ZBIT(zone->num);
        }
 
        obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
        if (cfg_obj_isuint32(obj)) {
-               new->max_policy_ttl = cfg_obj_asuint32(obj);
+               zone->max_policy_ttl = cfg_obj_asuint32(obj);
+       } else {
+               zone->max_policy_ttl = ttl_def;
+       }
+
+       obj = cfg_tuple_get(rpz_obj, "min-update-interval");
+       if (cfg_obj_isuint32(obj)) {
+               zone->min_update_int = cfg_obj_asuint32(obj);
        } else {
-               new->max_policy_ttl = ttl_def;
+               zone->min_update_int = minupdateint_def;
        }
-       if (*old_rpz_okp && new->max_policy_ttl != old->max_policy_ttl)
+
+       if (*old_rpz_okp && zone->max_policy_ttl != old->max_policy_ttl)
                *old_rpz_okp = ISC_FALSE;
 
        str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name"));
-       result = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone");
+       result = configure_rpz_name(view, rpz_obj, &zone->origin, str, "zone");
        if (result != ISC_R_SUCCESS)
                return (result);
-       if (dns_name_equal(&new->origin, dns_rootname)) {
+       if (dns_name_equal(&zone->origin, dns_rootname)) {
                cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
                            "invalid zone name '%s'", str);
                return (DNS_R_EMPTYLABEL);
        }
        for (rpz_num = 0; rpz_num < view->rpzs->p.num_zones-1; ++rpz_num) {
                if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin,
-                                  &new->origin)) {
+                                  &zone->origin)) {
                        cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
                                    "duplicate '%s'", str);
                        result = DNS_R_DUPLICATE;
                        return (result);
                }
        }
-       if (*old_rpz_okp && !dns_name_equal(&old->origin, &new->origin))
+       if (*old_rpz_okp && !dns_name_equal(&old->origin, &zone->origin))
                *old_rpz_okp = ISC_FALSE;
 
-       result = configure_rpz_name2(view, rpz_obj, &new->client_ip,
-                                    DNS_RPZ_CLIENT_IP_ZONE, &new->origin);
+       result = configure_rpz_name2(view, rpz_obj, &zone->client_ip,
+                                    DNS_RPZ_CLIENT_IP_ZONE, &zone->origin);
        if (result != ISC_R_SUCCESS)
                return (result);
 
-       result = configure_rpz_name2(view, rpz_obj, &new->ip,
-                                    DNS_RPZ_IP_ZONE, &new->origin);
+       result = configure_rpz_name2(view, rpz_obj, &zone->ip,
+                                    DNS_RPZ_IP_ZONE, &zone->origin);
        if (result != ISC_R_SUCCESS)
                return (result);
 
-       result = configure_rpz_name2(view, rpz_obj, &new->nsdname,
-                                    DNS_RPZ_NSDNAME_ZONE, &new->origin);
+       result = configure_rpz_name2(view, rpz_obj, &zone->nsdname,
+                                    DNS_RPZ_NSDNAME_ZONE, &zone->origin);
        if (result != ISC_R_SUCCESS)
                return (result);
 
-       result = configure_rpz_name2(view, rpz_obj, &new->nsip,
-                                    DNS_RPZ_NSIP_ZONE, &new->origin);
+       result = configure_rpz_name2(view, rpz_obj, &zone->nsip,
+                                    DNS_RPZ_NSIP_ZONE, &zone->origin);
        if (result != ISC_R_SUCCESS)
                return (result);
 
-       result = configure_rpz_name(view, rpz_obj, &new->passthru,
+       result = configure_rpz_name(view, rpz_obj, &zone->passthru,
                                    DNS_RPZ_PASSTHRU_NAME, "name");
        if (result != ISC_R_SUCCESS)
                return (result);
 
-       result = configure_rpz_name(view, rpz_obj, &new->drop,
+       result = configure_rpz_name(view, rpz_obj, &zone->drop,
                                    DNS_RPZ_DROP_NAME, "name");
        if (result != ISC_R_SUCCESS)
                return (result);
 
-       result = configure_rpz_name(view, rpz_obj, &new->tcp_only,
+       result = configure_rpz_name(view, rpz_obj, &zone->tcp_only,
                                    DNS_RPZ_TCP_ONLY_NAME, "name");
        if (result != ISC_R_SUCCESS)
                return (result);
 
        obj = cfg_tuple_get(rpz_obj, "policy");
        if (cfg_obj_isvoid(obj)) {
-               new->policy = DNS_RPZ_POLICY_GIVEN;
+               zone->policy = DNS_RPZ_POLICY_GIVEN;
        } else {
                str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name"));
-               new->policy = dns_rpz_str2policy(str);
-               INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
-               if (new->policy == DNS_RPZ_POLICY_CNAME) {
+               zone->policy = dns_rpz_str2policy(str);
+               INSIST(zone->policy != DNS_RPZ_POLICY_ERROR);
+               if (zone->policy == DNS_RPZ_POLICY_CNAME) {
                        str = cfg_obj_asstring(cfg_tuple_get(obj, "cname"));
-                       result = configure_rpz_name(view, rpz_obj, &new->cname,
+                       result = configure_rpz_name(view, rpz_obj, &zone->cname,
                                                    str, "cname");
                        if (result != ISC_R_SUCCESS)
                                return (result);
                }
        }
-       if (*old_rpz_okp && (new->policy != old->policy ||
-                            !dns_name_equal(&old->cname, &new->cname)))
+       if (*old_rpz_okp && (zone->policy != old->policy ||
+                            !dns_name_equal(&old->cname, &zone->cname)))
                *old_rpz_okp = ISC_FALSE;
 
        return (ISC_R_SUCCESS);
@@ -2025,7 +2017,8 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
        const cfg_obj_t *sub_obj;
        isc_boolean_t recursive_only_def;
        dns_ttl_t ttl_def;
-       dns_rpz_zones_t *new;
+       isc_uint32_t minupdateint_def;
+       dns_rpz_zones_t *zones;
        const dns_rpz_zones_t *old;
        dns_view_t *pview;
        const dns_rpz_zone_t *old_zone;
@@ -2038,10 +2031,12 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
        if (zone_element == NULL)
                return (ISC_R_SUCCESS);
 
-       result = dns_rpz_new_zones(&view->rpzs, view->mctx);
+       result = dns_rpz_new_zones(&view->rpzs, view->mctx,
+                                  ns_g_taskmgr, ns_g_timermgr);
        if (result != ISC_R_SUCCESS)
                return (result);
-       new = view->rpzs;
+
+       zones = view->rpzs;
 
        sub_obj = cfg_tuple_get(rpz_obj, "recursive-only");
        if (!cfg_obj_isvoid(sub_obj) &&
@@ -2053,9 +2048,9 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
        sub_obj = cfg_tuple_get(rpz_obj, "break-dnssec");
        if (!cfg_obj_isvoid(sub_obj) &&
            cfg_obj_asboolean(sub_obj))
-               new->p.break_dnssec = ISC_TRUE;
+               zones->p.break_dnssec = ISC_TRUE;
        else
-               new->p.break_dnssec = ISC_FALSE;
+               zones->p.break_dnssec = ISC_FALSE;
 
        sub_obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
        if (cfg_obj_isuint32(sub_obj))
@@ -2063,23 +2058,29 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
        else
                ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
 
+       sub_obj = cfg_tuple_get(rpz_obj, "min-update-interval");
+       if (cfg_obj_isuint32(sub_obj))
+               minupdateint_def = cfg_obj_asuint32(sub_obj);
+       else
+               minupdateint_def = DNS_RPZ_MINUPDATEINT_DEF;
+
        sub_obj = cfg_tuple_get(rpz_obj, "min-ns-dots");
        if (cfg_obj_isuint32(sub_obj))
-               new->p.min_ns_labels = cfg_obj_asuint32(sub_obj) + 1;
+               zones->p.min_ns_labels = cfg_obj_asuint32(sub_obj) + 1;
        else
-               new->p.min_ns_labels = 2;
+               zones->p.min_ns_labels = 2;
 
        sub_obj = cfg_tuple_get(rpz_obj, "qname-wait-recurse");
        if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj))
-               new->p.qname_wait_recurse = ISC_TRUE;
+               zones->p.qname_wait_recurse = ISC_TRUE;
        else
-               new->p.qname_wait_recurse = ISC_FALSE;
+               zones->p.qname_wait_recurse = ISC_FALSE;
 
        sub_obj = cfg_tuple_get(rpz_obj, "nsip-wait-recurse");
        if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj))
-               new->p.nsip_wait_recurse = ISC_TRUE;
+               zones->p.nsip_wait_recurse = ISC_TRUE;
        else
-               new->p.nsip_wait_recurse = ISC_FALSE;
+               zones->p.nsip_wait_recurse = ISC_FALSE;
 
        pview = NULL;
        result = dns_viewlist_find(&ns_g_server->viewlist,
@@ -2106,7 +2107,8 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
                }
                result = configure_rpz_zone(view, zone_element,
                                            recursive_only_def, ttl_def,
-                                           old_zone, old_rpz_okp);
+                                           minupdateint_def, old_zone,
+                                           old_rpz_okp);
                if (result != ISC_R_SUCCESS) {
                        if (pview != NULL)
                                dns_view_detach(&pview);
@@ -2119,7 +2121,7 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
         * zones are unchanged, then use the same policy data.
         * Data for individual zones that must be reloaded will be merged.
         */
-       if (old != NULL && memcmp(&old->p, &new->p, sizeof(new->p)) != 0)
+       if (old != NULL && memcmp(&old->p, &zones->p, sizeof(zones->p)) != 0)
                *old_rpz_okp = ISC_FALSE;
        if (*old_rpz_okp) {
                dns_rpz_detach_rpzs(&view->rpzs);
index 699971baa79057c751d3e0e7101bbc12679d0568..f75aca81a3a549150afb551c7bd539214570efb2 100644 (file)
@@ -510,24 +510,6 @@ getrrsetstats(dns_db_t *db) {
 
 }
 
-static void
-rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
-       sampledb_t *sampledb = (sampledb_t *) db;
-
-       REQUIRE(VALID_SAMPLEDB(sampledb));
-
-       dns_db_rpz_attach(sampledb->rbtdb, rpzs, rpz_num);
-}
-
-static isc_result_t
-rpz_ready(dns_db_t *db) {
-       sampledb_t *sampledb = (sampledb_t *) db;
-
-       REQUIRE(VALID_SAMPLEDB(sampledb));
-
-       return (dns_db_rpz_ready(sampledb->rbtdb));
-}
-
 static isc_result_t
 findnodeext(dns_db_t *db, const dns_name_t *name,
            isc_boolean_t create, dns_clientinfomethods_t *methods,
@@ -617,8 +599,8 @@ static dns_dbmethods_t sampledb_methods = {
        resigned,
        isdnssec,
        getrrsetstats,
-       rpz_attach,
-       rpz_ready,
+       NULL,
+       NULL,
        findnodeext,
        findext,
        setcachestats,
index 908400a94112447c034336b954c71e15df0e5a0b..862ae9cd5991c0843761b3b961b2e6bb3dcfdd12 100644 (file)
@@ -44,6 +44,7 @@ options {
        }
        min-ns-dots 0
        qname-wait-recurse yes
+       min-update-interval 0
        ;
 };
 
index 28600e0114dcecc196874273fc1309510b2fb27a..b8d3ac249885cef0ac03363517c0ebb6657eeaf4 100644 (file)
@@ -19,7 +19,7 @@ options {
        forward only;
        forwarders { 10.53.0.3; };
 
-       response-policy { zone "policy1"; };
+       response-policy { zone "policy1" min-update-interval 0; };
 };
 
 key rndc_key {
index d2a98e837676399f596daa950dc6ac278874b62a..a4fe1716e6344018cd0fa2de6de1d53b9a465fa8 100644 (file)
@@ -17,7 +17,9 @@ options {
        listen-on { 10.53.0.7; };
        listen-on-v6 { none; };
 
-       response-policy { zone "policy2"; } qname-wait-recurse no;
+       response-policy { zone "policy2"; }
+               qname-wait-recurse no
+               min-update-interval 0;
 };
 
 key rndc_key {
index 799b5f662075920e27d0af57913f9fb40d55792b..42144f8f1afd3f55d37e89ce3d72ae1a76b83a19 100644 (file)
@@ -24,6 +24,7 @@ run_server() {
     echo "I:starting resolver using named.$TESTNAME.conf"
     cp -f ns2/named.$TESTNAME.conf ns2/named.conf
     $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns2
+    sleep 3
 }
 
 run_query() {
index 899c7f9f107285684cae645a683c5ac6df2a6845..82f93e881aef73207312207ff1a71288442fec39 100644 (file)
@@ -4409,305 +4409,306 @@ badresp:1,adberr:0,findfail:0,valfail:0]
          statement in the <filename>named.conf</filename> file:
        </para>
 
-<programlisting><command>options {</command>
-  [ <command>attach-cache</command> <replaceable>cache_name</replaceable> ; ]
-  [ <command>version</command> <replaceable>version_string</replaceable> ; ]
-  [ <command>hostname</command> <replaceable>hostname_string</replaceable> ; ]
-  [ <command>server-id</command> <replaceable>server_id_string</replaceable> ; ]
-  [ <command>directory</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>dnstap {</command> <replaceable>message_type</replaceable> ; ... <command>}</command> ; ]
-  [ <command>dnstap-output</command> ( <option>file</option> | <option>unix</option> ) <replaceable>path_name</replaceable> [ <command>size</command> <replaceable>size_spec</replaceable> ] [ <command>versions</command> ( <replaceable>number</replaceable> | <option>unlimited</option> ) ] ; ]
-  [ <command>dnstap-identity</command> ( <replaceable>string</replaceable> | <option>hostname</option> | <option>none</option> ) ; ]
-  [ <command>dnstap-version</command> ( <replaceable>string</replaceable> | <option>none</option> ) ; ]
-  [ <command>fstrm-set-buffer-hint</command> <replaceable>number</replaceable> ; ]
-  [ <command>fstrm-set-flush-timeout</command> <replaceable>number</replaceable> ; ]
-  [ <command>fstrm-set-input-queue-size</command> <replaceable>number</replaceable> ; ]
-  [ <command>fstrm-set-output-notify-threshold</command> <replaceable>number</replaceable> ; ]
-  [ <command>fstrm-set-output-queue-model</command> ( <option>mpsc</option> | <option>spsc</option> ) ; ]
-  [ <command>fstrm-set-output-queue-size</command> <replaceable>number</replaceable> ; ]
-  [ <command>fstrm-set-reopen-interval</command> <replaceable>number</replaceable> ; ]
-  [ <command>geoip-directory</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>key-directory</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>managed-keys-directory</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>named-xfer</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>tkey-gssapi-keytab</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>tkey-gssapi-credential</command> <replaceable>principal</replaceable> ; ]
-  [ <command>tkey-domain</command> <replaceable>domain_name</replaceable> ; ]
-  [ <command>tkey-dhkey</command> <replaceable>key_name</replaceable> <replaceable>key_tag</replaceable> ; ]
-  [ <command>cache-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>dump-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>bindkeys-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>lock-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>secroots-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>session-keyfile</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>session-keyname</command> <replaceable>key_name</replaceable> ; ]
-  [ <command>session-keyalg</command> <replaceable>algorithm_id</replaceable> ; ]
-  [ <command>memstatistics</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>memstatistics-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>pid-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>recursing-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>statistics-file</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>zone-statistics</command> ( <option>full</option> | <option>terse</option> | <option>none</option> ) ; ]
-  [ <command>auth-nxdomain</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>nxdomain-redirect</command> <replaceable>string</replaceable> ; ]
-  [ <command>deallocate-on-exit</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>dialup</command> <replaceable>dialup_option</replaceable> ; ]
-  [ <command>fake-iquery</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>fetch-glue</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>flush-zones-on-shutdown</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>has-old-clients</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>host-statistics</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>host-statistics-max</command> <replaceable>number</replaceable> ; ]
-  [ <command>minimal-any</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>minimal-responses</command> ( <replaceable>yes_or_no</replaceable> | <option>no-auth</option> | <option>no-auth-recursive</option> ) ; ]
-  [ <command>multiple-cnames</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>notify</command> ( <replaceable>yes_or_no</replaceable> | <option>explicit</option> | <option>master-only</option> ) ; ]
-  [ <command>recursion</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>send-cookie</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>require-server-cookie</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>cookie-algorithm</command> <replaceable>algorithm_id</replaceable> ; ]
-  [ <command>cookie-secret</command> <replaceable>secret_string</replaceable> ; ]
-  [ <command>nocookie-udp-size</command> <replaceable>number</replaceable> ; ]
-  [ <command>request-nsid</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>rfc2308-type1</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>use-id-pool</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>maintain-ixfr-base</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>ixfr-from-differences</command> ( <replaceable>yes_or_no</replaceable> | <option>master</option> | <option>slave</option> ) ; ]
-  [ <command>auto-dnssec</command> ( <option>allow</option> | <option>maintain</option> | <option>off</option> ) ; ]
-  [ <command>dnssec-enable</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>dnssec-validation</command> ( <replaceable>yes_or_no</replaceable> | <option>auto</option> ) ; ]
-  [ <command>dnssec-lookaside</command> ( <option>auto</option> | <option>no</option> | <replaceable>domain</replaceable> trust-anchor <replaceable>domain</replaceable> ) ; ]
-  [ <command>dnssec-must-be-secure</command> <replaceable>domain yes_or_no</replaceable> ; ]
-  [ <command>dnssec-accept-expired</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>forward</command> ( <option>only</option> | <option>first</option> ) ; ]
-  [ <command>forwarders {</command>
-      ( <replaceable>ip_addr</replaceable> [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ; )
-        ...
-    <command>}</command> ; ]
-  [ <command>dual-stack-servers</command> [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] <command>{</command>
-      ( ( <replaceable>domain_name</replaceable> | <replaceable>ip_addr</replaceable> ) [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ; )
-        ...
-    <command>}</command> ; ]
-  [ <command>check-names</command> ( <option>master</option> | <option>slave</option> | <option>response</option> )
-               ( <option>warn</option> | <option>fail</option> | <option>ignore</option> ) ; ]
-  [ <command>check-dup-records</command> ( <option>warn</option> | <option>fail</option> | <option>ignore</option> ) ; ]
-  [ <command>check-mx</command> ( <option>warn</option> | <option>fail</option> | <option>ignore</option> ) ; ]
-  [ <command>check-wildcard</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>check-integrity</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>check-mx-cname</command> ( <option>warn</option> | <option>fail</option> | <option>ignore</option> ) ; ]
-  [ <command>check-srv-cname</command> ( <option>warn</option> | <option>fail</option> | <option>ignore</option> ) ; ]
-  [ <command>check-sibling</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>check-spf</command> ( <option>warn</option> | <option>ignore</option> ) ; ]
-  [ <command>allow-new-zones</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>allow-notify {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>allow-query {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>allow-query-on {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>allow-query-cache {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>allow-query-cache-on {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>allow-transfer {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>allow-recursion {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>allow-recursion-on {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>allow-update {</command> <replaceable>address_match_list</replaceable> <command>}</command> ]
-  [ <command>allow-update-forwarding {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>automatic-interface-scan</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>geoip-use-ecs</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>update-check-ksk</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>dnssec-update-mode</command> ( <option>maintain</option> | <option>no-resign</option> ) ; ]
-  [ <command>dnssec-dnskey-kskonly</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>dnssec-loadkeys-interval</command> <replaceable>number</replaceable> ; ]
-  [ <command>dnssec-secure-to-insecure</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>try-tcp-refresh</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>allow-v6-synthesis {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>blackhole {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>keep-response-order {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>no-case-compress {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>message-compression</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>use-v4-udp-ports {</command> <replaceable>port_list</replaceable> <command>}</command> ; ]
-  [ <command>avoid-v4-udp-ports {</command> <replaceable>port_list</replaceable> <command>}</command> ; ]
-  [ <command>use-v6-udp-ports {</command> <replaceable>port_list</replaceable> <command>}</command> ; ]
-  [ <command>avoid-v6-udp-ports {</command> <replaceable>port_list</replaceable> <command>}</command> ; ]
-  [ <command>listen-on</command> [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] <command>{</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>listen-on-v6</command> [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] <command>{</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>query-source</command> ( [ <command>address</command> ] ( <replaceable>ip4_addr</replaceable> | <option>*</option> ) )
-      [ <command>port</command> ( <replaceable>ip_port</replaceable> | <option>*</option> ) ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ] ;
-  [ <command>query-source-v6</command> ( [ <command>address</command> ] ( <replaceable>ip6_addr</replaceable> | <option>*</option> ) )
-      [ <command>port</command> ( <replaceable>ip_port</replaceable> | <option>*</option> ) ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ] ;
-  [ <command>use-queryport-pool</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>queryport-pool-ports</command> <replaceable>number</replaceable> ; ]
-  [ <command>queryport-pool-updateinterval</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-records</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-transfer-time-in</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-transfer-time-out</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-transfer-idle-in</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-transfer-idle-out</command> <replaceable>number</replaceable> ; ]
-  [ <command>reserved-sockets</command> <replaceable>number</replaceable> ; ]
-  [ <command>recursive-clients</command> <replaceable>number</replaceable> ; ]
-  [ <command>tcp-clients</command> <replaceable>number</replaceable> ; ]
-  [ <command>clients-per-query</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-clients-per-query</command> <replaceable>number</replaceable> ; ]
-  [ <command>fetches-per-server</command> <replaceable>number</replaceable> [ ( <option>drop</option> | <option>fail</option> ) ] ; ]
-  [ <command>fetches-per-zone</command> <replaceable>number</replaceable> [ ( <option>drop</option> | <option>fail</option> ) ] ; ]
-  [ <command>fetch-quota-params</command> <replaceable>number fixedpoint fixedpoint fixedpoint</replaceable> ; ]
-  [ <command>notify-rate</command> <replaceable>number</replaceable> ; ]
-  [ <command>startup-notify-rate</command> <replaceable>number</replaceable> ; ]
-  [ <command>serial-query-rate</command> <replaceable>number</replaceable> ; ]
-  [ <command>serial-queries</command> <replaceable>number</replaceable> ; ]
-  [ <command>tcp-listen-queue</command> <replaceable>number</replaceable> ; ]
-  [ <command>tcp-initial-timeout</command> <replaceable>number</replaceable>; ]
-  [ <command>tcp-idle-timeout</command> <replaceable>number</replaceable>; ]
-  [ <command>tcp-keepalive-timeout</command> <replaceable>number</replaceable>; ]
-  [ <command>tcp-advertised-timeout</command> <replaceable>number</replaceable>; ]
-  [ <command>transfer-format</command> ( <option>one-answer</option> | <option>many-answers</option> ) ; ]
-  [ <command>transfer-message-size</command>  <replaceable>number</replaceable> ; ]
-  [ <command>transfers-in</command>  <replaceable>number</replaceable> ; ]
-  [ <command>transfers-out</command> <replaceable>number</replaceable> ; ]
-  [ <command>transfers-per-ns</command> <replaceable>number</replaceable> ; ]
-  [ <command>transfer-source</command> ( <replaceable>ip4_addr</replaceable> | <option>*</option> )
-      [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ; ]
-  [ <command>transfer-source-v6</command> ( <replaceable>ip6_addr</replaceable> | <option>*</option> )
-      [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ; ]
-  [ <command>alt-transfer-source</command> ( <replaceable>ip4_addr</replaceable> | <option>*</option> )
-      [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ; ]
-  [ <command>alt-transfer-source-v6</command> ( <replaceable>ip6_addr</replaceable> | <option>*</option> )
-      [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ; ]
-  [ <command>use-alt-transfer-source</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>notify-delay</command> <replaceable>seconds</replaceable> ; ]
-  [ <command>notify-source</command> ( <replaceable>ip4_addr</replaceable> | <option>*</option> )
-      [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ; ]
-  [ <command>notify-source-v6</command> ( <replaceable>ip6_addr</replaceable> | <option>*</option> )
-      [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] ; ]
-  [ <command>notify-to-soa</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>also-notify</command> [ <command>port</command> <replaceable>ip_port</replaceable>] [ <command>dscp</command> <replaceable>ip_dscp</replaceable>] <command>{</command>
-      ( <replaceable>masters</replaceable> | <replaceable>ip_addr</replaceable> [ <command>port</command> <replaceable>ip_port</replaceable> ] ) [ <command>key</command> <replaceable>key_name</replaceable> ] ;
-        ...
-    <command>}</command> ; ]
-  [ <command>max-ixfr-log-size</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-journal-size</command> <replaceable>size_spec</replaceable> ; ]
-  [ <command>coresize</command> <replaceable>size_spec</replaceable> ; ]
-  [ <command>datasize</command> <replaceable>size_spec</replaceable> ; ]
-  [ <command>files</command> <replaceable>size_spec</replaceable> ; ]
-  [ <command>stacksize</command> <replaceable>size_spec</replaceable> ; ]
-  [ <command>cleaning-interval</command> <replaceable>number</replaceable> ; ]
-  [ <command>heartbeat-interval</command> <replaceable>number</replaceable> ; ]
-  [ <command>interface-interval</command> <replaceable>number</replaceable> ; ]
-  [ <command>statistics-interval</command> <replaceable>number</replaceable> ; ]
-  [ <command>topology {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>sortlist {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>rrset-order {</command> <replaceable>order_spec</replaceable> ; ... <command>}</command> ; ]
-  [ <command>lame-ttl</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-ncache-ttl</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-cache-ttl</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-zone-ttl</command> ( <option>unlimited</option> | <replaceable>number</replaceable> ) ; ]
-  [ <command>serial-update-method</command> ( <option>increment</option> | <option>unixtime</option> | <option>date</option> ) ; ]
-  [ <command>servfail-ttl</command> <replaceable>number</replaceable> ; ]
-  [ <command>sig-validity-interval</command> <replaceable>number</replaceable> [<replaceable>number</replaceable>] ; ]
-  [ <command>sig-signing-nodes</command> <replaceable>number</replaceable> ; ]
-  [ <command>sig-signing-signatures</command> <replaceable>number</replaceable> ; ]
-  [ <command>sig-signing-type</command> <replaceable>number</replaceable> ; ]
-  [ <command>min-roots</command> <replaceable>number</replaceable> ; ]
-  [ <command>use-ixfr</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>provide-ixfr</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>request-ixfr</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>request-expire</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>treat-cr-as-space</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>min-refresh-time</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-refresh-time</command> <replaceable>number</replaceable> ; ]
-  [ <command>min-retry-time</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-retry-time</command> <replaceable>number</replaceable> ; ]
-  [ <command>nta-lifetime</command> <replaceable>duration</replaceable> ; ]
-  [ <command>nta-recheck</command> <replaceable>duration</replaceable> ; ]
-  [ <command>port</command> <replaceable>ip_port</replaceable> ; ]
-  [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ; ]
-  [ <command>additional-from-auth</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>additional-from-cache</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>random-device</command> <replaceable>path_name</replaceable> ; ]
-  [ <command>max-cache-size</command> <replaceable>size_or_percent</replaceable> ; ]
-  [ <command>match-mapped-addresses</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>filter-aaaa-on-v4</command> ( <replaceable>yes_or_no</replaceable> | <option>break-dnssec</option> ) ; ]
-  [ <command>filter-aaaa-on-v6</command> ( <replaceable>yes_or_no</replaceable> | <option>break-dnssec</option> ) ; ]
-  [ <command>filter-aaaa {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-  [ <command>dns64</command> <replaceable>ipv6-prefix</replaceable> <command>{</command>
-      [ <command>clients {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-      [ <command>mapped {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-      [ <command>exclude {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-      [ <command>suffix</command> <replaceable>ip6-address</replaceable> ; ]
-      [ <command>recursive-only</command> <replaceable>yes_or_no</replaceable> ; ]
-      [ <command>break-dnssec</command> <replaceable>yes_or_no</replaceable> ; ]
-    <command>}</command> ; ]
-  [ <command>dns64-server</command> <replaceable>name</replaceable> ]
-  [ <command>dns64-contact</command> <replaceable>name</replaceable> ]
-  [ <command>preferred-glue</command> ( <option>A</option> | <option>AAAA</option> | <option>none</option> ); ]
-  [ <command>edns-udp-size</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-udp-size</command> <replaceable>number</replaceable> ; ]
-  [ <command>response-padding</command> { <replaceable>address_match_list</replaceable> } block-size <replaceable>number</replaceable> ; ]
-  [ <command>max-rsa-exponent-size</command> <replaceable>number</replaceable> ; ]
-  [ <command>root-delegation-only</command> [ <command>exclude {</command> <replaceable>namelist</replaceable> <command>}</command> ] ; ]
-  [ <command>querylog</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>disable-algorithms</command> <replaceable>domain</replaceable> <command>{</command> <replaceable>algorithm</replaceable> ; ... <command>}</command> ; ]
-  [ <command>disable-ds-digests</command> <replaceable>domain</replaceable> <command>{</command> <replaceable>digest_type</replaceable> ; ... <command>}</command> ; ]
-  [ <command>acache-enable</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>acache-cleaning-interval</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-acache-size</command> <replaceable>size_spec</replaceable> ; ]
-  [ <command>max-recursion-depth</command> <replaceable>number</replaceable> ; ]
-  [ <command>max-recursion-queries</command> <replaceable>number</replaceable> ; ]
-  [ <command>masterfile-format</command> ( <option>text</option> | <option>raw</option> | <option>map</option> ) ; ]
-  [ <command>masterfile-style</command> ( <option>relative</option> | <option>full</option> ) ; ]
-  [ <command>empty-server</command> <replaceable>name</replaceable> ; ]
-  [ <command>empty-contact</command> <replaceable>name</replaceable> ; ]
-  [ <command>empty-zones-enable</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>disable-empty-zone</command> <replaceable>zone_name</replaceable> ; ]
-  [ <command>zero-no-soa-ttl</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>zero-no-soa-ttl-cache</command> <replaceable>yes_or_no</replaceable> ; ]
-  [ <command>resolver-query-timeout</command> <replaceable>number</replaceable> ; ]
-  [ <command>deny-answer-addresses {</command> <replaceable>address_match_list</replaceable> <command>}</command>
-      [ <command>except-from {</command> <replaceable>namelist</replaceable> <command>}</command> ] ; ]
-  [ <command>deny-answer-aliases {</command> <replaceable>namelist</replaceable> <command>}</command>
-      [ <command>except-from {</command> <replaceable>namelist</replaceable> <command>}</command> ] ; ]
-  [ <command>prefetch</command> <replaceable>number</replaceable> [ <replaceable>number</replaceable> ] ; ]
-  [ <command>rate-limit {</command>
-      [ <command>responses-per-second</command> <replaceable>number</replaceable> ; ]
-      [ <command>referrals-per-second</command> <replaceable>number</replaceable> ; ]
-      [ <command>nodata-per-second</command> <replaceable>number</replaceable> ; ]
-      [ <command>nxdomains-per-second</command> <replaceable>number</replaceable> ; ]
-      [ <command>errors-per-second</command> <replaceable>number</replaceable> ; ]
-      [ <command>all-per-second</command> <replaceable>number</replaceable> ; ]
-      [ <command>window</command> <replaceable>number</replaceable> ; ]
-      [ <command>log-only</command> <replaceable>yes_or_no</replaceable> ; ]
-      [ <command>qps-scale</command> <replaceable>number</replaceable> ; ]
-      [ <command>ipv4-prefix-length</command> <replaceable>number</replaceable> ; ]
-      [ <command>ipv6-prefix-length</command> <replaceable>number</replaceable> ; ]
-      [ <command>slip</command> <replaceable>number</replaceable> ; ]
-      [ <command>exempt-clients {</command> <replaceable>address_match_list</replaceable> <command>}</command> ; ]
-      [ <command>max-table-size</command> <replaceable>number</replaceable> ; ]
-      [ <command>min-table-size</command> <replaceable>number</replaceable> ; ]
-    <command>}</command> ; ]
-  [ <command>response-policy {</command>
-        <command>zone</command> <replaceable>zone_name</replaceable>
-      [ <command>policy</command> ( given | disabled | passthru | drop |
-                 tcp-only | nxdomain | nodata | cname <replaceable>domain</replaceable> ) ]
-      [ <command>recursive-only</command> <replaceable>yes_or_no</replaceable> ]
-      [ <command>log</command> <replaceable>yes_or_no</replaceable> ]
-      [ <command>max-policy-ttl</command> <replaceable>number</replaceable> ] ;
-         ...
-    <command>}</command> [ <command>recursive-only</command> <replaceable>yes_or_no</replaceable> ]
-      [ <command>max-policy-ttl</command> <replaceable>number</replaceable> ]
-      [ <command>break-dnssec</command> <replaceable>yes_or_no</replaceable> ]
-      [ <command>min-ns-dots</command> <replaceable>number</replaceable> ]
-      [ <command>nsip-wait-recurse</command> <replaceable>yes_or_no</replaceable> ]
-      [ <command>qname-wait-recurse</command> <replaceable>yes_or_no</replaceable> ] ; ]
-  [ <command>catalog-zones {</command>
-        <command>zone</command> <replaceable>quoted_string</replaceable>
-          [ <option>default-masters</option> [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] <command>{</command>
-              ( <replaceable>masters_list</replaceable> | <replaceable>ip_addr</replaceable> [<command>port</command> <replaceable>ip_port</replaceable>] [ <command>key</command> <replaceable>key_name</replaceable>] ) ;
-                ...
-            <command>}</command> ]
-          [ <command>zone-directory</command> <replaceable>path_name</replaceable> ]
-          [ <command>in-memory</command> <replaceable>yes_or_no</replaceable> ]
-          [ <command>min-update-interval</command> <replaceable>interval</replaceable> ] ;
-        ...
-    <command>}</command> ; ]
-  [ <command>v6-bias</command> <replaceable>number</replaceable> ; ]
-<command>}</command> ; ]
+<programlisting><command>options</command> {
+    <optional> attach-cache <replaceable>cache_name</replaceable>; </optional>
+    <optional> version <replaceable>version_string</replaceable>; </optional>
+    <optional> hostname <replaceable>hostname_string</replaceable>; </optional>
+    <optional> server-id <replaceable>server_id_string</replaceable>; </optional>
+    <optional> directory <replaceable>path_name</replaceable>; </optional>
+    <optional> dnstap { <replaceable>message_type</replaceable>; ... }; </optional>
+    <optional> dnstap-output ( <literal>file</literal> | <literal>unix</literal> ) <replaceable>path_name</replaceable>; </optional>
+    <optional> dnstap-identity ( <replaceable>string</replaceable> | <literal>hostname</literal> | <literal>none</literal> ); </optional>
+    <optional> dnstap-version ( <replaceable>string</replaceable> | <literal>none</literal> ); </optional>
+    <optional> fstrm-set-buffer-hint <replaceable>number</replaceable> ; </optional>
+    <optional> fstrm-set-flush-timeout <replaceable>number</replaceable> ; </optional>
+    <optional> fstrm-set-input-queue-size <replaceable>number</replaceable> ; </optional>
+    <optional> fstrm-set-output-notify-threshold <replaceable>number</replaceable> ; </optional>
+    <optional> fstrm-set-output-queue-model ( <replaceable>mpsc</replaceable> |
+                        <replaceable>spsc</replaceable> ) ; </optional>
+    <optional> fstrm-set-output-queue-size <replaceable>number</replaceable> ; </optional>
+    <optional> fstrm-set-reopen-interval <replaceable>number</replaceable> ; </optional>
+    <optional> geoip-directory <replaceable>path_name</replaceable>; </optional>
+    <optional> key-directory <replaceable>path_name</replaceable>; </optional>
+    <optional> managed-keys-directory <replaceable>path_name</replaceable>; </optional>
+    <optional> named-xfer <replaceable>path_name</replaceable>; </optional>
+    <optional> tkey-gssapi-keytab <replaceable>path_name</replaceable>; </optional>
+    <optional> tkey-gssapi-credential <replaceable>principal</replaceable>; </optional>
+    <optional> tkey-domain <replaceable>domainname</replaceable>; </optional>
+    <optional> tkey-dhkey <replaceable>key_name</replaceable> <replaceable>key_tag</replaceable>; </optional>
+    <optional> cache-file <replaceable>path_name</replaceable>; </optional>
+    <optional> dump-file <replaceable>path_name</replaceable>; </optional>
+    <optional> bindkeys-file <replaceable>path_name</replaceable>; </optional>
+    <optional> lock-file <replaceable>path_name</replaceable>; </optional>
+    <optional> secroots-file <replaceable>path_name</replaceable>; </optional>
+    <optional> session-keyfile <replaceable>path_name</replaceable>; </optional>
+    <optional> session-keyname <replaceable>key_name</replaceable>; </optional>
+    <optional> session-keyalg <replaceable>algorithm_id</replaceable>; </optional>
+    <optional> memstatistics <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> memstatistics-file <replaceable>path_name</replaceable>; </optional>
+    <optional> pid-file <replaceable>path_name</replaceable>; </optional>
+    <optional> recursing-file <replaceable>path_name</replaceable>; </optional>
+    <optional> statistics-file <replaceable>path_name</replaceable>; </optional>
+    <optional> zone-statistics <replaceable>full</replaceable> | <replaceable>terse</replaceable> | <replaceable>none</replaceable>; </optional>
+    <optional> auth-nxdomain <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> nxdomain-redirect <replaceable>string</replaceable>; </optional>
+    <optional> deallocate-on-exit <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> dialup <replaceable>dialup_option</replaceable>; </optional>
+    <optional> fake-iquery <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> fetch-glue <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> flush-zones-on-shutdown <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> has-old-clients <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> host-statistics <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> host-statistics-max <replaceable>number</replaceable>; </optional>
+    <optional> minimal-any <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> minimal-responses (<replaceable>yes_or_no</replaceable> | <constant>no-auth</constant> | <constant>no-auth-recursive</constant>); </optional>
+    <optional> multiple-cnames <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> notify <replaceable>yes_or_no</replaceable> | <replaceable>explicit</replaceable> | <replaceable>master-only</replaceable>; </optional>
+    <optional> recursion <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> send-cookie <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> require-server-cookie <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> cookie-algorithm <replaceable>algorithm_id</replaceable>; </optional>
+    <optional> cookie-secret <replaceable>secret_string</replaceable>; </optional>
+    <optional> nocookie-udp-size <replaceable>number</replaceable> ; </optional>
+    <optional> request-nsid <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> rfc2308-type1 <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> use-id-pool <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> maintain-ixfr-base <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> ixfr-from-differences (<replaceable>yes_or_no</replaceable> | <constant>master</constant> | <constant>slave</constant>); </optional>
+    <optional> auto-dnssec <constant>allow</constant>|<constant>maintain</constant>|<constant>off</constant>; </optional>
+    <optional> dnssec-enable <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> dnssec-validation (<replaceable>yes_or_no</replaceable> | <constant>auto</constant>); </optional>
+    <optional> dnssec-lookaside ( <replaceable>auto</replaceable> |
+                       <replaceable>no</replaceable> |
+                       <replaceable>domain</replaceable> trust-anchor <replaceable>domain</replaceable> ); </optional>
+    <optional> dnssec-must-be-secure <replaceable>domain yes_or_no</replaceable>; </optional>
+    <optional> dnssec-accept-expired <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> forward ( <replaceable>only</replaceable> | <replaceable>first</replaceable> ); </optional>
+    <optional> forwarders { <optional> <replaceable>ip_addr</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> ; ... </optional> }; </optional>
+    <optional> dual-stack-servers <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> {
+       ( <replaceable>domain_name</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> |
+         <replaceable>ip_addr</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional>) ;
+       ... }; </optional>
+    <optional> check-names ( <replaceable>master</replaceable> | <replaceable>slave</replaceable> | <replaceable>response</replaceable> )
+       ( <replaceable>warn</replaceable> | <replaceable>fail</replaceable> | <replaceable>ignore</replaceable> ); </optional>
+    <optional> check-dup-records ( <replaceable>warn</replaceable> | <replaceable>fail</replaceable> | <replaceable>ignore</replaceable> ); </optional>
+    <optional> check-mx ( <replaceable>warn</replaceable> | <replaceable>fail</replaceable> | <replaceable>ignore</replaceable> ); </optional>
+    <optional> check-wildcard <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> check-integrity <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> check-mx-cname ( <replaceable>warn</replaceable> | <replaceable>fail</replaceable> | <replaceable>ignore</replaceable> ); </optional>
+    <optional> check-srv-cname ( <replaceable>warn</replaceable> | <replaceable>fail</replaceable> | <replaceable>ignore</replaceable> ); </optional>
+    <optional> check-sibling <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> check-spf ( <replaceable>warn</replaceable> | <replaceable>ignore</replaceable> ); </optional>
+    <optional> allow-new-zones { <replaceable>yes_or_no</replaceable> }; </optional>
+    <optional> allow-notify { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-query { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-query-on { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-query-cache { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-query-cache-on { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-transfer { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-recursion { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-recursion-on { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-update { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> allow-update-forwarding { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> automatic-interface-scan { <replaceable>yes_or_no</replaceable> }; </optional>
+    <optional> geoip-use-ecs <replaceable>yes_or_no</replaceable>;</optional>
+    <optional> update-check-ksk <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> dnssec-update-mode ( <replaceable>maintain</replaceable> | <replaceable>no-resign</replaceable> ); </optional>
+    <optional> dnssec-dnskey-kskonly <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> dnssec-loadkeys-interval <replaceable>number</replaceable>; </optional>
+    <optional> dnssec-secure-to-insecure <replaceable>yes_or_no</replaceable> ;</optional>
+    <optional> try-tcp-refresh <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> allow-v6-synthesis { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> blackhole { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> keep-response-order { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> no-case-compress { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> message-compression <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> use-v4-udp-ports { <replaceable>port_list</replaceable> }; </optional>
+    <optional> avoid-v4-udp-ports { <replaceable>port_list</replaceable> }; </optional>
+    <optional> use-v6-udp-ports { <replaceable>port_list</replaceable> }; </optional>
+    <optional> avoid-v6-udp-ports { <replaceable>port_list</replaceable> }; </optional>
+    <optional> listen-on <optional> port <replaceable>ip_port</replaceable> </optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> listen-on-v6 <optional> port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional>
+{ <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> query-source ( ( <replaceable>ip4_addr</replaceable> | <replaceable>*</replaceable> )
+       <optional> port ( <replaceable>ip_port</replaceable> | <replaceable>*</replaceable> ) </optional>
+       <optional> dscp <replaceable>ip_dscp</replaceable></optional> |
+       <optional> address ( <replaceable>ip4_addr</replaceable> | <replaceable>*</replaceable> ) </optional>
+       <optional> port ( <replaceable>ip_port</replaceable> | <replaceable>*</replaceable> ) </optional> )
+       <optional> dscp <replaceable>ip_dscp</replaceable></optional> ; </optional>
+    <optional> query-source-v6 ( ( <replaceable>ip6_addr</replaceable> | <replaceable>*</replaceable> )
+       <optional> port ( <replaceable>ip_port</replaceable> | <replaceable>*</replaceable> ) </optional>
+       <optional> dscp <replaceable>ip_dscp</replaceable></optional> |
+       <optional> address ( <replaceable>ip6_addr</replaceable> | <replaceable>*</replaceable> ) </optional>
+       <optional> port ( <replaceable>ip_port</replaceable> | <replaceable>*</replaceable> ) </optional> )
+       <optional> dscp <replaceable>ip_dscp</replaceable></optional> ; </optional>
+    <optional> use-queryport-pool <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> queryport-pool-ports <replaceable>number</replaceable>; </optional>
+    <optional> queryport-pool-updateinterval <replaceable>number</replaceable>; </optional>
+    <optional> max-transfer-time-in <replaceable>number</replaceable>; </optional>
+    <optional> max-transfer-time-out <replaceable>number</replaceable>; </optional>
+    <optional> max-transfer-idle-in <replaceable>number</replaceable>; </optional>
+    <optional> max-transfer-idle-out <replaceable>number</replaceable>; </optional>
+    <optional> reserved-sockets <replaceable>number</replaceable>; </optional>
+    <optional> recursive-clients <replaceable>number</replaceable>; </optional>
+    <optional> tcp-clients <replaceable>number</replaceable>; </optional>
+    <optional> clients-per-query <replaceable>number</replaceable> ; </optional>
+    <optional> max-clients-per-query <replaceable>number</replaceable> ; </optional>
+    <optional> fetches-per-server <replaceable>number</replaceable> <optional><replaceable>(drop | fail)</replaceable></optional>; </optional>
+    <optional> fetch-quota-params <replaceable>number fixedpoint fixedpoint fixedpoint</replaceable> ; </optional>
+    <optional> fetches-per-zone <replaceable>number</replaceable> <optional><replaceable>(drop | fail)</replaceable></optional>; </optional>
+    <optional> notify-rate <replaceable>number</replaceable>; </optional>
+    <optional> startup-notify-rate <replaceable>number</replaceable>; </optional>
+    <optional> serial-query-rate <replaceable>number</replaceable>; </optional>
+    <optional> serial-queries <replaceable>number</replaceable>; </optional>
+    <optional> tcp-listen-queue <replaceable>number</replaceable>; </optional>
+    <optional> transfer-format <replaceable>( one-answer | many-answers )</replaceable>; </optional>
+    <optional> transfer-message-size  <replaceable>number</replaceable>; </optional>
+    <optional> transfers-in  <replaceable>number</replaceable>; </optional>
+    <optional> transfers-out <replaceable>number</replaceable>; </optional>
+    <optional> transfers-per-ns <replaceable>number</replaceable>; </optional>
+    <optional> transfer-source (<replaceable>ip4_addr</replaceable> | <constant>*</constant>) <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> ; </optional>
+    <optional> transfer-source-v6 (<replaceable>ip6_addr</replaceable> | <constant>*</constant>) <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> ; </optional>
+    <optional> alt-transfer-source (<replaceable>ip4_addr</replaceable> | <constant>*</constant>) <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> ; </optional>
+    <optional> alt-transfer-source-v6 (<replaceable>ip6_addr</replaceable> | <constant>*</constant>) <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> ; </optional>
+    <optional> use-alt-transfer-source <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> notify-delay <replaceable>seconds</replaceable> ; </optional>
+    <optional> notify-source (<replaceable>ip4_addr</replaceable> | <constant>*</constant>) <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> ; </optional>
+    <optional> notify-source-v6 (<replaceable>ip6_addr</replaceable> | <constant>*</constant>) <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> ; </optional>
+    <optional> notify-to-soa <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> also-notify <optional>port <replaceable>ip_port</replaceable></optional> <optional>dscp <replaceable>ip_dscp</replaceable></optional> { ( <replaceable>masters</replaceable> | <replaceable>ip_addr</replaceable>
+                   <optional>port <replaceable>ip_port</replaceable></optional> ) <optional>key <replaceable>keyname</replaceable></optional> ; ... }; </optional>
+    <optional> max-ixfr-log-size <replaceable>number</replaceable>; </optional>
+    <optional> max-journal-size <replaceable>size_spec</replaceable>; </optional>
+    <optional> coresize <replaceable>size_spec</replaceable> ; </optional>
+    <optional> datasize <replaceable>size_spec</replaceable> ; </optional>
+    <optional> files <replaceable>size_spec</replaceable> ; </optional>
+    <optional> stacksize <replaceable>size_spec</replaceable> ; </optional>
+    <optional> cleaning-interval <replaceable>number</replaceable>; </optional>
+    <optional> heartbeat-interval <replaceable>number</replaceable>; </optional>
+    <optional> interface-interval <replaceable>number</replaceable>; </optional>
+    <optional> statistics-interval <replaceable>number</replaceable>; </optional>
+    <optional> topology { <replaceable>address_match_list</replaceable> }</optional>;
+    <optional> sortlist { <replaceable>address_match_list</replaceable> }</optional>;
+    <optional> rrset-order { <replaceable>order_spec</replaceable> ; <optional> <replaceable>order_spec</replaceable> ; ... </optional> </optional> };
+    <optional> lame-ttl <replaceable>number</replaceable>; </optional>
+    <optional> max-ncache-ttl <replaceable>number</replaceable>; </optional>
+    <optional> max-cache-ttl <replaceable>number</replaceable>; </optional>
+    <optional> max-zone-ttl ( <constant>unlimited</constant> | <replaceable>number</replaceable> ; </optional>
+    <optional> serial-update-method <constant>increment</constant>|<constant>unixtime</constant>|<constant>date</constant>; </optional>
+    <optional> servfail-ttl <replaceable>number</replaceable>; </optional>
+    <optional> sig-validity-interval <replaceable>number</replaceable> <optional><replaceable>number</replaceable></optional> ; </optional>
+    <optional> sig-signing-nodes <replaceable>number</replaceable> ; </optional>
+    <optional> sig-signing-signatures <replaceable>number</replaceable> ; </optional>
+    <optional> sig-signing-type <replaceable>number</replaceable> ; </optional>
+    <optional> min-roots <replaceable>number</replaceable>; </optional>
+    <optional> use-ixfr <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> provide-ixfr <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> request-ixfr <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> request-expire <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> treat-cr-as-space <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> min-refresh-time <replaceable>number</replaceable> ; </optional>
+    <optional> max-refresh-time <replaceable>number</replaceable> ; </optional>
+    <optional> min-retry-time <replaceable>number</replaceable> ; </optional>
+    <optional> max-retry-time <replaceable>number</replaceable> ; </optional>
+    <optional> nta-lifetime <replaceable>duration</replaceable> ; </optional>
+    <optional> nta-recheck <replaceable>duration</replaceable> ; </optional>
+    <optional> port <replaceable>ip_port</replaceable>; </optional>
+    <optional> dscp <replaceable>ip_dscp</replaceable></optional> ;
+    <optional> additional-from-auth <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> additional-from-cache <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> random-device <replaceable>path_name</replaceable> ; </optional>
+    <optional> max-cache-size <replaceable>size_or_percent</replaceable> ; </optional>
+    <optional> match-mapped-addresses <replaceable>yes_or_no</replaceable>; </optional>
+    <optional> filter-aaaa-on-v4 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
+    <optional> filter-aaaa-on-v6 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
+    <optional> filter-aaaa { <replaceable>address_match_list</replaceable> }; </optional>
+    <optional> dns64 <replaceable>ipv6-prefix</replaceable> {
+       <optional> clients { <replaceable>address_match_list</replaceable> }; </optional>
+       <optional> mapped { <replaceable>address_match_list</replaceable> }; </optional>
+       <optional> exclude { <replaceable>address_match_list</replaceable> }; </optional>
+       <optional> suffix <replaceable>IPv6-address</replaceable>; </optional>
+       <optional> recursive-only <replaceable>yes_or_no</replaceable>; </optional>
+       <optional> break-dnssec <replaceable>yes_or_no</replaceable>; </optional>
+    }; </optional>;
+    <optional> dns64-server <replaceable>name</replaceable> </optional>
+    <optional> dns64-contact <replaceable>name</replaceable> </optional>
+    <optional> preferred-glue ( <replaceable>A</replaceable> | <replaceable>AAAA</replaceable> | <replaceable>NONE</replaceable> ); </optional>
+    <optional> edns-udp-size <replaceable>number</replaceable>; </optional>
+    <optional> max-udp-size <replaceable>number</replaceable>; </optional>
+    <optional> max-rsa-exponent-size <replaceable>number</replaceable>; </optional>
+    <optional> root-delegation-only <optional> exclude { <replaceable>namelist</replaceable> } </optional> ; </optional>
+    <optional> querylog <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> disable-algorithms <replaceable>domain</replaceable> { <replaceable>algorithm</replaceable>;
+                               <optional> <replaceable>algorithm</replaceable>; </optional> }; </optional>
+    <optional> disable-ds-digests <replaceable>domain</replaceable> { <replaceable>digest_type</replaceable>;
+                               <optional> <replaceable>digest_type</replaceable>; </optional> }; </optional>
+    <optional> acache-enable <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> acache-cleaning-interval <replaceable>number</replaceable>; </optional>
+    <optional> max-acache-size <replaceable>size_spec</replaceable> ; </optional>
+    <optional> max-recursion-depth <replaceable>number</replaceable> ; </optional>
+    <optional> max-recursion-queries <replaceable>number</replaceable> ; </optional>
+    <optional> masterfile-format
+           (<constant>text</constant>|<constant>raw</constant>|<constant>map</constant>) ; </optional>
+    <optional> masterfile-style
+           (<constant>relative</constant>|<constant>full</constant>) ; </optional>
+    <optional> empty-server <replaceable>name</replaceable> ; </optional>
+    <optional> empty-contact <replaceable>name</replaceable> ; </optional>
+    <optional> empty-zones-enable <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> disable-empty-zone <replaceable>zone_name</replaceable> ; </optional>
+    <optional> zero-no-soa-ttl <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> zero-no-soa-ttl-cache <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> resolver-query-timeout <replaceable>number</replaceable> ; </optional>
+    <optional> deny-answer-addresses { <replaceable>address_match_list</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
+    <optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
+    <optional> prefetch <replaceable>number</replaceable> <optional><replaceable>number</replaceable></optional> ; </optional>
+
+    <optional> rate-limit {
+       <optional> responses-per-second <replaceable>number</replaceable> ; </optional>
+       <optional> referrals-per-second <replaceable>number</replaceable> ; </optional>
+       <optional> nodata-per-second <replaceable>number</replaceable> ; </optional>
+       <optional> nxdomains-per-second <replaceable>number</replaceable> ; </optional>
+       <optional> errors-per-second <replaceable>number</replaceable> ; </optional>
+       <optional> all-per-second <replaceable>number</replaceable> ; </optional>
+       <optional> window <replaceable>number</replaceable> ; </optional>
+       <optional> log-only <replaceable>yes_or_no</replaceable> ; </optional>
+       <optional> qps-scale <replaceable>number</replaceable> ; </optional>
+       <optional> ipv4-prefix-length <replaceable>number</replaceable> ; </optional>
+       <optional> ipv6-prefix-length <replaceable>number</replaceable> ; </optional>
+       <optional> slip <replaceable>number</replaceable> ; </optional>
+       <optional> exempt-clients  { <replaceable>address_match_list</replaceable> } ; </optional>
+       <optional> max-table-size <replaceable>number</replaceable> ; </optional>
+       <optional> min-table-size <replaceable>number</replaceable> ; </optional>
+    } ; </optional>
+    <optional> response-policy {
+       zone <replaceable>zone_name</replaceable>
+       <optional> policy <replaceable>(given | disabled | passthru | drop |
+                 tcp-only | nxdomain | nodata | cname domain</replaceable>) </optional>
+       <optional> recursive-only <replaceable>yes_or_no</replaceable> </optional>
+       <optional> log <replaceable>yes_or_no</replaceable> </optional>
+       <optional> max-policy-ttl <replaceable>number</replaceable> </optional>
+       <optional> min-update-interval <replaceable>number</replaceable> </optional>
+       ; <optional>...</optional>
+    } <optional> recursive-only <replaceable>yes_or_no</replaceable> </optional>
+      <optional> max-policy-ttl <replaceable>number</replaceable> </optional>
+      <optional> min-update-interval <replaceable>number</replaceable> </optional>
+      <optional> break-dnssec <replaceable>yes_or_no</replaceable> </optional>
+      <optional> min-ns-dots <replaceable>number</replaceable> </optional>
+      <optional> nsip-wait-recurse <replaceable>yes_or_no</replaceable> </optional>
+      <optional> qname-wait-recurse <replaceable>yes_or_no</replaceable> </optional>
+      <optional> automatic-interface-scan <replaceable>yes_or_no</replaceable> </optional>
+    ; </optional>
+    <optional> catalog-zones {
+        zone <replaceable>quoted_string</replaceable>
+            <optional> default-masters
+               <optional>port <replaceable>ip_port</replaceable></optional>
+               <optional>dscp <replaceable>ip_dscp</replaceable></optional>
+               { ( <replaceable>masters_list</replaceable> | <replaceable>ip_addr</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> <optional>key <replaceable>key</replaceable></optional> ) ; <optional>...</optional> }</optional>
+         <optional>in-memory <replaceable>yes_or_no</replaceable></optional>
+         <optional>min-update-interval <replaceable>interval</replaceable></optional>
+       ; <optional>...</optional> };
+    ; </optional>
+    <optional>v6-bias <replaceable>number</replaceable> ; </optional>
+};
 </programlisting>
 
       </section>
@@ -10735,6 +10736,15 @@ example.com                 CNAME   rpz-tcp-only.
            turn off rewrite logging for a particular response policy
            zone. By default, all rewrites are logged.
          </para>
+
+         <para>
+           Updates to RPZ zones are processed asynchronously; if there
+           is more than one update pending they are bundled together.
+           If an update to a RPZ zone (for example, via IXFR) happens less
+           than <option>min-update-interval</option> seconds after the most
+           recent update, then the changes will not be carried out until this
+           interval has elapsed.  The default is <literal>5</literal> seconds.
+         </para>
        </section>
 
        <section xml:id="rrl"><info><title>Response Rate Limiting</title></info>
index 298beb3510fc923729d7c8b2a512bf69dd64c534..57f563844c41f17d23e895c57448bd75545f3d20 100644 (file)
     <itemizedlist>
       <listitem>
        <para>
+         The Response Policy Zone (RPZ) implementation has been
+         substantially refactored: updates to the RPZ summary
+         database are no longer directly performed by the zone
+         database but by a separate function that is called when
+         a policy zone is updated.  This improves both performance
+         and reliability when policy zones receive frequent updates.
+         Summary database updates can be rate-limited by using the
+         <command>min-update-interval</command> option in a
+         <command>response-policy</command> statement. [RT #43449]
+       </para>
+      </listitem>
+      <listitem>
+        <para>
          <command>dnstap</command> now stores both the local and remote
          addresses for all messages, instead of only the remote address.
          The default output format for <command>dnstap-read</command> has
index b32e37a7c968df3f98c64021d094b7c37a218f0e..c697de264fd0c38a4c54298cbaa3d56aa26e22e6 100644 (file)
@@ -302,10 +302,11 @@ options {
         response-padding { <address_match_element>; ... } block-size
             <integer>;
         response-policy { zone <quoted_string> [ log <boolean> ] [
-            max-policy-ttl <integer> ] [ policy ( cname | disabled | drop |
-            given | no-op | nodata | nxdomain | passthru | tcp-only
-            <quoted_string> ) ] [ recursive-only <boolean> ]; ... } [
-            break-dnssec <boolean> ] [ max-policy-ttl <integer> ] [
+            max-policy-ttl <integer> ] [ min-update-interval <integer> ] [
+            policy ( cname | disabled | drop | given | no-op | nodata | 
+            nxdomain | passthru | tcp-only <quoted_string> ) ] [
+            recursive-only <boolean> ]; ... } [ break-dnssec <boolean> ] [
+            max-policy-ttl <integer> ] [ min-update-interval <integer> ] [
             min-ns-dots <integer> ] [ nsip-wait-recurse <boolean> ] [
             qname-wait-recurse <boolean> ] [ recursive-only <boolean> ];
         rfc2308-type1 <boolean>; // not yet implemented
@@ -610,10 +611,11 @@ view <string> [ <class> ] {
         response-padding { <address_match_element>; ... } block-size
             <integer>;
         response-policy { zone <quoted_string> [ log <boolean> ] [
-            max-policy-ttl <integer> ] [ policy ( cname | disabled | drop |
-            given | no-op | nodata | nxdomain | passthru | tcp-only
-            <quoted_string> ) ] [ recursive-only <boolean> ]; ... } [
-            break-dnssec <boolean> ] [ max-policy-ttl <integer> ] [
+            max-policy-ttl <integer> ] [ min-update-interval <integer> ] [
+            policy ( cname | disabled | drop | given | no-op | nodata |
+            nxdomain | passthru | tcp-only | <quoted_string> ) ] [
+            recursive-only <boolean> ]; ... } [ break-dnssec <boolean> ] [
+            max-policy-ttl <integer> ] [ min-update-interval <integer> ] [
             min-ns-dots <integer> ] [ nsip-wait-recurse <boolean> ] [
             qname-wait-recurse <boolean> ] [ recursive-only <boolean> ];
         rfc2308-type1 <boolean>; // not yet implemented
index c707e94380282ec9fbd84b9c7d744581d8e07580..c0d649ab928d6911892d40f5a76dd959cf6b67ce 100644 (file)
@@ -1048,7 +1048,7 @@ dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset,
  * it is dealing with a database that understands response policy zones.
  */
 void
-dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+dns_db_rpz_attach(dns_db_t *db, void *rpzs, isc_uint8_t rpz_num) {
        REQUIRE(db->methods->rpz_attach != NULL);
        (db->methods->rpz_attach)(db, rpzs, rpz_num);
 }
index 44da8fa3ecd306445b3c7995d6c4ec4d6693c53e..e6a73a0b340b0cfe0ec59f9df22f873909423dcd 100644 (file)
@@ -56,7 +56,6 @@
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataset.h>
-#include <dns/rpz.h>
 #include <dns/types.h>
 
 ISC_LANG_BEGINDECLS
@@ -167,8 +166,8 @@ typedef struct dns_dbmethods {
                                           dns_dbversion_t *version);
        isc_boolean_t   (*isdnssec)(dns_db_t *db);
        dns_stats_t     *(*getrrsetstats)(dns_db_t *db);
-       void            (*rpz_attach)(dns_db_t *db, dns_rpz_zones_t *rpzs,
-                                     dns_rpz_num_t rpz_num);
+       void            (*rpz_attach)(dns_db_t *db, void *rpzs,
+                                     isc_uint8_t rpz_num);
        isc_result_t    (*rpz_ready)(dns_db_t *db);
        isc_result_t    (*findnodeext)(dns_db_t *db, const dns_name_t *name,
                                     isc_boolean_t create,
@@ -1635,14 +1634,16 @@ dns_db_setcachestats(dns_db_t *db, isc_stats_t *stats);
  */
 
 void
-dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
+dns_db_rpz_attach(dns_db_t *db, void *rpzs, isc_uint8_t rpz_num)
+       ISC_DEPRECATED;
 /*%<
  * Attach the response policy information for a view to a database for a
  * zone for the view.
  */
 
 isc_result_t
-dns_db_rpz_ready(dns_db_t *db);
+dns_db_rpz_ready(dns_db_t *db)
+       ISC_DEPRECATED;
 /*%<
  * Finish loading a response policy zone.
  */
index 2023f01e98beef99b5ecc2aa14ffda26b8299708..91249b7f050180c3238f3f5c16bc8d13ff8fbd4f 100644 (file)
@@ -75,6 +75,7 @@
 #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_RPZUPDATED                   (ISC_EVENTCLASS_DNS + 57)
 
 #define DNS_EVENT_FIRSTEVENT                   (ISC_EVENTCLASS_DNS + 0)
 #define DNS_EVENT_LASTEVENT                    (ISC_EVENTCLASS_DNS + 65535)
index d7350a937964c957ca05824002cdb390562c0458..baf4eeb3361c67c5fa48a3c2648e0a4fb51b408a 100644 (file)
 #include <isc/lang.h>
 #include <isc/refcount.h>
 #include <isc/rwlock.h>
+#include <isc/ht.h>
+#include <isc/time.h>
+#include <isc/event.h>
+#include <isc/timer.h>
 
 #include <dns/fixedname.h>
 #include <dns/rdata.h>
@@ -37,7 +41,6 @@ ISC_LANG_BEGINDECLS
 #define DNS_RPZ_DROP_NAME      DNS_RPZ_PREFIX"drop"
 #define DNS_RPZ_TCP_ONLY_NAME  DNS_RPZ_PREFIX"tcp-only"
 
-
 typedef isc_uint8_t            dns_rpz_prefix_t;
 
 typedef enum {
@@ -118,20 +121,38 @@ struct dns_rpz_triggers {
  * A single response policy zone.
  */
 typedef struct dns_rpz_zone dns_rpz_zone_t;
+typedef struct dns_rpz_zones dns_rpz_zones_t;
+
 struct dns_rpz_zone {
-       isc_refcount_t  refs;
-       dns_rpz_num_t   num;            /* ordinal in list of policy zones */
-       dns_name_t      origin;         /* Policy zone name */
-       dns_name_t      client_ip;      /* DNS_RPZ_CLIENT_IP_ZONE.origin. */
-       dns_name_t      ip;             /* DNS_RPZ_IP_ZONE.origin. */
-       dns_name_t      nsdname;        /* DNS_RPZ_NSDNAME_ZONE.origin */
-       dns_name_t      nsip;           /* DNS_RPZ_NSIP_ZONE.origin. */
-       dns_name_t      passthru;       /* DNS_RPZ_PASSTHRU_NAME. */
-       dns_name_t      drop;           /* DNS_RPZ_DROP_NAME. */
-       dns_name_t      tcp_only;       /* DNS_RPZ_TCP_ONLY_NAME. */
-       dns_name_t      cname;          /* override value for ..._CNAME */
-       dns_ttl_t       max_policy_ttl;
+       isc_refcount_t   refs;
+       dns_rpz_num_t    num;           /* ordinal in list of policy zones */
+       dns_name_t       origin;        /* Policy zone name */
+       dns_name_t       client_ip;     /* DNS_RPZ_CLIENT_IP_ZONE.origin. */
+       dns_name_t       ip;            /* DNS_RPZ_IP_ZONE.origin. */
+       dns_name_t       nsdname;       /* DNS_RPZ_NSDNAME_ZONE.origin */
+       dns_name_t       nsip;          /* DNS_RPZ_NSIP_ZONE.origin. */
+       dns_name_t       passthru;      /* DNS_RPZ_PASSTHRU_NAME. */
+       dns_name_t       drop;          /* DNS_RPZ_DROP_NAME. */
+       dns_name_t       tcp_only;      /* DNS_RPZ_TCP_ONLY_NAME. */
+       dns_name_t       cname;         /* override value for ..._CNAME */
+       dns_ttl_t        max_policy_ttl;
        dns_rpz_policy_t policy;        /* DNS_RPZ_POLICY_GIVEN or override */
+
+       isc_uint32_t     min_update_int;/* minimal interval between updates */
+       isc_ht_t         *nodes;        /* entries in zone */
+       dns_rpz_zones_t  *rpzs;         /* owner */
+       isc_time_t       lastupdated;   /* last time the zone was processed */
+       isc_boolean_t    updatepending; /* there is an update pending/waiting */
+       isc_boolean_t    updaterunning; /* there is an update running */
+       dns_db_t         *db;           /* zones database */
+       dns_dbversion_t  *dbversion;    /* version we will be updating to */
+       dns_db_t         *updb;         /* zones database we're working on */
+       dns_dbversion_t  *updbversion;  /* version we're currently working on */
+       dns_dbiterator_t *updbit;       /* iterator to use when updating */
+       isc_ht_t         *newnodes;     /* entries in zone being updated */
+       isc_boolean_t    db_registered; /* is the notify event registered? */
+       isc_timer_t      *updatetimer;
+       isc_event_t      updateevent;
 };
 
 /*
@@ -176,7 +197,6 @@ struct dns_rpz_popt {
 /*
  * Response policy zones known to a view.
  */
-typedef struct dns_rpz_zones dns_rpz_zones_t;
 struct dns_rpz_zones {
        dns_rpz_popt_t          p;
        dns_rpz_zone_t          *zones[DNS_RPZ_MAX_ZONES];
@@ -215,6 +235,9 @@ struct dns_rpz_zones {
        dns_rpz_triggers_t      total_triggers;
 
        isc_mem_t               *mctx;
+       isc_taskmgr_t           *taskmgr;
+       isc_timermgr_t          *timermgr;
+       isc_task_t              *updater;
        isc_refcount_t          refs;
        /*
         * One lock for short term read-only search that guarantees the
@@ -311,6 +334,7 @@ typedef struct {
 
 #define DNS_RPZ_TTL_DEFAULT            5
 #define DNS_RPZ_MAX_TTL_DEFAULT                DNS_RPZ_TTL_DEFAULT
+#define DNS_RPZ_MINUPDATEINT_DEF       60
 
 /*
  * So various response policy zone messages can be turned up or down.
@@ -336,7 +360,14 @@ dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
                     dns_name_t *selfname);
 
 isc_result_t
-dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx);
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
+                 isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr);
+
+isc_result_t
+dns_rpz_new_zone(dns_rpz_zones_t *rpzs, dns_rpz_zone_t **rpzp);
+
+isc_result_t
+dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg);
 
 void
 dns_rpz_attach_rpzs(dns_rpz_zones_t *source, dns_rpz_zones_t **target);
@@ -346,11 +377,13 @@ dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp);
 
 isc_result_t
 dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
-                 dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
+                 dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num)
+       ISC_DEPRECATED;
 
 isc_result_t
 dns_rpz_ready(dns_rpz_zones_t *rpzs,
-             dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num);
+             dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num)
+       ISC_DEPRECATED;
 
 isc_result_t
 dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
index 278b6f8aa17d07e6a1fde589f624239db44457d4..c8f0167ee1e8652b3bd2eb71e42525d9559b89a4 100644 (file)
@@ -53,7 +53,6 @@
 #include <dns/nsec.h>
 #include <dns/nsec3.h>
 #include <dns/rbt.h>
-#include <dns/rpz.h>
 #include <dns/rdata.h>
 #include <dns/rdataset.h>
 #include <dns/rdatasetiter.h>
@@ -251,8 +250,6 @@ typedef isc_uint64_t                    rbtdb_serial_t;
 #define resign_insert resign_insert64
 #define resign_sooner resign_sooner64
 #define resigned resigned64
-#define rpz_attach rpz_attach64
-#define rpz_ready rpz_ready64
 #define serialize serialize64
 #define set_index set_index64
 #define set_ttl set_ttl64
@@ -678,9 +675,6 @@ struct dns_rbtdb {
        dns_rbt_t *                     tree;
        dns_rbt_t *                     nsec;
        dns_rbt_t *                     nsec3;
-       dns_rpz_zones_t                 *rpzs;
-       dns_rpz_num_t                   rpz_num;
-       dns_rpz_zones_t                 *load_rpzs;
 
        /* Unlocked */
        unsigned int                    quantum;
@@ -1296,19 +1290,6 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
        if (rbtdb->cachestats != NULL)
                isc_stats_detach(&rbtdb->cachestats);
 
-       if (rbtdb->load_rpzs != NULL) {
-               /*
-                * We must be cleaning up after a failed zone loading.
-                */
-               REQUIRE(rbtdb->rpzs != NULL &&
-                       rbtdb->rpz_num < rbtdb->rpzs->p.num_zones);
-               dns_rpz_detach_rpzs(&rbtdb->load_rpzs);
-       }
-       if (rbtdb->rpzs != NULL) {
-               REQUIRE(rbtdb->rpz_num < rbtdb->rpzs->p.num_zones);
-               dns_rpz_detach_rpzs(&rbtdb->rpzs);
-       }
-
        isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks,
                    rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t));
        isc_rwlock_destroy(&rbtdb->tree_lock);
@@ -1924,7 +1905,6 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
        dns_fixedname_t fname;
        dns_name_t *name;
        isc_result_t result = ISC_R_UNEXPECTED;
-       unsigned int node_has_rpz;
 
        INSIST(!ISC_LINK_LINKED(node, deadlink));
 
@@ -1951,11 +1931,7 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
                name = dns_fixedname_name(&fname);
                dns_rbt_fullnamefromnode(node, name);
 
-               node_has_rpz = node->rpz;
                result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
-               if (result == ISC_R_SUCCESS &&
-                   rbtdb->rpzs != NULL && node_has_rpz)
-                       dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
                break;
        case DNS_RBT_NSEC_HAS_NSEC:
                dns_fixedname_init(&fname);
@@ -1988,11 +1964,7 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
                                              isc_result_totext(result));
                        }
                }
-               node_has_rpz = node->rpz;
                result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
-               if (result == ISC_R_SUCCESS &&
-                   rbtdb->rpzs != NULL && node_has_rpz)
-                       dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
                break;
        case DNS_RBT_NSEC_NSEC:
                result = dns_rbt_deletenode(rbtdb->nsec, node, ISC_FALSE);
@@ -3010,34 +2982,6 @@ findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, const dns_name_t *name,
 
        reactivate_node(rbtdb, node, locktype);
 
-       /*
-        * Always try to add the policy zone data, because this node might
-        * already have been implicitly created by the previous addition of
-        * a longer domain.  A common example is adding *.example.com
-        * (implicitly creating example.com) followed by explicitly adding
-        * example.com.
-        */
-       if (create && rbtdb->rpzs != NULL && tree == rbtdb->tree) {
-               dns_fixedname_t fnamef;
-               dns_name_t *fname;
-
-               dns_fixedname_init(&fnamef);
-               fname = dns_fixedname_name(&fnamef);
-               dns_rbt_fullnamefromnode(node, fname);
-               result = dns_rpz_add(rbtdb->rpzs, rbtdb->rpz_num, fname);
-               if (result == ISC_R_SUCCESS)
-                       node->rpz = 1;
-               if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
-                       /*
-                        * It is too late to give up, so merely complain.
-                        */
-                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
-                                     DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
-                                     "dns_rpz_add(): %s",
-                                     isc_result_totext(result));
-               }
-       }
-
        RWUNLOCK(&rbtdb->tree_lock, locktype);
 
        *nodep = (dns_dbnode_t *)node;
@@ -4944,45 +4888,6 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
        return (result);
 }
 
-/*
- * Connect this RBTDB to the response policy zone summary data for the view.
- */
-static void
-rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
-       dns_rbtdb_t * rbtdb;
-
-       rbtdb = (dns_rbtdb_t *)db;
-       REQUIRE(VALID_RBTDB(rbtdb));
-
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
-       REQUIRE(rbtdb->rpzs == NULL && rbtdb->rpz_num == DNS_RPZ_INVALID_NUM);
-       dns_rpz_attach_rpzs(rpzs, &rbtdb->rpzs);
-       rbtdb->rpz_num = rpz_num;
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
-}
-
-/*
- * Enable this RBTDB as a response policy zone.
- */
-static isc_result_t
-rpz_ready(dns_db_t *db) {
-       dns_rbtdb_t * rbtdb;
-       isc_result_t result;
-
-       rbtdb = (dns_rbtdb_t *)db;
-       REQUIRE(VALID_RBTDB(rbtdb));
-
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
-       if (rbtdb->rpzs == NULL) {
-               INSIST(rbtdb->rpz_num == DNS_RPZ_INVALID_NUM);
-               result = ISC_R_SUCCESS;
-       } else {
-               result = dns_rpz_ready(rbtdb->rpzs, &rbtdb->load_rpzs,
-                                      rbtdb->rpz_num);
-       }
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
-       return (result);
-}
 
 static isc_result_t
 cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
@@ -7127,35 +7032,10 @@ static isc_result_t
 loadnode(dns_rbtdb_t *rbtdb, const dns_name_t *name, dns_rbtnode_t **nodep,
         isc_boolean_t hasnsec)
 {
-       isc_result_t noderesult, rpzresult, nsecresult, tmpresult;
+       isc_result_t noderesult, nsecresult, tmpresult;
        dns_rbtnode_t *nsecnode = NULL, *node = NULL;
 
        noderesult = dns_rbt_addnode(rbtdb->tree, name, &node);
-       if (rbtdb->rpzs != NULL &&
-           (noderesult == ISC_R_SUCCESS || noderesult == ISC_R_EXISTS)) {
-               rpzresult = dns_rpz_add(rbtdb->load_rpzs, rbtdb->rpz_num,
-                                       name);
-               if (rpzresult == ISC_R_SUCCESS) {
-                       node->rpz = 1;
-               } else if (noderesult != ISC_R_EXISTS) {
-                       /*
-                        * Remove the node we just added above.
-                        */
-                       tmpresult = dns_rbt_deletenode(rbtdb->tree, node,
-                                                      ISC_FALSE);
-                       if (tmpresult != ISC_R_SUCCESS)
-                               isc_log_write(dns_lctx,
-                                             DNS_LOGCATEGORY_DATABASE,
-                                             DNS_LOGMODULE_CACHE,
-                                             ISC_LOG_WARNING,
-                                             "loading_addrdataset: "
-                                             "dns_rbt_deletenode: %s after "
-                                             "dns_rbt_addnode(NSEC): %s",
-                                             isc_result_totext(tmpresult),
-                                             isc_result_totext(ISC_R_SUCCESS));
-                       noderesult = rpzresult;
-               }
-       }
        if (!hasnsec)
                goto done;
        if (noderesult == ISC_R_EXISTS) {
@@ -7196,21 +7076,12 @@ loadnode(dns_rbtdb_t *rbtdb, const dns_name_t *name, dns_rbtnode_t **nodep,
        }
 
        if (noderesult == ISC_R_SUCCESS) {
-               unsigned int node_has_rpz;
 
                /*
                 * Remove the node we just added above.
                 */
-               node_has_rpz = node->rpz;
                tmpresult = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
-               if (tmpresult == ISC_R_SUCCESS) {
-                       /*
-                        * Clean rpz entries added above.
-                        */
-                       if (rbtdb->rpzs != NULL && node_has_rpz)
-                               dns_rpz_delete(rbtdb->load_rpzs,
-                                              rbtdb->rpz_num, name);
-               } else {
+               if (tmpresult != ISC_R_SUCCESS) {
                        isc_log_write(dns_lctx,
                                      DNS_LOGCATEGORY_DATABASE,
                                      DNS_LOGMODULE_CACHE,
@@ -7541,18 +7412,6 @@ beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
 
        RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
 
-       if (rbtdb->rpzs != NULL) {
-               isc_result_t result;
-
-               result = dns_rpz_beginload(&rbtdb->load_rpzs,
-                                          rbtdb->rpzs, rbtdb->rpz_num);
-               if (result != ISC_R_SUCCESS) {
-                       isc_mem_put(rbtdb->common.mctx, loadctx,
-                                   sizeof(*loadctx));
-                       return (result);
-               }
-       }
-
        REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING))
                == 0);
        rbtdb->attributes |= RBTDB_ATTR_LOADING;
@@ -8222,8 +8081,8 @@ static dns_dbmethods_t zone_methods = {
        resigned,
        isdnssec,
        NULL,
-       rpz_attach,
-       rpz_ready,
+       NULL,
+       NULL,
        NULL,
        NULL,
        NULL,
@@ -8545,9 +8404,6 @@ dns_rbtdb_create
        }
        rbtdb->attributes = 0;
        rbtdb->task = NULL;
-       rbtdb->rpzs = NULL;
-       rbtdb->load_rpzs = NULL;
-       rbtdb->rpz_num = DNS_RPZ_INVALID_NUM;
 
        /*
         * Version Initialization.
index 10c3f356f0593b73fa076b9e4c5e7c704d159063..a5d539cb0602514d3b471f7c30af36f4747348da 100644 (file)
 #include <isc/rwlock.h>
 #include <isc/stdlib.h>
 #include <isc/string.h>
+#include <isc/task.h>
 #include <isc/util.h>
 
 #include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/events.h>
 #include <dns/fixedname.h>
 #include <dns/log.h>
 #include <dns/rdata.h>
 #include <dns/rdataset.h>
 #include <dns/rdatastruct.h>
+#include <dns/rdatasetiter.h>
 #include <dns/result.h>
 #include <dns/rbt.h>
 #include <dns/rpz.h>
  * 5 labels all of which are numbers, and a prefix between 1 and 32.
  */
 
+/*
+ * Nodes hashtable calculation parameters
+ */
+#define DNS_RPZ_HTSIZE_MAX     24
+#define DNS_RPZ_HTSIZE_DIV     3
+
+/*
+ * Maximum number of nodes to process per quantum
+ */
+#define DNS_RPZ_QUANTUM 1024
+
+static void
+dns_rpz_update_from_db(dns_rpz_zone_t *rpz);
+
+static void
+dns_rpz_update_taskaction(isc_task_t *task, isc_event_t *event);
 
 /*
  * Use a private definition of IPv6 addresses because s6_addr32 is not
@@ -1150,7 +1170,8 @@ search(dns_rpz_zones_t *rpzs,
                                         * The node lacked relevant data,
                                         * but will have it now.
                                         */
-                                       cur->set.client_ip |= tgt_set->client_ip;
+                                       cur->set.client_ip |=
+                                               tgt_set->client_ip;
                                        cur->set.ip |= tgt_set->ip;
                                        cur->set.nsip |= tgt_set->nsip;
                                        set_sum_pair(cur);
@@ -1378,54 +1399,576 @@ rpz_node_deleter(void *nm_data, void *mctx) {
  * Get ready for a new set of policy zones.
  */
 isc_result_t
-dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx) {
-       dns_rpz_zones_t *new;
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
+                 isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
+{
+       dns_rpz_zones_t *zones;
        isc_result_t result;
 
        REQUIRE(rpzsp != NULL && *rpzsp == NULL);
 
-       *rpzsp = NULL;
+       zones = isc_mem_get(mctx, sizeof(*zones));
+       if (zones == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(zones, 0, sizeof(*zones));
 
-       new = isc_mem_get(mctx, sizeof(*new));
-       if (new == NULL)
+       result = isc_rwlock_init(&zones->search_lock, 0, 0);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_rwlock;
+
+       result = isc_mutex_init(&zones->maint_lock);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_mutex;
+
+       result = isc_refcount_init(&zones->refs, 1);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_refcount;
+
+       result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &zones->rbt);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_rbt;
+
+       result = isc_task_create(taskmgr, 0, &zones->updater);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_task;
+
+       isc_mem_attach(mctx, &zones->mctx);
+       zones->timermgr = timermgr;
+       zones->taskmgr = taskmgr;
+
+       *rpzsp = zones;
+       return (ISC_R_SUCCESS);
+
+cleanup_task:
+       dns_rbt_destroy(&zones->rbt);
+
+cleanup_rbt:
+       isc_refcount_decrement(&zones->refs, NULL);
+       isc_refcount_destroy(&zones->refs);
+
+cleanup_refcount:
+       DESTROYLOCK(&zones->maint_lock);
+
+cleanup_mutex:
+       isc_rwlock_destroy(&zones->search_lock);
+
+cleanup_rwlock:
+       isc_mem_put(mctx, zones, sizeof(*zones));
+
+       return (result);
+}
+
+isc_result_t
+dns_rpz_new_zone(dns_rpz_zones_t *rpzs, dns_rpz_zone_t **rpzp) {
+       dns_rpz_zone_t *zone;
+       isc_result_t result;
+
+       REQUIRE(rpzp != NULL && *rpzp == NULL);
+       REQUIRE(rpzs != NULL);
+       if (rpzs->p.num_zones >= DNS_RPZ_MAX_ZONES) {
+               return (ISC_R_NOSPACE);
+       }
+
+       zone = isc_mem_get(rpzs->mctx, sizeof(*zone));
+       if (zone == NULL) {
                return (ISC_R_NOMEMORY);
-       memset(new, 0, sizeof(*new));
+       }
+
+       memset(zone, 0, sizeof(*zone));
+       result = isc_refcount_init(&zone->refs, 1);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_refcount;
+
+       result = isc_timer_create(rpzs->timermgr, isc_timertype_inactive,
+                                 NULL, NULL, rpzs->updater,
+                                 dns_rpz_update_taskaction,
+                                 zone, &zone->updatetimer);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_timer;
+
+       /*
+        * This will never be used, but costs us nothing and
+        * simplifies update_from_db
+        */
+
+       result = isc_ht_init(&zone->nodes, rpzs->mctx, 1);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_ht;
+
+       dns_name_init(&zone->origin, NULL);
+       dns_name_init(&zone->client_ip, NULL);
+       dns_name_init(&zone->ip, NULL);
+       dns_name_init(&zone->nsdname, NULL);
+       dns_name_init(&zone->nsip, NULL);
+       dns_name_init(&zone->passthru, NULL);
+       dns_name_init(&zone->drop, NULL);
+       dns_name_init(&zone->tcp_only, NULL);
+       dns_name_init(&zone->cname, NULL);
+
+       isc_time_settoepoch(&zone->lastupdated);
+       zone->updatepending = ISC_FALSE;
+       zone->updaterunning = ISC_FALSE;
+       zone->db = NULL;
+       zone->dbversion = NULL;
+       zone->updb = NULL;
+       zone->updbversion = NULL;
+       zone->updbit = NULL;
+       zone->rpzs = rpzs;
+       zone->db_registered = ISC_FALSE;
+
+       zone->num = rpzs->p.num_zones++;
+       rpzs->zones[zone->num] = zone;
+
+       *rpzp = zone;
+
+       return (ISC_R_SUCCESS);
+
+cleanup_ht:
+       isc_timer_detach(&zone->updatetimer);
+
+cleanup_timer:
+       isc_refcount_decrement(&zone->refs, NULL);
+       isc_refcount_destroy(&zone->refs);
+
+cleanup_refcount:
+       isc_mem_put(zone->rpzs->mctx, zone, sizeof(*zone));
+
+       return (result);
+}
+
+isc_result_t
+dns_rpz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
+       dns_rpz_zone_t *zone = (dns_rpz_zone_t *) fn_arg;
+       isc_time_t now;
+       isc_uint64_t tdiff;
+       isc_result_t result = ISC_R_SUCCESS;
+       char dname[DNS_NAME_FORMATSIZE];
+
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(zone != NULL);
+
+       LOCK(&zone->rpzs->maint_lock);
+       REQUIRE(zone->db_registered);
+
+
+       /* New zone came as AXFR */
+       if (zone->db != NULL && zone->db != db) {
+               /* We need to clean up the old DB */
+               if (zone->dbversion != NULL)
+                       dns_db_closeversion(zone->db, &zone->dbversion,
+                                           ISC_FALSE);
+               dns_db_updatenotify_unregister(zone->db,
+                                              dns_rpz_dbupdate_callback,
+                                              zone);
+               dns_db_detach(&zone->db);
+       }
+
+       if (zone->db == NULL) {
+               RUNTIME_CHECK(zone->dbversion == NULL);
+               dns_db_attach(db, &zone->db);
+       }
+
+       if (!zone->updatepending && !zone->updaterunning) {
+               zone->updatepending = ISC_TRUE;
+               isc_time_now(&now);
+               tdiff = isc_time_microdiff(&now, &zone->lastupdated) / 1000000;
+               if (tdiff < zone->min_update_int) {
+                       isc_uint64_t defer = zone->min_update_int - tdiff;
+                       isc_interval_t interval;
+                       dns_name_format(&zone->origin, dname,
+                                       DNS_NAME_FORMATSIZE);
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                                     "rpz: %s: new zone version came "
+                                     "too soon, deferring update for "
+                                     "%llu seconds", dname, defer);
+                       isc_interval_set(&interval, defer, 0);
+                       dns_db_currentversion(zone->db, &zone->dbversion);
+                       result = isc_timer_reset(zone->updatetimer,
+                                                isc_timertype_once,
+                                                NULL, &interval, ISC_TRUE);
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+               } else {
+                       isc_event_t *event;
+
+                       dns_db_currentversion(zone->db, &zone->dbversion);
+                       ISC_EVENT_INIT(&zone->updateevent,
+                                      sizeof(zone->updateevent), 0, NULL,
+                                      DNS_EVENT_RPZUPDATED,
+                                      dns_rpz_update_taskaction,
+                                      zone, zone, NULL, NULL);
+                       event = &zone->updateevent;
+                       isc_task_send(zone->rpzs->updater, &event);
+               }
+       } else {
+               zone->updatepending = ISC_TRUE;
+               dns_name_format(&zone->origin, dname, DNS_NAME_FORMATSIZE);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+                             "rpz: %s: update already queued or running", dname);
+               if (zone->dbversion != NULL)
+                       dns_db_closeversion(zone->db, &zone->dbversion,
+                                           ISC_FALSE);
+               dns_db_currentversion(zone->db, &zone->dbversion);
+       }
+
+  cleanup:
+       UNLOCK(&zone->rpzs->maint_lock);
+
+       return (result);
+}
+
+static void
+dns_rpz_update_taskaction(isc_task_t *task, isc_event_t *event) {
+       isc_result_t result;
+       dns_rpz_zone_t *zone;
+
+       REQUIRE(event != NULL);
+       REQUIRE(event->ev_arg != NULL);
+
+       UNUSED(task);
+       zone = (dns_rpz_zone_t *) event->ev_arg;
+       LOCK(&zone->rpzs->maint_lock);
+       zone->updatepending = ISC_FALSE;
+       zone->updaterunning = ISC_TRUE;
+       dns_rpz_update_from_db(zone);
+       result = isc_timer_reset(zone->updatetimer, isc_timertype_inactive,
+                                NULL, NULL, ISC_TRUE);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       result = isc_time_now(&zone->lastupdated);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       UNLOCK(&zone->rpzs->maint_lock);
+       isc_event_free(&event);
+}
+
+static isc_result_t
+setup_update(dns_rpz_zone_t *rpz) {
+       isc_result_t result;
+       char domain[DNS_NAME_FORMATSIZE];
+       unsigned int nodecount;
+       uint32_t hashsize;
+
+       dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE);
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                     DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                     "rpz: %s: reload start", domain);
+
+       nodecount = dns_db_nodecount(rpz->updb);
+       hashsize = 1;
+       while (nodecount != 0 &&
+              hashsize <= (DNS_RPZ_HTSIZE_MAX + DNS_RPZ_HTSIZE_DIV))
+       {
+               hashsize++;
+               nodecount >>=1;
+       }
+
+       if (hashsize > DNS_RPZ_HTSIZE_DIV)
+               hashsize -= DNS_RPZ_HTSIZE_DIV;
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                             "rpz: %s: using hashtable size %d",
+                             domain, hashsize);
 
-       result = isc_rwlock_init(&new->search_lock, 0, 0);
+       result = isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize);
        if (result != ISC_R_SUCCESS) {
-               isc_mem_put(mctx, new, sizeof(*new));
-               return (result);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "rpz: %s: failed to initialize hashtable - %s",
+                             domain, isc_result_totext(result));
+               goto cleanup;
        }
 
-       result = isc_mutex_init(&new->maint_lock);
+       result = dns_db_createiterator(rpz->updb, DNS_DB_NONSEC3, &rpz->updbit);
        if (result != ISC_R_SUCCESS) {
-               isc_rwlock_destroy(&new->search_lock);
-               isc_mem_put(mctx, new, sizeof(*new));
-               return (result);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "rpz: %s: failed to create DB iterator - %s",
+                             domain, isc_result_totext(result));
+               goto cleanup;
        }
 
-       result = isc_refcount_init(&new->refs, 1);
+       result = dns_dbiterator_first(rpz->updbit);
        if (result != ISC_R_SUCCESS) {
-               DESTROYLOCK(&new->maint_lock);
-               isc_rwlock_destroy(&new->search_lock);
-               isc_mem_put(mctx, new, sizeof(*new));
-               return (result);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "rpz: %s: failed to get db iterator - %s",
+                             domain, isc_result_totext(result));
+               goto cleanup;
        }
 
-       result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &new->rbt);
+ cleanup:
        if (result != ISC_R_SUCCESS) {
-               isc_refcount_decrement(&new->refs, NULL);
-               isc_refcount_destroy(&new->refs);
-               DESTROYLOCK(&new->maint_lock);
-               isc_rwlock_destroy(&new->search_lock);
-               isc_mem_put(mctx, new, sizeof(*new));
-               return (result);
+               if (rpz->updbit != NULL)
+                       dns_dbiterator_destroy(&rpz->updbit);
+               if (rpz->newnodes != NULL)
+                       isc_ht_destroy(&rpz->newnodes);
+               dns_db_closeversion(rpz->updb, &rpz->updbversion, ISC_FALSE);
        }
 
-       isc_mem_attach(mctx, &new->mctx);
+       return (result);
+}
 
-       *rpzsp = new;
-       return (ISC_R_SUCCESS);
+static void
+finish_update(dns_rpz_zone_t *rpz) {
+       isc_result_t result;
+       isc_ht_t *tmpht = NULL;
+       isc_ht_iter_t *iter = NULL;
+       dns_fixedname_t fname;
+       char dname[DNS_NAME_FORMATSIZE];
+       dns_name_t *name;
+
+       /*
+        * Iterate over old ht with existing nodes deleted to delete
+        * deleted nodes from RPZ
+        */
+       result = isc_ht_iter_create(rpz->nodes, &iter);
+       if (result != ISC_R_SUCCESS) {
+               char domain[DNS_NAME_FORMATSIZE];
+
+               dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "rpz: %s: failed to create HT iterator - %s",
+                             domain, isc_result_totext(result));
+               goto cleanup;
+       }
+
+       dns_fixedname_init(&fname);
+       name = dns_fixedname_name(&fname);
+
+       for (result = isc_ht_iter_first(iter);
+            result == ISC_R_SUCCESS;
+            result = isc_ht_iter_delcurrent_next(iter))
+       {
+               isc_region_t region;
+               unsigned char *key;
+               size_t keysize;
+
+               isc_ht_iter_currentkey(iter, &key, &keysize);
+               region.base = key;
+               region.length = keysize;
+               dns_name_fromregion(name, &region);
+               dns_rpz_delete(rpz->rpzs, rpz->num, name);
+       }
+
+       tmpht = rpz->nodes;
+       rpz->nodes = rpz->newnodes;
+       rpz->newnodes = tmpht;
+
+       LOCK(&rpz->rpzs->maint_lock);
+       rpz->updaterunning = ISC_FALSE;
+       /*
+        * If there's an update pending schedule it
+        */
+       if (rpz->updatepending == ISC_TRUE) {
+               isc_uint64_t defer = rpz->min_update_int;
+               isc_interval_t interval;
+               dns_name_format(&rpz->origin, dname,
+                               DNS_NAME_FORMATSIZE);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                             "rpz: %s: new zone version came "
+                             "too soon, deferring update for "
+                             "%llu seconds", dname, defer);
+               isc_interval_set(&interval, defer, 0);
+               result = isc_timer_reset(rpz->updatetimer,
+                                        isc_timertype_once,
+                                        NULL, &interval, ISC_TRUE);
+       }
+       UNLOCK(&rpz->rpzs->maint_lock);
+
+cleanup:
+       if (iter != NULL)
+               isc_ht_iter_destroy(&iter);
+}
+
+static void
+update_quantum(isc_task_t *task, isc_event_t *event) {
+       isc_result_t result = ISC_R_SUCCESS;
+       dns_dbnode_t *node = NULL;
+       dns_rpz_zone_t *rpz;
+       char domain[DNS_NAME_FORMATSIZE];
+       dns_fixedname_t fixname;
+       dns_name_t *name;
+       int count = 0;
+
+       UNUSED(task);
+
+       REQUIRE(event != NULL);
+       REQUIRE(event->ev_arg != NULL);
+
+       rpz = (dns_rpz_zone_t *) event->ev_arg;
+       isc_event_free(&event);
+
+       REQUIRE(rpz->updbit != NULL);
+       REQUIRE(rpz->newnodes != NULL);
+
+       dns_fixedname_init(&fixname);
+       name = dns_fixedname_name(&fixname);
+
+       dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE);
+
+       while (result == ISC_R_SUCCESS && count++ < DNS_RPZ_QUANTUM) {
+               char namebuf[DNS_NAME_FORMATSIZE];
+               dns_rdatasetiter_t *rdsiter = NULL;
+
+               result = dns_dbiterator_current(rpz->updbit, &node, name);
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                                     "rpz: %s: failed to get dbiterator - %s",
+                                     domain, isc_result_totext(result));
+                       dns_db_detachnode(rpz->updb, &node);
+                       break;
+               }
+
+               result = dns_db_allrdatasets(rpz->updb, node, rpz->updbversion, 0,
+                                            &rdsiter);
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                                     "rpz: %s: failed to fetch "
+                                     "rrdatasets - %s",
+                                     domain, isc_result_totext(result));
+                       dns_db_detachnode(rpz->updb, &node);
+                       break;
+               }
+
+               result = dns_rdatasetiter_first(rdsiter);
+               dns_rdatasetiter_destroy(&rdsiter);
+               if (result != ISC_R_SUCCESS) { /* empty non-terminal */
+                       if (result != ISC_R_NOMORE)
+                               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                                     "rpz: %s: error %s while creating "
+                                     "rdatasetiter",
+                                     domain, isc_result_totext(result));
+                       dns_db_detachnode(rpz->updb, &node);
+                       result = dns_dbiterator_next(rpz->updbit);
+                       continue;
+               }
+
+               result = isc_ht_add(rpz->newnodes, name->ndata,
+                                   name->length, rpz);
+               if (result != ISC_R_SUCCESS) {
+                       dns_name_format(name, namebuf, sizeof(namebuf));
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                                     "rpz: %s, adding node %s to HT error %s",
+                                     domain, namebuf,
+                                     isc_result_totext(result));
+                       dns_db_detachnode(rpz->updb, &node);
+                       result = dns_dbiterator_next(rpz->updbit);
+                       continue;
+               }
+
+               result = isc_ht_find(rpz->nodes, name->ndata,
+                                    name->length, NULL);
+               if (result == ISC_R_SUCCESS) {
+                       isc_ht_delete(rpz->nodes, name->ndata, name->length);
+               } else { /* not found */
+                       result = dns_rpz_add(rpz->rpzs, rpz->num, name);
+                       if (result != ISC_R_SUCCESS) {
+                               dns_name_format(name, namebuf, sizeof(namebuf));
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_GENERAL,
+                                             DNS_LOGMODULE_MASTER,
+                                             ISC_LOG_ERROR,
+                                             "rpz: %s: adding node %s "
+                                             "to RPZ error %s",
+                                             domain, namebuf,
+                                             isc_result_totext(result));
+                       } else {
+                               dns_name_format(name, namebuf, sizeof(namebuf));
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_GENERAL,
+                                             DNS_LOGMODULE_MASTER,
+                                             ISC_LOG_DEBUG(3),
+                                             "rpz: %s: adding node %s",
+                                             domain, namebuf);
+                       }
+               }
+
+               dns_db_detachnode(rpz->updb, &node);
+               result = dns_dbiterator_next(rpz->updbit);
+       }
+
+       if (result == ISC_R_SUCCESS) {
+               isc_event_t *nevent;
+               /*
+                * Pause the iterator so that the DB is not locked
+                */
+               dns_dbiterator_pause(rpz->updbit);
+               /*
+                * We finished a quantum; trigger the next one and return
+                */
+               ISC_EVENT_INIT(&rpz->updateevent,
+                              sizeof(rpz->updateevent), 0, NULL,
+                              DNS_EVENT_RPZUPDATED,
+                              update_quantum,
+                              rpz, rpz, NULL, NULL);
+               nevent = &rpz->updateevent;
+               isc_task_send(rpz->rpzs->updater, &nevent);
+               return;
+       } else if (result == ISC_R_NOMORE) {
+               /*
+                * All done.
+                */
+               finish_update(rpz);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+                             "rpz: %s: reload done", domain);
+       }
+
+       /*
+        * If we're here, we've either finished or something went wrong,
+        * so clean up.
+        */
+       if (rpz->updbit != NULL)
+               dns_dbiterator_destroy(&rpz->updbit);
+       if (rpz->newnodes != NULL)
+               isc_ht_destroy(&rpz->newnodes);
+       dns_db_closeversion(rpz->updb, &rpz->updbversion, ISC_FALSE);
+       dns_db_detach(&rpz->updb);
+}
+
+static void
+dns_rpz_update_from_db(dns_rpz_zone_t *rpz) {
+       isc_result_t result;
+       isc_event_t *event;
+
+       REQUIRE(rpz != NULL);
+       REQUIRE(DNS_DB_VALID(rpz->db));
+       REQUIRE(rpz->updb == NULL);
+       REQUIRE(rpz->updbversion == NULL);
+       REQUIRE(rpz->updbit == NULL);
+       REQUIRE(rpz->newnodes == NULL);
+
+       dns_db_attach(rpz->db, &rpz->updb);
+       rpz->updbversion = rpz->dbversion;
+       rpz->dbversion = NULL;
+
+       result = setup_update(rpz);
+       if (result != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+
+       event = &rpz->updateevent;
+       ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent),
+                      0, NULL, DNS_EVENT_RPZUPDATED,
+                      update_quantum, rpz, rpz, NULL, NULL);
+       isc_task_send(rpz->rpzs->updater, &event);
+       return;
+
+ cleanup:
+       if (rpz->updbit != NULL)
+               dns_dbiterator_destroy(&rpz->updbit);
+       if (rpz->newnodes != NULL)
+               isc_ht_destroy(&rpz->newnodes);
+       dns_db_closeversion(rpz->updb, &rpz->updbversion, ISC_FALSE);
+       dns_db_detach(&rpz->updb);
 }
 
 /*
@@ -1494,6 +2037,16 @@ rpz_detach(dns_rpz_zone_t **rpzp, dns_rpz_zones_t *rpzs) {
                dns_name_free(&rpz->tcp_only, rpzs->mctx);
        if (dns_name_dynamic(&rpz->cname))
                dns_name_free(&rpz->cname, rpzs->mctx);
+       if (rpz->db_registered)
+               dns_db_updatenotify_unregister(rpz->db,
+                                              dns_rpz_dbupdate_callback, rpz);
+       if (rpz->dbversion != NULL)
+               dns_db_closeversion(rpz->db, &rpz->dbversion,
+                                   ISC_FALSE);
+       if (rpz->db)
+               dns_db_detach(&rpz->db);
+       isc_ht_destroy(&rpz->nodes);
+       isc_timer_detach(&rpz->updatetimer);
 
        isc_mem_put(rpzs->mctx, rpz, sizeof(*rpz));
 }
@@ -1538,345 +2091,37 @@ dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) {
                DESTROYLOCK(&rpzs->maint_lock);
                isc_rwlock_destroy(&rpzs->search_lock);
                isc_refcount_destroy(&rpzs->refs);
+               isc_task_destroy(&rpzs->updater);
                isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
        }
 }
 
 /*
- * Create empty summary database to load one zone.
- * The RBTDB write tree lock must be held.
+ * Deprecated and removed.
  */
 isc_result_t
 dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
                  dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num)
 {
-       dns_rpz_zones_t *load_rpzs;
-       dns_rpz_zone_t *rpz;
-       dns_rpz_zbits_t tgt;
-       isc_result_t result;
-
-       REQUIRE(rpz_num < rpzs->p.num_zones);
-       rpz = rpzs->zones[rpz_num];
-       REQUIRE(rpz != NULL);
-
-       /*
-        * When reloading a zone, there are usually records among the summary
-        * data for the zone.  Some of those records might be deleted by the
-        * reloaded zone data.  To deal with that case:
-        *    reload the new zone data into a new blank summary database
-        *    if the reload fails, discard the new summary database
-        *    if the new zone data is acceptable, copy the records for the
-        *      other zones into the new summary CIDR and RBT databases
-        *      and replace the old summary databases with the new, and
-        *      correct the triggers and have values for the updated
-        *      zone.
-        *
-        * At the first attempt to load a zone, there is no summary data
-        * for the zone and so no records that need to be deleted.
-        * This is also the most common case of policy zone loading.
-        * Most policy zone maintenance should be by incremental changes
-        * and so by the addition and deletion of individual records.
-        * Detect that case and load records the first time into the
-        * operational summary database
-        */
-       tgt = DNS_RPZ_ZBIT(rpz_num);
-       LOCK(&rpzs->maint_lock);
-       RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
-       if ((rpzs->load_begun & tgt) == 0) {
-               /*
-                * There is no existing version of the target zone.
-                */
-               rpzs->load_begun |= tgt;
-               dns_rpz_attach_rpzs(rpzs, load_rpzsp);
-       } else {
-               /*
-                * Setup the new RPZ struct with empty summary trees.
-                */
-               result = dns_rpz_new_zones(load_rpzsp, rpzs->mctx);
-               if (result != ISC_R_SUCCESS)
-                       return (result);
-               load_rpzs = *load_rpzsp;
-               /*
-                * Initialize some members so that dns_rpz_add() works.
-                */
-               load_rpzs->p.num_zones = rpzs->p.num_zones;
-               memset(&load_rpzs->triggers, 0, sizeof(load_rpzs->triggers));
-               load_rpzs->zones[rpz_num] = rpz;
-               isc_refcount_increment(&rpz->refs, NULL);
-       }
-
-       RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
-       UNLOCK(&rpzs->maint_lock);
+       UNUSED(load_rpzsp);
+       UNUSED(rpzs);
+       UNUSED(rpz_num);
 
-       return (ISC_R_SUCCESS);
+       return (ISC_R_NOTIMPLEMENTED);
 }
 
 /*
- * This function updates "have" bits and also the qname_skip_recurse
- * mask. It must be called when holding a write lock on rpzs->search_lock.
- */
-static void
-fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
-       dns_rpz_num_t n;
-       dns_rpz_triggers_t old_totals;
-       dns_rpz_zbits_t zbit;
-       char namebuf[DNS_NAME_FORMATSIZE];
-
-       /*
-        * rpzs->total_triggers is only used to log a message below.
-        */
-
-       memmove(&old_totals, &rpzs->total_triggers, sizeof(old_totals));
-       memset(&rpzs->total_triggers, 0, sizeof(rpzs->total_triggers));
-
-#define SET_TRIG(n, zbit, type)                                                \
-       if (rpzs->triggers[n].type == 0U) {                             \
-               rpzs->have.type &= ~zbit;                               \
-       } else {                                                        \
-               rpzs->total_triggers.type += rpzs->triggers[n].type;    \
-               rpzs->have.type |= zbit;                                \
-       }
-
-       for (n = 0; n < rpzs->p.num_zones; ++n) {
-               zbit = DNS_RPZ_ZBIT(n);
-               SET_TRIG(n, zbit, client_ipv4);
-               SET_TRIG(n, zbit, client_ipv6);
-               SET_TRIG(n, zbit, qname);
-               SET_TRIG(n, zbit, ipv4);
-               SET_TRIG(n, zbit, ipv6);
-               SET_TRIG(n, zbit, nsdname);
-               SET_TRIG(n, zbit, nsipv4);
-               SET_TRIG(n, zbit, nsipv6);
-       }
-
-#undef SET_TRIG
-
-       fix_qname_skip_recurse(rpzs);
-
-       dns_name_format(&rpzs->zones[rpz_num]->origin,
-                       namebuf, sizeof(namebuf));
-       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
-                     DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL,
-                     "(re)loading policy zone '%s' changed from"
-                     " %lu to %lu qname, %lu to %lu nsdname,"
-                     " %lu to %lu IP, %lu to %lu NSIP,"
-                     " %lu to %lu CLIENTIP entries",
-                     namebuf,
-                     (unsigned long) old_totals.qname,
-                     (unsigned long) rpzs->total_triggers.qname,
-                     (unsigned long) old_totals.nsdname,
-                     (unsigned long) rpzs->total_triggers.nsdname,
-                     (unsigned long) old_totals.ipv4 + old_totals.ipv6,
-                     (unsigned long) (rpzs->total_triggers.ipv4 +
-                                      rpzs->total_triggers.ipv6),
-                     (unsigned long) old_totals.nsipv4 + old_totals.nsipv6,
-                     (unsigned long) (rpzs->total_triggers.nsipv4 +
-                                      rpzs->total_triggers.nsipv6),
-                     (unsigned long) old_totals.client_ipv4 +
-                                     old_totals.client_ipv6,
-                     (unsigned long) (rpzs->total_triggers.client_ipv4 +
-                                      rpzs->total_triggers.client_ipv6));
-}
-
-/*
- * Finish loading one zone. This function is called during a commit when
- * a RPZ zone loading is complete.  The RBTDB write tree lock must be
- * held.
- *
- * Here, rpzs is a pointer to the view's common rpzs
- * structure. *load_rpzsp is a rpzs structure that is local to the
- * RBTDB, which is used during a single zone's load.
- *
- * During the zone load, i.e., between dns_rpz_beginload() and
- * dns_rpz_ready(), only the zone that is being loaded updates
- * *load_rpzsp. These updates in the summary databases inside load_rpzsp
- * are made only for the rpz_num (and corresponding bit) of that
- * zone. Nothing else reads or writes *load_rpzsp. The view's common
- * rpzs is used during this time for queries.
- *
- * When zone loading is complete and we arrive here, the parts of the
- * summary databases (CIDR and nsdname+qname RBT trees) from the view's
- * common rpzs struct have to be merged into the summary databases of
- * *load_rpzsp, as the summary databases of the view's common rpzs
- * struct may have changed during the time the zone was being loaded.
- *
- * The function below carries out the merge. During the merge, it holds
- * the maint_lock of the view's common rpzs struct so that it is not
- * updated while the merging is taking place.
- *
- * After the merging is carried out, *load_rpzsp contains the most
- * current state of the rpzs structure, i.e., the summary trees contain
- * data for the new zone that was just loaded, as well as all other
- * zones.
- *
- * Pointers to the summary databases of *load_rpzsp (CIDR and
- * nsdname+qname RBT trees) are then swapped into the view's common rpz
- * struct, so that the query path can continue using it. During the
- * swap, the search_lock of the view's common rpz struct is acquired so
- * that queries are paused while this swap occurs.
- *
- * The trigger counts for the new zone are also copied into the view's
- * common rpz struct, and some other summary counts and masks are
- * updated.
+ * Deprecated and removed.
  */
 isc_result_t
 dns_rpz_ready(dns_rpz_zones_t *rpzs,
              dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num)
 {
-       dns_rpz_zones_t *load_rpzs;
-       const dns_rpz_cidr_node_t *cnode, *next_cnode, *parent_cnode;
-       dns_rpz_cidr_node_t *found;
-       dns_rpz_zbits_t new_bit;
-       dns_rpz_addr_zbits_t new_ip;
-       dns_rbt_t *rbt;
-       dns_rbtnodechain_t chain;
-       dns_rbtnode_t *nmnode;
-       dns_rpz_nm_data_t *nm_data, new_data;
-       dns_fixedname_t labelf, originf, namef;
-       dns_name_t *label, *origin, *name;
-       isc_result_t result;
-
-       INSIST(rpzs != NULL);
-       LOCK(&rpzs->maint_lock);
-       load_rpzs = *load_rpzsp;
-       INSIST(load_rpzs != NULL);
-
-       if (load_rpzs == rpzs) {
-               /*
-                * This is a successful initial zone loading, perhaps
-                * for a new instance of a view.
-                */
-               RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
-               fix_triggers(rpzs, rpz_num);
-               RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
-               UNLOCK(&rpzs->maint_lock);
-               dns_rpz_detach_rpzs(load_rpzsp);
-               return (ISC_R_SUCCESS);
-       }
-
-       LOCK(&load_rpzs->maint_lock);
-       RWLOCK(&load_rpzs->search_lock, isc_rwlocktype_write);
+       UNUSED(rpzs);
+       UNUSED(load_rpzsp);
+       UNUSED(rpz_num);
 
-       /*
-        * Unless there is only one policy zone, copy the other policy zones
-        * from the old policy structure to the new summary databases.
-        */
-       if (rpzs->p.num_zones > 1) {
-               new_bit = ~DNS_RPZ_ZBIT(rpz_num);
-
-               /*
-                * Copy to the radix tree.
-                */
-               for (cnode = rpzs->cidr; cnode != NULL; cnode = next_cnode) {
-                       new_ip.ip = cnode->set.ip & new_bit;
-                       new_ip.client_ip = cnode->set.client_ip & new_bit;
-                       new_ip.nsip = cnode->set.nsip & new_bit;
-                       if (new_ip.client_ip != 0 ||
-                           new_ip.ip != 0 ||
-                           new_ip.nsip != 0) {
-                               result = search(load_rpzs,
-                                               &cnode->ip, cnode->prefix,
-                                               &new_ip, ISC_TRUE, &found);
-                               if (result == ISC_R_NOMEMORY)
-                                       goto unlock_and_detach;
-                               INSIST(result == ISC_R_SUCCESS);
-                       }
-                       /*
-                        * Do down and to the left as far as possible.
-                        */
-                       next_cnode = cnode->child[0];
-                       if (next_cnode != NULL)
-                               continue;
-                       /*
-                        * Go up until we find a branch to the right where
-                        * we previously took the branch to the left.
-                        */
-                       for (;;) {
-                               parent_cnode = cnode->parent;
-                               if (parent_cnode == NULL)
-                                       break;
-                               if (parent_cnode->child[0] == cnode) {
-                                       next_cnode = parent_cnode->child[1];
-                                       if (next_cnode != NULL)
-                                           break;
-                               }
-                               cnode = parent_cnode;
-                       }
-               }
-
-               /*
-                * Copy to the summary RBT.
-                */
-               dns_fixedname_init(&namef);
-               name = dns_fixedname_name(&namef);
-               dns_fixedname_init(&labelf);
-               label = dns_fixedname_name(&labelf);
-               dns_fixedname_init(&originf);
-               origin = dns_fixedname_name(&originf);
-               dns_rbtnodechain_init(&chain, NULL);
-               result = dns_rbtnodechain_first(&chain, rpzs->rbt, NULL, NULL);
-               while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
-                       result = dns_rbtnodechain_current(&chain, label, origin,
-                                                       &nmnode);
-                       INSIST(result == ISC_R_SUCCESS);
-                       nm_data = nmnode->data;
-                       if (nm_data != NULL) {
-                               new_data.set.qname = (nm_data->set.qname &
-                                                     new_bit);
-                               new_data.set.ns = nm_data->set.ns & new_bit;
-                               new_data.wild.qname = (nm_data->wild.qname &
-                                                      new_bit);
-                               new_data.wild.ns = nm_data->wild.ns & new_bit;
-                               if (new_data.set.qname != 0 ||
-                                   new_data.set.ns != 0 ||
-                                   new_data.wild.qname != 0 ||
-                                   new_data.wild.ns != 0) {
-                                       result = dns_name_concatenate(label,
-                                                       origin, name, NULL);
-                                       INSIST(result == ISC_R_SUCCESS);
-                                       result = add_nm(load_rpzs, name,
-                                                       &new_data);
-                                       if (result != ISC_R_SUCCESS)
-                                               goto unlock_and_detach;
-                               }
-                       }
-                       result = dns_rbtnodechain_next(&chain, NULL, NULL);
-               }
-               if (result != ISC_R_NOMORE && result != ISC_R_NOTFOUND) {
-                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
-                                     DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
-                                     "dns_rpz_ready(): unexpected %s",
-                                     isc_result_totext(result));
-                       goto unlock_and_detach;
-               }
-       }
-
-       /*
-        * Exchange the summary databases.
-        */
-       RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
-
-       rpzs->triggers[rpz_num] = load_rpzs->triggers[rpz_num];
-       fix_triggers(rpzs, rpz_num);
-
-       found = rpzs->cidr;
-       rpzs->cidr = load_rpzs->cidr;
-       load_rpzs->cidr = found;
-
-       rbt = rpzs->rbt;
-       rpzs->rbt = load_rpzs->rbt;
-       load_rpzs->rbt = rbt;
-
-       RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
-
-       result = ISC_R_SUCCESS;
-
- unlock_and_detach:
-       UNLOCK(&rpzs->maint_lock);
-       RWUNLOCK(&load_rpzs->search_lock, isc_rwlocktype_write);
-       UNLOCK(&load_rpzs->maint_lock);
-       dns_rpz_detach_rpzs(load_rpzsp);
-       return (result);
+       return (ISC_R_NOTIMPLEMENTED);
 }
 
 /*
@@ -1893,11 +2138,10 @@ dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
        REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
        rpz = rpzs->zones[rpz_num];
        REQUIRE(rpz != NULL);
+       RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
 
        rpz_type = type_from_name(rpz, src_name);
 
-       LOCK(&rpzs->maint_lock);
-       RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
 
        switch (rpz_type) {
        case DNS_RPZ_TYPE_QNAME:
@@ -1912,9 +2156,8 @@ dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
        case DNS_RPZ_TYPE_BAD:
                break;
        }
-
        RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
-       UNLOCK(&rpzs->maint_lock);
+
        return (result);
 }
 
@@ -1966,7 +2209,8 @@ del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
        tgt->set.nsip &= ~tgt_set.nsip;
        set_sum_pair(tgt);
 
-       adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, ISC_FALSE);
+       adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix,
+                       ISC_FALSE);
 
        /*
         * We might need to delete 2 nodes.
@@ -2071,7 +2315,8 @@ del_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
                result = dns_rbt_deletenode(rpzs->rbt, nmnode, ISC_FALSE);
                if (result != ISC_R_SUCCESS) {
                        /*
-                        * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+                        * bin/tests/system/rpz/tests.sh looks for
+                        * "rpz.*failed".
                         */
                        dns_name_format(src_name, namebuf, sizeof(namebuf));
                        isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
@@ -2098,11 +2343,10 @@ dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
        rpz = rpzs->zones[rpz_num];
        REQUIRE(rpz != NULL);
 
-       rpz_type = type_from_name(rpz, src_name);
-
-       LOCK(&rpzs->maint_lock);
        RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
 
+       rpz_type = type_from_name(rpz, src_name);
+
        switch (rpz_type) {
        case DNS_RPZ_TYPE_QNAME:
        case DNS_RPZ_TYPE_NSDNAME:
@@ -2118,7 +2362,6 @@ dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
        }
 
        RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
-       UNLOCK(&rpzs->maint_lock);
 }
 
 /*
@@ -2142,9 +2385,9 @@ dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
        dns_rpz_have_t have;
        int i;
 
-       LOCK(&rpzs->maint_lock);
+       RWLOCK(&rpzs->search_lock, isc_rwlocktype_read);
        have = rpzs->have;
-       UNLOCK(&rpzs->maint_lock);
+       RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_read);
 
        /*
         * Convert IP address to CIDR tree key.
index 98040f2bb2cc99e5453243840bc083063df23d51..e7cbb6fa53d362a1d8fc495759c10a7a1e6cf063 100644 (file)
@@ -960,11 +960,13 @@ dns_rootns_create
 dns_rpz_add
 dns_rpz_attach_rpzs
 dns_rpz_beginload
+dns_rpz_dbupdate_callback
 dns_rpz_decode_cname
 dns_rpz_delete
 dns_rpz_detach_rpzs
 dns_rpz_find_ip
 dns_rpz_find_name
+dns_rpz_new_zone
 dns_rpz_new_zones
 dns_rpz_policy2str
 dns_rpz_ready
index 39f4a636aba0e846a4a6abe021a1627e4c76de57..e503039b81282103f7dacb091fed07854d1f1dfa 100644 (file)
@@ -1752,10 +1752,15 @@ dns_zone_get_rpz_num(dns_zone_t *zone) {
  */
 void
 dns_zone_rpz_enable_db(dns_zone_t *zone, dns_db_t *db) {
-       if (zone->rpz_num != DNS_RPZ_INVALID_NUM) {
-               REQUIRE(zone->rpzs != NULL);
-               dns_db_rpz_attach(db, zone->rpzs, zone->rpz_num);
-       }
+       isc_result_t result;
+       if (zone->rpz_num == DNS_RPZ_INVALID_NUM)
+               return;
+       REQUIRE(zone->rpzs != NULL);
+       zone->rpzs->zones[zone->rpz_num]->db_registered = ISC_TRUE;
+       result = dns_db_updatenotify_register(db,
+                                             dns_rpz_dbupdate_callback,
+                                             zone->rpzs->zones[zone->rpz_num]);
+       REQUIRE(result == ISC_R_SUCCESS);
 }
 
 void
@@ -4619,9 +4624,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
                if (result != ISC_R_SUCCESS)
                        goto cleanup;
        } else {
-               result = dns_db_rpz_ready(db);
-               if (result != ISC_R_SUCCESS)
-                       goto cleanup;
                zone_attachdb(zone, db);
                ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
                DNS_ZONE_SETFLAG(zone,
@@ -14471,10 +14473,6 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) {
        if (inline_raw(zone))
                REQUIRE(LOCKED_ZONE(zone->secure));
 
-       result = dns_db_rpz_ready(db);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-
        result = zone_get_from_db(zone, db, &nscount, &soacount,
                                  NULL, NULL, NULL, NULL, NULL, NULL);
        if (result == ISC_R_SUCCESS) {
index e68dca41126b2c01c31e647a5287ae1dbc82e78d..536f04fe6b0fa02b3aa0da8948240c8a6f0ade9a 100644 (file)
@@ -157,14 +157,15 @@ isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
 
        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;
+                   memcmp(key, node->key, keysize) == 0)
+               {
+                       if (valuep != NULL)
+                               *valuep = node->value;
                        return (ISC_R_SUCCESS);
                }
                node = node->next;
index 25819b8ed53fec7bcf8565c52d75fb6cc2387499..1a6f44f263525df58340871cb64caeddf907a7b6 100644 (file)
@@ -61,7 +61,9 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize,
 
 /*%
  * Find a node matching 'key'/'keysize' in hashtable 'ht';
- * if found, set 'value' to its value
+ * if found, set '*valuep' to its value. (If 'valuep' is NULL,
+ * then simply return SUCCESS or NOTFOUND to indicate whether the
+ * key exists in the hashtable.)
  *
  * Requires:
  * \li 'ht' is a valid hashtable
index ca8342305efe83536855e0a9f84e1f158c0c15d7..badbcf9af724e7e5660ca4a25304b43c62ed1d5b 100644 (file)
@@ -1401,8 +1401,9 @@ static cfg_type_t cfg_type_dnstapoutput = {
  *     zone <string> [ policy (given|disabled|passthru|drop|tcp-only|
  *                                     nxdomain|nodata|cname <domain> ) ]
  *                   [ recursive-only yes|no ] [ log yes|no ]
- *                   [ max-policy-ttl number ] ;
+ *                   [ max-policy-ttl number ] [ min-update-interval number ] ;
  *  } [ recursive-only yes|no ] [ max-policy-ttl number ]
+ *      [ min-update-interval number ]
  *      [ break-dnssec yes|no ] [ min-ns-dots number ]
  *      [ qname-wait-recurse yes|no ] ;
  */
@@ -1592,6 +1593,7 @@ static cfg_tuplefielddef_t rpz_zone_fields[] = {
        { "zone name", &cfg_type_rpz_zone, 0 },
        { "log", &cfg_type_boolean, 0 },
        { "max-policy-ttl", &cfg_type_uint32, 0 },
+       { "min-update-interval", &cfg_type_uint32, 0 },
        { "policy", &cfg_type_rpz_policy, 0 },
        { "recursive-only", &cfg_type_boolean, 0 },
        { NULL, NULL, 0 }
@@ -1610,6 +1612,7 @@ static cfg_tuplefielddef_t rpz_fields[] = {
        { "zone list", &cfg_type_rpz_list, 0 },
        { "break-dnssec", &cfg_type_boolean, 0 },
        { "max-policy-ttl", &cfg_type_uint32, 0 },
+       { "min-update-interval", &cfg_type_uint32, 0 },
        { "min-ns-dots", &cfg_type_uint32, 0 },
        { "nsip-wait-recurse", &cfg_type_boolean, 0 },
        { "qname-wait-recurse", &cfg_type_boolean, 0 },