]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] RPZ speedup (phase 2, multiple RPZ's)
authorEvan Hunt <each@isc.org>
Mon, 25 Feb 2013 20:46:51 +0000 (12:46 -0800)
committerEvan Hunt <each@isc.org>
Mon, 25 Feb 2013 20:46:51 +0000 (12:46 -0800)
3495. [func] Support multiple response-policy zones, while
improving RPZ performance. [RT #32476]

52 files changed:
CHANGES
bin/named/include/named/server.h
bin/named/query.c
bin/named/server.c
bin/named/statschannel.c
bin/tests/system/rpz/Makefile.in
bin/tests/system/rpz/clean.sh
bin/tests/system/rpz/ns1/named.conf
bin/tests/system/rpz/ns1/root.db
bin/tests/system/rpz/ns2/base-tld2s.db
bin/tests/system/rpz/ns2/hints
bin/tests/system/rpz/ns2/named.conf
bin/tests/system/rpz/ns2/tld2.db
bin/tests/system/rpz/ns3/base.db
bin/tests/system/rpz/ns3/crash1
bin/tests/system/rpz/ns3/crash2
bin/tests/system/rpz/ns3/hints
bin/tests/system/rpz/ns3/named.conf
bin/tests/system/rpz/ns4/hints
bin/tests/system/rpz/ns4/named.conf
bin/tests/system/rpz/ns4/tld4.db
bin/tests/system/rpz/ns5/hints
bin/tests/system/rpz/ns5/named.args [new file with mode: 0644]
bin/tests/system/rpz/ns5/named.conf
bin/tests/system/rpz/ns5/tld5.db [new file with mode: 0644]
bin/tests/system/rpz/qperf.sh
bin/tests/system/rpz/rpz.c
bin/tests/system/rpz/setup.sh
bin/tests/system/rpz/test1
bin/tests/system/rpz/test2
bin/tests/system/rpz/test3
bin/tests/system/rpz/test4
bin/tests/system/rpz/test4a [new file with mode: 0644]
bin/tests/system/rpz/test5
bin/tests/system/rpz/tests.sh
configure
configure.in
doc/arm/Bv9ARM-book.xml
lib/dns/db.c
lib/dns/ecdb.c
lib/dns/include/dns/db.h
lib/dns/include/dns/rpz.h
lib/dns/include/dns/view.h
lib/dns/include/dns/zone.h
lib/dns/rbtdb.c
lib/dns/rpz.c
lib/dns/sdb.c
lib/dns/sdlz.c
lib/dns/view.c
lib/dns/win32/libdns.def
lib/dns/zone.c
lib/isccfg/namedconf.c

diff --git a/CHANGES b/CHANGES
index 37ab2c427a8d2b050e50a2a841d77d2d7e78cf2a..3d0b776e6f4d272e33017a5893bb271c5353a308 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+3495.  [func]          Support multiple response-policy zones, while
+                       improving RPZ performance. [RT #32476]
+
 3494.  [func]          DNS RRL: Blunt the impact of DNS reflection and
                        amplification attacks by rate-limiting substantially-
                        identical responses. [RT #28130]
index eed11b97a7f5df418066f73f1b243a3f6e7c502f..7e9aaada121c77ea44ce16a339a1af63f72f565c 100644 (file)
@@ -172,7 +172,9 @@ enum {
        dns_nsstatscounter_ratedropped = 38,
        dns_nsstatscounter_rateslipped = 39,
 
-       dns_nsstatscounter_max = 40
+       dns_nsstatscounter_rpz_rewrites = 40,
+
+       dns_nsstatscounter_max = 41
 };
 
 void
index 3f7a96575c654679fc2e89cb3c0684501ce26801..2d3cbadc85a07bd01d9490dd3270d9828bec0dec 100644 (file)
@@ -875,74 +875,93 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
 }
 
 static void
-rpz_log_rewrite(ns_client_t *client, const char *disabled,
+rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled,
                dns_rpz_policy_t policy, dns_rpz_type_t type,
-               dns_name_t *rpz_qname) {
+               dns_zone_t *p_zone, dns_name_t *p_name)
+{
+       isc_stats_t *zonestats;
        char qname_buf[DNS_NAME_FORMATSIZE];
-       char rpz_qname_buf[DNS_NAME_FORMATSIZE];
+       char p_name_buf[DNS_NAME_FORMATSIZE];
+
+       /*
+        * Count enabled rewrites in the global counter.
+        * Count both enabled and disabled rewrites for each zone.
+        */
+       if (!disabled && policy != DNS_RPZ_POLICY_PASSTHRU) {
+               isc_stats_increment(ns_g_server->nsstats,
+                                   dns_nsstatscounter_rpz_rewrites);
+       }
+       if (p_zone != NULL) {
+               zonestats = dns_zone_getrequeststats(p_zone);
+               if (zonestats != NULL)
+                       isc_stats_increment(zonestats,
+                                           dns_nsstatscounter_rpz_rewrites);
+       }
 
        if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
                return;
 
        dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf));
-       dns_name_format(rpz_qname, rpz_qname_buf, sizeof(rpz_qname_buf));
+       dns_name_format(p_name, p_name_buf, sizeof(p_name_buf));
 
        ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
                      DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s",
-                     disabled,
+                     disabled ? "disabled " : "",
                      dns_rpz_type2str(type), dns_rpz_policy2str(policy),
-                     qname_buf, rpz_qname_buf);
+                     qname_buf, p_name_buf);
 }
 
 static void
-rpz_log_fail(ns_client_t *client, int level,
-            dns_rpz_type_t rpz_type, dns_name_t *name,
-            const char *str, isc_result_t result)
+rpz_log_fail(ns_client_t *client, int level, dns_name_t *p_name,
+            dns_rpz_type_t rpz_type, const char *str, isc_result_t result)
 {
-       char namebuf1[DNS_NAME_FORMATSIZE];
-       char namebuf2[DNS_NAME_FORMATSIZE];
+       char qnamebuf[DNS_NAME_FORMATSIZE];
+       char p_namebuf[DNS_NAME_FORMATSIZE];
 
        if (!isc_log_wouldlog(ns_g_lctx, level))
                return;
 
-       dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1));
-       dns_name_format(name, namebuf2, sizeof(namebuf2));
+       /*
+        * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+        */
+       dns_name_format(client->query.qname, qnamebuf, sizeof(qnamebuf));
+       dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
        ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS,
                      NS_LOGMODULE_QUERY, level,
                      "rpz %s rewrite %s via %s %sfailed: %s",
                      dns_rpz_type2str(rpz_type),
-                     namebuf1, namebuf2, str, isc_result_totext(result));
+                     qnamebuf, p_namebuf, str, isc_result_totext(result));
 }
 
 /*
  * Get a policy rewrite zone database.
  */
 static isc_result_t
-rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *rpz_qname,
+rpz_getdb(ns_client_t *client, dns_name_t *p_name, dns_rpz_type_t rpz_type,
          dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp)
 {
-       char namebuf1[DNS_NAME_FORMATSIZE];
-       char namebuf2[DNS_NAME_FORMATSIZE];
+       char qnamebuf[DNS_NAME_FORMATSIZE];
+       char p_namebuf[DNS_NAME_FORMATSIZE];
        dns_dbversion_t *rpz_version = NULL;
        isc_result_t result;
 
-       result = query_getzonedb(client, rpz_qname, dns_rdatatype_any,
+       result = query_getzonedb(client, p_name, dns_rdatatype_any,
                                 DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version);
        if (result == ISC_R_SUCCESS) {
                if (isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) {
-                       dns_name_format(client->query.qname, namebuf1,
-                                       sizeof(namebuf1));
-                       dns_name_format(rpz_qname, namebuf2, sizeof(namebuf2));
+                       dns_name_format(client->query.qname, qnamebuf,
+                                       sizeof(qnamebuf));
+                       dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
                        ns_client_log(client, DNS_LOGCATEGORY_RPZ,
                                      NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2,
                                      "try rpz %s rewrite %s via %s",
                                      dns_rpz_type2str(rpz_type),
-                                     namebuf1, namebuf2);
+                                     qnamebuf, p_namebuf);
                }
                *versionp = rpz_version;
                return (ISC_R_SUCCESS);
        }
-       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname,
+       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
                     "query_getzonedb() ", result);
        return (result);
 }
@@ -3935,7 +3954,7 @@ rpz_clean(dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep,
                dns_rdataset_disassociate(*rdatasetp);
 }
 
-static void
+static inline void
 rpz_match_clear(dns_rpz_st_t *st)
 {
        rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset);
@@ -3943,16 +3962,16 @@ rpz_match_clear(dns_rpz_st_t *st)
 }
 
 static inline isc_result_t
-rpz_ready(ns_client_t *client, dns_zone_t **zonep, dns_db_t **dbp,
-         dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp)
+rpz_ready(ns_client_t *client, dns_rdataset_t **rdatasetp)
 {
        REQUIRE(rdatasetp != NULL);
 
-       rpz_clean(zonep, dbp, nodep, rdatasetp);
        if (*rdatasetp == NULL) {
                *rdatasetp = query_newrdataset(client);
                if (*rdatasetp == NULL)
                        return (DNS_R_SERVFAIL);
+       } else if (dns_rdataset_isassociated(*rdatasetp)) {
+               dns_rdataset_disassociate(*rdatasetp);
        }
        return (ISC_R_SUCCESS);
 }
@@ -3981,13 +4000,80 @@ rpz_st_clear(ns_client_t *client) {
        st->m.policy = DNS_RPZ_POLICY_MISS;
 }
 
+static dns_rpz_zbits_t
+rpz_get_zbits(ns_client_t *client,
+             dns_rdatatype_t ip_type, dns_rpz_type_t rpz_type)
+{
+       dns_rpz_zones_t *rpzs;
+       dns_rpz_st_t *st;
+       dns_rpz_zbits_t zbits;
+
+       rpzs = client->view->rpzs;
+
+       switch (rpz_type) {
+       case DNS_RPZ_TYPE_QNAME:
+               zbits = rpzs->have.qname;
+               break;
+       case DNS_RPZ_TYPE_IP:
+               if (ip_type == dns_rdatatype_a) {
+                       zbits = rpzs->have.ipv4;
+               } else if (ip_type == dns_rdatatype_aaaa) {
+                       zbits = rpzs->have.ipv6;
+               } else {
+                       zbits = rpzs->have.ip;
+               }
+               break;
+       case DNS_RPZ_TYPE_NSDNAME:
+               zbits = rpzs->have.nsdname;
+               break;
+       case DNS_RPZ_TYPE_NSIP:
+               if (ip_type == dns_rdatatype_a) {
+                       zbits = rpzs->have.nsipv4;
+               } else if (ip_type == dns_rdatatype_aaaa) {
+                       zbits = rpzs->have.nsipv6;
+               } else {
+                       zbits = rpzs->have.nsip;
+               }
+               break;
+       default:
+               INSIST(0);
+               break;
+       }
+
+       st = client->query.rpz_st;
+
+       /*
+        * Choose
+        *      the earliest configured policy zone (rpz->num)
+        *      QNAME over IP over NSDNAME over NSIP (rpz_type)
+        *      the smallest name,
+        *      the longest IP address prefix,
+        *      the lexically smallest address.
+        */
+       if (st->m.policy != DNS_RPZ_POLICY_MISS) {
+               if (st->m.type >= rpz_type) {
+                       zbits &= DNS_RPZ_ZMASK(st->m.rpz->num);
+               } else{
+                       zbits &= DNS_RPZ_ZMASK(st->m.rpz->num) >> 1;
+               }
+       }
+
+       /*
+        * If the client wants recursion, allow only compatible policies.
+        */
+       if (!RECURSIONOK(client))
+               zbits &= rpzs->p.no_rd_ok;
+
+       return (zbits);
+}
+
 /*
- * Get NS, A, or AAAA rrset for response policy zone checks.
+ * Get an NS, A, or AAAA rrset related to the response for the client
+ * to check the contents of that rrset for hits by eligible policy zones.
  */
 static isc_result_t
-rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type,
-              dns_name_t *name, dns_rdatatype_t type,
-              dns_db_t **dbp, dns_dbversion_t *version,
+rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
+              dns_rpz_type_t rpz_type, dns_db_t **dbp, dns_dbversion_t *version,
               dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
 {
        dns_rpz_st_t *st;
@@ -3999,15 +4085,13 @@ rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type,
        dns_clientinfomethods_t cm;
        dns_clientinfo_t ci;
 
-       dns_clientinfomethods_init(&cm, ns_client_sourceip);
-       dns_clientinfo_init(&ci, client);
-
        st = client->query.rpz_st;
        if ((st->state & DNS_RPZ_RECURSING) != 0) {
                INSIST(st->r.r_type == type);
                INSIST(dns_name_equal(name, st->r_name));
                INSIST(*rdatasetp == NULL ||
                       !dns_rdataset_isassociated(*rdatasetp));
+               INSIST(*dbp == NULL);
                st->state &= ~DNS_RPZ_RECURSING;
                *dbp = st->r.db;
                st->r.db = NULL;
@@ -4017,16 +4101,15 @@ rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type,
                st->r.r_rdataset = NULL;
                result = st->r.r_result;
                if (result == DNS_R_DELEGATION) {
-                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
-                                    rpz_type, name,
-                                    "rpz_rrset_find(1) ", result);
+                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+                                    rpz_type, "rpz_rrset_find(1) ", result);
                        st->m.policy = DNS_RPZ_POLICY_ERROR;
                        result = DNS_R_SERVFAIL;
                }
                return (result);
        }
 
-       result = rpz_ready(client, NULL, NULL, NULL, rdatasetp);
+       result = rpz_ready(client, rdatasetp);
        if (result != ISC_R_SUCCESS) {
                st->m.policy = DNS_RPZ_POLICY_ERROR;
                return (result);
@@ -4041,9 +4124,8 @@ rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type,
                result = query_getdb(client, name, type, 0, &zone, dbp,
                                     &version, &is_zone);
                if (result != ISC_R_SUCCESS) {
-                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
-                                    rpz_type, name,
-                                    "rpz_rrset_find(2) ", result);
+                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+                                    rpz_type, "rpz_rrset_find(2) ", result);
                        st->m.policy = DNS_RPZ_POLICY_ERROR;
                        if (zone != NULL)
                                dns_zone_detach(&zone);
@@ -4056,6 +4138,8 @@ rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type,
        node = NULL;
        dns_fixedname_init(&fixed);
        found = dns_fixedname_name(&fixed);
+       dns_clientinfomethods_init(&cm, ns_client_sourceip);
+       dns_clientinfo_init(&ci, client);
        result = dns_db_findext(*dbp, name, version, type, DNS_DBFIND_GLUEOK,
                                client->now, &node, found,
                                &cm, &ci, *rdatasetp, NULL);
@@ -4094,175 +4178,94 @@ rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type,
 }
 
 /*
- * Check the IP address in an A or AAAA rdataset against
- * the IP or NSIP response policy rules of a view.
+ * Compute a policy owner name, p_name, in a policy zone given the needed
+ *     policy type and the trigger name.
  */
 static isc_result_t
-rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset,
-              dns_rpz_type_t rpz_type)
-{
-       dns_rpz_st_t *st;
-       dns_dbversion_t *version;
-       dns_zone_t *zone;
-       dns_db_t *db;
-       dns_rpz_zone_t *rpz;
-       isc_result_t result;
-
-       st = client->query.rpz_st;
-       if (st->m.rdataset == NULL) {
-               st->m.rdataset = query_newrdataset(client);
-               if (st->m.rdataset == NULL)
-                       return (DNS_R_SERVFAIL);
-       }
-       zone = NULL;
-       db = NULL;
-       for (rpz = ISC_LIST_HEAD(client->view->rpz_zones);
-            rpz != NULL;
-            rpz = ISC_LIST_NEXT(rpz, link)) {
-               if (!RECURSIONOK(client) && rpz->recursive_only)
-                       continue;
-
-               /*
-                * Do not check policy zones that cannot replace a policy
-                * already known to match.
-                */
-               if (st->m.policy != DNS_RPZ_POLICY_MISS) {
-                       if (st->m.rpz->num < rpz->num)
-                               break;
-                       if (st->m.rpz->num == rpz->num &&
-                           st->m.type < rpz_type)
-                               continue;
-               }
-
-               /*
-                * Find the database for this policy zone to get its radix tree.
-                */
-               version = NULL;
-               result = rpz_getdb(client, rpz_type, &rpz->origin,
-                                  &zone, &db, &version);
-               if (result != ISC_R_SUCCESS) {
-                       rpz_clean(&zone, &db, NULL, NULL);
-                       continue;
-               }
-               /*
-                * Look for a better (e.g. longer prefix) hit for an IP address
-                * in this rdataset in this radix tree than than the previous
-                * hit, if any.  Note the domain name and quality of the
-                * best hit.
-                */
-               dns_db_rpz_findips(rpz, rpz_type, zone, db, version,
-                                  rdataset, st, client->query.rpz_st->qname);
-               rpz_clean(&zone, &db, NULL, NULL);
-       }
-       return (ISC_R_SUCCESS);
-}
-
-/*
- * Look for an A or AAAA rdataset
- * and check for IP or NSIP rewrite policy rules.
- */
-static isc_result_t
-rpz_rewrite_rrset(ns_client_t *client, dns_rpz_type_t rpz_type,
-                 dns_rdatatype_t type, dns_name_t *name,
-                 dns_db_t **dbp, dns_dbversion_t *version,
-                 dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
+rpz_get_p_name(ns_client_t *client, dns_name_t *p_name,
+              dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+              dns_name_t *trig_name)
 {
+       dns_offsets_t prefix_offsets;
+       dns_name_t prefix, *suffix;
+       unsigned int first, labels;
        isc_result_t result;
 
-       result = rpz_rrset_find(client, rpz_type, name, type, dbp, version,
-                               rdatasetp, resuming);
-       switch (result) {
-       case ISC_R_SUCCESS:
-               result = rpz_rewrite_ip(client, *rdatasetp, rpz_type);
+       /*
+        * The policy owner name consists of a suffix depending on the type
+        * and policy zone and a prefix that is the longest possible string
+        * from the trigger name that keesp the resulting policy owner name
+        * from being too long.
+        */
+       switch (rpz_type) {
+       case DNS_RPZ_TYPE_QNAME:
+               suffix = &rpz->origin;
                break;
-       case DNS_R_EMPTYNAME:
-       case DNS_R_EMPTYWILD:
-       case DNS_R_NXDOMAIN:
-       case DNS_R_NCACHENXDOMAIN:
-       case DNS_R_NXRRSET:
-       case DNS_R_NCACHENXRRSET:
-       case ISC_R_NOTFOUND:
-               result = ISC_R_SUCCESS;
+       case DNS_RPZ_TYPE_IP:
+               suffix = &rpz->ip;
                break;
-       case DNS_R_DELEGATION:
-       case DNS_R_DUPLICATE:
-       case DNS_R_DROP:
+       case DNS_RPZ_TYPE_NSDNAME:
+               suffix = &rpz->nsdname;
                break;
-       case DNS_R_CNAME:
-       case DNS_R_DNAME:
-               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, rpz_type,
-                            name, "NS address rewrite rrset ", result);
-               result = ISC_R_SUCCESS;
+       case DNS_RPZ_TYPE_NSIP:
+               suffix = &rpz->nsip;
                break;
        default:
-               if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
-                       client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR;
-                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type,
-                                    name, "NS address rewrite rrset ", result);
-               }
-               break;
+               INSIST(0);
        }
-       return (result);
-}
 
-/*
- * Look for both A and AAAA rdatasets
- * and check for IP or NSIP rewrite policy rules.
- * Look only for addresses that will be in the ANSWER section
- * when checking for IP rules.
- */
-static isc_result_t
-rpz_rewrite_rrsets(ns_client_t *client, dns_rpz_type_t rpz_type,
-                  dns_name_t *name, dns_rdatatype_t type,
-                  dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
-{
-       dns_rpz_st_t *st;
-       dns_dbversion_t *version;
-       dns_db_t *ipdb;
-       isc_result_t result;
-
-       st = client->query.rpz_st;
-       version = NULL;
-       ipdb = NULL;
-       if ((st->state & DNS_RPZ_DONE_IPv4) == 0 &&
-           ((rpz_type == DNS_RPZ_TYPE_NSIP) ?
-            (st->state & DNS_RPZ_HAVE_NSIPv4) :
-            (st->state & DNS_RPZ_HAVE_IP)) != 0 &&
-           (type == dns_rdatatype_any || type == dns_rdatatype_a)) {
-               result = rpz_rewrite_rrset(client, rpz_type, dns_rdatatype_a,
-                                          name, &ipdb, version, rdatasetp,
-                                          resuming);
+       /*
+        * Start with relative version of the full trigger name,
+        * and trim enough allow the addition of the suffix.
+        */
+       dns_name_init(&prefix, prefix_offsets);
+       labels = dns_name_countlabels(trig_name);
+       first = 0;
+       for (;;) {
+               dns_name_getlabelsequence(trig_name, first, labels-first-1,
+                                         &prefix);
+               result = dns_name_concatenate(&prefix, suffix, p_name, NULL);
                if (result == ISC_R_SUCCESS)
-                       st->state |= DNS_RPZ_DONE_IPv4;
-       } else {
-               result = ISC_R_SUCCESS;
+                       return (ISC_R_SUCCESS);
+               INSIST(result == DNS_R_NAMETOOLONG);
+               /*
+                * Trim the trigger name until the combination is not too long.
+                */
+               if (labels-first < 2) {
+                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, suffix,
+                                    rpz_type, "concatentate() ", result);
+                       return (ISC_R_FAILURE);
+               }
+               /*
+                * Complain once about trimming the trigger name.
+                */
+               if (first == 0) {
+                       rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, suffix,
+                                    rpz_type, "concatentate() ", result);
+               }
+               ++first;
        }
-       if (result == ISC_R_SUCCESS &&
-           ((rpz_type == DNS_RPZ_TYPE_NSIP) ?
-            (st->state & DNS_RPZ_HAVE_NSIPv6) :
-            (st->state & DNS_RPZ_HAVE_IP)) != 0 &&
-           (type == dns_rdatatype_any || type == dns_rdatatype_aaaa)) {
-               result = rpz_rewrite_rrset(client, rpz_type, dns_rdatatype_aaaa,
-                                          name, &ipdb, version, rdatasetp,
-                                          resuming);
-       }
-       if (ipdb != NULL)
-               dns_db_detach(&ipdb);
-       return (result);
 }
 
 /*
- * Get the rrset from a response policy zone.
+ * Look in policy zone rpz for a policy of rpz_type by p_name.
+ * The self- name (usually the client qname or an NS name) is compared with
+ * the target of a CNAME policy for the old style passthru encoding.
+ * If found, the policy is recorded in *zonep, *dbp, *versionp, *nodep,
+ * *rdatasetp, and *policyp.
+ * The target DNS type, qtype, chooses the best rdataset for *rdatasetp.
+ * The caller must decide if the found policy is most suitable, including
+ * better than a previously found policy.
+ * If it is best, the caller records it in client->query.rpz_st->m.
  */
 static isc_result_t
-rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
-        dns_name_t *sname, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
-        dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp,
-        dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
-        dns_rpz_policy_t *policyp)
+rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
+          dns_name_t *p_name, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+          dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp,
+          dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
+          dns_rpz_policy_t *policyp)
 {
-       dns_rpz_policy_t policy;
-       dns_fixedname_t fixed;
+       dns_fixedname_t foundf;
        dns_name_t *found;
        isc_result_t result;
        dns_clientinfomethods_t cm;
@@ -4270,31 +4273,28 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
 
        REQUIRE(nodep != NULL);
 
-       dns_clientinfomethods_init(&cm, ns_client_sourceip);
-       dns_clientinfo_init(&ci, client);
-
-       result = rpz_ready(client, zonep, dbp, nodep, rdatasetp);
-       if (result != ISC_R_SUCCESS) {
-               *policyp = DNS_RPZ_POLICY_ERROR;
-               return (result);
-       }
-
        /*
-        * Try to get either a CNAME or the type of record demanded by the
+        * Try to find either a CNAME or the type of record demanded by the
         * request from the policy zone.
         */
+       rpz_clean(zonep, dbp, nodep, rdatasetp);
+       result = rpz_ready(client, rdatasetp);
+       if (result != ISC_R_SUCCESS)
+               return (DNS_R_SERVFAIL);
        *versionp = NULL;
-       result = rpz_getdb(client, rpz_type, qnamef, zonep, dbp, versionp);
-       if (result != ISC_R_SUCCESS) {
-               *policyp = DNS_RPZ_POLICY_MISS;
+       result = rpz_getdb(client, p_name, rpz_type, zonep, dbp, versionp);
+       if (result != ISC_R_SUCCESS)
                return (DNS_R_NXDOMAIN);
-       }
-
-       dns_fixedname_init(&fixed);
-       found = dns_fixedname_name(&fixed);
-       result = dns_db_findext(*dbp, qnamef, *versionp, dns_rdatatype_any, 0,
+       dns_fixedname_init(&foundf);
+       found = dns_fixedname_name(&foundf);
+       dns_clientinfomethods_init(&cm, ns_client_sourceip);
+       dns_clientinfo_init(&ci, client);
+       result = dns_db_findext(*dbp, p_name, *versionp, dns_rdatatype_any, 0,
                                client->now, nodep, found, &cm, &ci,
                                *rdatasetp, NULL);
+       /*
+        * Choose the best rdataset if we found something.
+        */
        if (result == ISC_R_SUCCESS) {
                dns_rdatasetiter_t *rdsiter;
 
@@ -4302,10 +4302,8 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
                result = dns_db_allrdatasets(*dbp, *nodep, *versionp, 0,
                                             &rdsiter);
                if (result != ISC_R_SUCCESS) {
-                       dns_db_detachnode(*dbp, nodep);
-                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type,
-                                    qnamef, "allrdatasets() ", result);
-                       *policyp = DNS_RPZ_POLICY_ERROR;
+                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name,
+                                    rpz_type, "allrdatasets() ", result);
                        return (DNS_R_SERVFAIL);
                }
                for (result = dns_rdatasetiter_first(rdsiter);
@@ -4321,9 +4319,8 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
                if (result != ISC_R_SUCCESS) {
                        if (result != ISC_R_NOMORE) {
                                rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
-                                            rpz_type, qnamef, "rdatasetiter ",
-                                            result);
-                               *policyp = DNS_RPZ_POLICY_ERROR;
+                                            p_name, rpz_type,
+                                            "rdatasetiter ", result);
                                return (DNS_R_SERVFAIL);
                        }
                        /*
@@ -4338,7 +4335,7 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
                            qtype == dns_rdatatype_sig)
                                result = DNS_R_NXRRSET;
                        else
-                               result = dns_db_findext(*dbp, qnamef, *versionp,
+                               result = dns_db_findext(*dbp, p_name, *versionp,
                                                        qtype, 0, client->now,
                                                        nodep, found, &cm, &ci,
                                                        *rdatasetp, NULL);
@@ -4347,198 +4344,533 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
        switch (result) {
        case ISC_R_SUCCESS:
                if ((*rdatasetp)->type != dns_rdatatype_cname) {
-                       policy = DNS_RPZ_POLICY_RECORD;
+                       *policyp = DNS_RPZ_POLICY_RECORD;
                } else {
-                       policy = dns_rpz_decode_cname(rpz, *rdatasetp, sname);
-                       if ((policy == DNS_RPZ_POLICY_RECORD ||
-                            policy == DNS_RPZ_POLICY_WILDCNAME) &&
+                       *policyp = dns_rpz_decode_cname(rpz, *rdatasetp,
+                                                       self_name);
+                       if ((*policyp == DNS_RPZ_POLICY_RECORD ||
+                            *policyp == DNS_RPZ_POLICY_WILDCNAME) &&
                            qtype != dns_rdatatype_cname &&
                            qtype != dns_rdatatype_any)
-                               result = DNS_R_CNAME;
+                               return (DNS_R_CNAME);
                }
-               break;
+               return (ISC_R_SUCCESS);
+       case DNS_R_NXRRSET:
+               *policyp = DNS_RPZ_POLICY_NODATA;
+               return (result);
        case DNS_R_DNAME:
                /*
                 * DNAME policy RRs have very few if any uses that are not
-                * better served with simple wildcards.  Making the work would
+                * better served with simple wildcards.  Making them work would
                 * require complications to get the number of labels matched
                 * in the name or the found name to the main DNS_R_DNAME case
-                * in query_find(). So fall through to treat them as NODATA.
+                * in query_find().  The domain also does not appear in the
+                * summary database at the right level, so this happens only
+                * with a single policy zone when we have no summary database.
+                * Treat it as a miss.
                 */
-       case DNS_R_NXRRSET:
-               policy = DNS_RPZ_POLICY_NODATA;
-               break;
        case DNS_R_NXDOMAIN:
        case DNS_R_EMPTYNAME:
+               return (DNS_R_NXDOMAIN);
+       default:
+               rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
+                            "", result);
+               return (DNS_R_SERVFAIL);
+       }
+}
+
+static void
+rpz_save_p(dns_rpz_st_t *st, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+          dns_rpz_policy_t policy, dns_name_t *p_name, dns_rpz_prefix_t prefix,
+          isc_result_t result, dns_zone_t **zonep, dns_db_t **dbp,
+          dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
+          dns_dbversion_t *version)
+{
+       dns_rdataset_t *trdataset;
+
+       rpz_match_clear(st);
+       st->m.rpz = rpz;
+       st->m.type = rpz_type;
+       st->m.policy = policy;
+       dns_name_copy(p_name, st->p_name, NULL);
+       st->m.prefix = prefix;
+       st->m.result = result;
+       st->m.zone = *zonep;
+       *zonep = NULL;
+       st->m.db = *dbp;
+       *dbp = NULL;
+       st->m.node = *nodep;
+       *nodep = NULL;
+       if (*rdatasetp != NULL && dns_rdataset_isassociated(*rdatasetp)) {
                /*
-                * If we don't get a qname hit,
-                * see if it is worth looking for other types.
+                * Save the replacement rdataset from the policy
+                * and make the previous replacement rdataset scratch.
                 */
-               dns_db_rpz_enabled(*dbp, client->query.rpz_st);
-               dns_db_detach(dbp);
-               dns_zone_detach(zonep);
-               policy = DNS_RPZ_POLICY_MISS;
+               trdataset = st->m.rdataset;
+               st->m.rdataset = *rdatasetp;
+               *rdatasetp = trdataset;
+               st->m.ttl = ISC_MIN(st->m.rdataset->ttl, rpz->max_policy_ttl);
+       } else {
+               st->m.ttl = ISC_MIN(DNS_RPZ_TTL_DEFAULT, rpz->max_policy_ttl);
+       }
+       st->m.version = version;
+}
+
+/*
+ * Check this address in every eligible policy zone.
+ */
+static isc_result_t
+rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
+              dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+              dns_rpz_zbits_t zbits, dns_rdataset_t **p_rdatasetp)
+{
+       dns_rpz_zones_t *rpzs;
+       dns_rpz_st_t *st;
+       dns_rpz_zone_t *rpz;
+       dns_rpz_prefix_t prefix;
+       dns_rpz_num_t rpz_num;
+       dns_fixedname_t ip_namef, p_namef;
+       dns_name_t *ip_name, *p_name;
+       dns_zone_t *p_zone;
+       dns_db_t *p_db;
+       dns_dbversion_t *p_version;
+       dns_dbnode_t *p_node;
+       dns_rpz_policy_t policy;
+       isc_result_t result;
+
+       dns_fixedname_init(&ip_namef);
+       ip_name = dns_fixedname_name(&ip_namef);
+
+       p_zone = NULL;
+       p_db = NULL;
+       p_node = NULL;
+
+       rpzs = client->view->rpzs;
+       st = client->query.rpz_st;
+       while (zbits != 0) {
+               rpz_num = dns_rpz_find_ip(rpzs, rpz_type, zbits, netaddr,
+                                         ip_name, &prefix);
+               if (rpz_num == DNS_RPZ_INVALID_NUM)
+                       break;
+               zbits &= (DNS_RPZ_ZMASK(rpz_num) >> 1);
+
+               /*
+                * Do not try applying policy zones that cannot replace a
+                * previously found policy zone.
+                * Stop looking if the next best choice cannot
+                * replace what we already have.
+                */
+               rpz = rpzs->zones[rpz_num];
+               if (st->m.policy != DNS_RPZ_POLICY_MISS) {
+                       if (st->m.rpz->num < rpz->num)
+                               break;
+                       if (st->m.rpz->num == rpz->num &&
+                           (st->m.type < rpz_type ||
+                            st->m.prefix > prefix))
+                               break;
+               }
+
+               /*
+                * Get the policy for a prefix at least as long
+                * as the prefix of the entry we had before.
+                */
+               dns_fixedname_init(&p_namef);
+               p_name = dns_fixedname_name(&p_namef);
+               result = rpz_get_p_name(client, p_name, rpz, rpz_type, ip_name);
+               if (result != ISC_R_SUCCESS)
+                       continue;
+               result = rpz_find_p(client, ip_name, qtype,
+                                   p_name, rpz, rpz_type,
+                                   &p_zone, &p_db, &p_version, &p_node,
+                                   p_rdatasetp, &policy);
+               switch (result) {
+               case DNS_R_NXDOMAIN:
+                       /*
+                        * Continue after a policy record that is missing
+                        * contrary to the summary data.  The summary
+                        * data can out of date during races with and among
+                        * policy zone updates.
+                        */
+                       continue;
+               case DNS_R_SERVFAIL:
+                       rpz_clean(&p_zone, &p_db, &p_node, p_rdatasetp);
+                       st->m.policy = DNS_RPZ_POLICY_ERROR;
+                       return (DNS_R_SERVFAIL);
+               default:
+                       /*
+                        * Forget this policy if it is not preferable
+                        * to the previously found policy.
+                        * If this policy is not good, then stop looking
+                        * because none of the later policy zones would work.
+                        *
+                        * With more than one applicable policy, prefer
+                        * the earliest configured policy,
+                        * QNAME over IP over NSDNAME over NSIP,
+                        * the longest prefix
+                        * the lexically smallest address.
+                        * dns_rpz_find_ip() ensures st->m.rpz->num >= rpz->num.
+                        * We can compare new and current p_name because
+                        * both are of the same type and in the same zone.
+                        * The tests above eliminate other reasons to
+                        * reject this policy.  If this policy can't work,
+                        * then neither can later zones.
+                        */
+                       if (st->m.policy != DNS_RPZ_POLICY_MISS &&
+                           rpz->num == st->m.rpz->num &&
+                            (st->m.type == rpz_type &&
+                             st->m.prefix == prefix &&
+                             0 > dns_name_rdatacompare(st->p_name, p_name)))
+                               break;
+
+                       /*
+                        * Stop checking after saving an enabled hit.
+                        */
+                       if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
+                               rpz_save_p(st, rpz, rpz_type,
+                                          policy, p_name, prefix, result,
+                                          &p_zone, &p_db, &p_node,
+                                          p_rdatasetp, p_version);
+                               break;
+                       }
+
+                       /*
+                        * Log DNS_RPZ_POLICY_DISABLED zones
+                        * and try the next eligible policy zone.
+                        */
+                       rpz_log_rewrite(client, ISC_TRUE, policy, rpz_type,
+                                       p_zone, p_name);
+               }
+       }
+
+       rpz_clean(&p_zone, &p_db, &p_node, p_rdatasetp);
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Check the IP addresses in the A or AAAA rrsets for name against
+ * all eligible rpz_type (IP or NSIP) response policy rewrite rules.
+ */
+static isc_result_t
+rpz_rewrite_ip_rrset(ns_client_t *client,
+                    dns_name_t *name, dns_rdatatype_t qtype,
+                    dns_rpz_type_t rpz_type, dns_rdatatype_t ip_type,
+                    dns_db_t **ip_dbp, dns_dbversion_t *ip_version,
+                    dns_rdataset_t **ip_rdatasetp,
+                    dns_rdataset_t **p_rdatasetp, isc_boolean_t resuming)
+{
+       dns_rpz_zbits_t zbits;
+       isc_netaddr_t netaddr;
+       struct in_addr ina;
+       struct in6_addr in6a;
+       isc_result_t result;
+
+       zbits = rpz_get_zbits(client, ip_type, rpz_type);
+       if (zbits == 0)
+               return (ISC_R_SUCCESS);
+
+       /*
+        * Get the A or AAAA rdataset.
+        */
+       result = rpz_rrset_find(client, name, ip_type, rpz_type, ip_dbp,
+                               ip_version, ip_rdatasetp, resuming);
+       switch (result) {
+       case ISC_R_SUCCESS:
+       case DNS_R_GLUE:
+       case DNS_R_ZONECUT:
                break;
-       default:
-               dns_db_detach(dbp);
-               dns_zone_detach(zonep);
-               rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef,
-                            "", result);
-               policy = DNS_RPZ_POLICY_ERROR;
-               result = DNS_R_SERVFAIL;
+       case DNS_R_EMPTYNAME:
+       case DNS_R_EMPTYWILD:
+       case DNS_R_NXDOMAIN:
+       case DNS_R_NCACHENXDOMAIN:
+       case DNS_R_NXRRSET:
+       case DNS_R_NCACHENXRRSET:
+       case ISC_R_NOTFOUND:
+               return (ISC_R_SUCCESS);
+       case DNS_R_DELEGATION:
+       case DNS_R_DUPLICATE:
+       case DNS_R_DROP:
+               return (result);
+       case DNS_R_CNAME:
+       case DNS_R_DNAME:
+               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, name, rpz_type,
+                            "NS address rewrite rrset ", result);
+               return (ISC_R_SUCCESS);
                break;
+       default:
+               if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
+                       client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR;
+                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+                                    rpz_type, "NS address rewrite rrset ",
+                                    result);
+               }
+               return (DNS_R_SERVFAIL);
        }
 
-       *policyp = policy;
-       return (result);
+       /*
+        * Check all of the IP addresses in the rdataset.
+        */
+       for (result = dns_rdataset_first(*ip_rdatasetp);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(*ip_rdatasetp)) {
+
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+               dns_rdataset_current(*ip_rdatasetp, &rdata);
+               switch (rdata.type) {
+               case dns_rdatatype_a:
+                       INSIST(rdata.length == 4);
+                       memcpy(&ina.s_addr, rdata.data, 4);
+                       isc_netaddr_fromin(&netaddr, &ina);
+                       break;
+               case dns_rdatatype_aaaa:
+                       INSIST(rdata.length == 16);
+                       memcpy(in6a.s6_addr, rdata.data, 16);
+                       isc_netaddr_fromin6(&netaddr, &in6a);
+                       break;
+               default:
+                       continue;
+               }
+
+               result = rpz_rewrite_ip(client, &netaddr, qtype, rpz_type,
+                                       zbits, p_rdatasetp);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       }
+
+       return (ISC_R_SUCCESS);
 }
 
 /*
- * Build and look for a QNAME or NSDNAME owner name in a response policy zone.
+ * Look for IP addresses in A and AAAA rdatasets
+ * that trigger all eligible IP or NSIP policy rules.
  */
 static isc_result_t
-rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
-                dns_rpz_type_t rpz_type, dns_rdataset_t **rdatasetp)
+rpz_rewrite_ip_rrsets(ns_client_t *client, dns_name_t *name,
+                     dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+                     dns_rdataset_t **ip_rdatasetp, isc_boolean_t resuming)
 {
        dns_rpz_st_t *st;
+       dns_dbversion_t *ip_version;
+       dns_db_t *ip_db;
+       dns_rdataset_t *p_rdataset;
+       isc_result_t result;
+
+       st = client->query.rpz_st;
+       ip_version = NULL;
+       ip_db = NULL;
+       p_rdataset = NULL;
+       if ((st->state & DNS_RPZ_DONE_IPv4) == 0 &&
+           (qtype == dns_rdatatype_a ||
+            qtype == dns_rdatatype_any ||
+            rpz_type == DNS_RPZ_TYPE_NSIP)) {
+               /*
+                * Rewrite based on an IPv4 address that will appear
+                * in the ANSWER section or if we are checking IP addresses.
+                */
+               result = rpz_rewrite_ip_rrset(client, name, qtype,
+                                             rpz_type, dns_rdatatype_a,
+                                             &ip_db, ip_version, ip_rdatasetp,
+                                             &p_rdataset, resuming);
+               if (result == ISC_R_SUCCESS)
+                       st->state |= DNS_RPZ_DONE_IPv4;
+       } else {
+               result = ISC_R_SUCCESS;
+       }
+       if (result == ISC_R_SUCCESS &&
+           (qtype == dns_rdatatype_aaaa ||
+            qtype == dns_rdatatype_any ||
+            rpz_type == DNS_RPZ_TYPE_NSIP)) {
+               /*
+                * Rewrite based on IPv6 addresses that will appear
+                * in the ANSWER section or if we are checking IP addresses.
+                */
+               result = rpz_rewrite_ip_rrset(client, name,  qtype,
+                                             rpz_type, dns_rdatatype_aaaa,
+                                             &ip_db, ip_version, ip_rdatasetp,
+                                             &p_rdataset, resuming);
+       }
+       if (ip_db != NULL)
+               dns_db_detach(&ip_db);
+       query_putrdataset(client, &p_rdataset);
+       return (result);
+}
+
+/*
+ * Try to rewrite a request for a qtype rdataset based on the trigger name
+ * trig_name and rpz_type (DNS_RPZ_TYPE_QNAME or DNS_RPZ_TYPE_NSDNAME).
+ * Record the results including the replacement rdataset if any
+ * in client->query.rpz_st.
+ * *rdatasetp is a scratch rdataset.
+ */
+static isc_result_t
+rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
+                dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+                dns_rpz_zbits_t allowed_zbits, dns_rdataset_t **rdatasetp)
+{
        dns_rpz_zone_t *rpz;
-       dns_fixedname_t prefixf, rpz_qnamef;
-       dns_name_t *prefix, *suffix, *rpz_qname;
-       dns_zone_t *zone;
-       dns_db_t *db;
-       dns_dbversion_t *version;
-       dns_dbnode_t *node;
+       dns_rpz_st_t *st;
+       dns_fixedname_t p_namef;
+       dns_name_t *p_name;
+       dns_rpz_zbits_t zbits;
+       dns_rpz_num_t rpz_num;
+       dns_zone_t *p_zone;
+       dns_db_t *p_db;
+       dns_dbversion_t *p_version;
+       dns_dbnode_t *p_node;
        dns_rpz_policy_t policy;
-       unsigned int labels;
        isc_result_t result;
 
+       zbits = rpz_get_zbits(client, qtype, rpz_type);
+       zbits &= allowed_zbits;
+       if (zbits == 0)
+               return (ISC_R_SUCCESS);
+
+       /*
+        * If there is only one eligible policy zone, just check it.
+        * If more than one, then use the summary database to find
+        * the bit mask of policy zones with policies for this trigger name.
+        *      x&-x is the least significant bit set in x
+        */
+       if (zbits != (zbits & -zbits)) {
+               zbits = dns_rpz_find_name(client->view->rpzs,
+                                         rpz_type, zbits, trig_name);
+               if (zbits == 0)
+                       return (ISC_R_SUCCESS);
+       }
+
+       dns_fixedname_init(&p_namef);
+       p_name = dns_fixedname_name(&p_namef);
+
+       p_zone = NULL;
+       p_db = NULL;
+       p_node = NULL;
+
        st = client->query.rpz_st;
-       zone = NULL;
-       db = NULL;
-       node = NULL;
 
-       for (rpz = ISC_LIST_HEAD(client->view->rpz_zones);
-            rpz != NULL;
-            rpz = ISC_LIST_NEXT(rpz, link)) {
-               if (!RECURSIONOK(client) && rpz->recursive_only)
+       /*
+        * Check the trigger name in every policy zone that the summary data
+        * says has a hit for the trigger name.
+        * Most of the time there are no eligible zones and the summary data
+        * keeps us from getting this far.
+        * We check the most eligible zone first and so usually check only
+        * one policy zone.
+        */
+       for (rpz_num = 0;
+            zbits != 0;
+            ++rpz_num, zbits >>= 1) {
+               if ((zbits & 1) == 0) {
+                       INSIST(rpz_num <= client->view->rpzs->p.num_zones);
                        continue;
+               }
 
                /*
-                * Do not check policy zones that cannot replace a policy
-                * already known to match.
+                * Do not check policy zones that cannot replace a previously
+                * found policy.
                 */
+               rpz = client->view->rpzs->zones[rpz_num];
                if (st->m.policy != DNS_RPZ_POLICY_MISS) {
                        if (st->m.rpz->num < rpz->num)
                                break;
                        if (st->m.rpz->num == rpz->num &&
                            st->m.type < rpz_type)
-                               continue;
-               }
-               /*
-                * Construct the policy's owner name.
-                */
-               dns_fixedname_init(&prefixf);
-               prefix = dns_fixedname_name(&prefixf);
-               dns_name_split(qname, 1, prefix, NULL);
-               if (rpz_type == DNS_RPZ_TYPE_NSDNAME)
-                       suffix = &rpz->nsdname;
-               else
-                       suffix = &rpz->origin;
-               dns_fixedname_init(&rpz_qnamef);
-               rpz_qname = dns_fixedname_name(&rpz_qnamef);
-               for (;;) {
-                       result = dns_name_concatenate(prefix, suffix,
-                                                     rpz_qname, NULL);
-                       if (result == ISC_R_SUCCESS)
                                break;
-                       INSIST(result == DNS_R_NAMETOOLONG);
-                       labels = dns_name_countlabels(prefix);
-                       if (labels < 2) {
-                               rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
-                                            rpz_type, suffix,
-                                            "concatentate() ", result);
-                               return (ISC_R_SUCCESS);
-                       }
-                       if (labels+1 == dns_name_countlabels(qname)) {
-                               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1,
-                                            rpz_type, suffix,
-                                            "concatentate() ", result);
-                       }
-                       dns_name_split(prefix, labels - 1, NULL, prefix);
                }
 
                /*
-                * See if the policy record exists and get its policy.
+                * Get the next policy zone's record for this trigger name.
                 */
-               result = rpz_find(client, qtype, rpz_qname, qname, rpz,
-                                 rpz_type, &zone, &db, &version, &node,
-                                 rdatasetp, &policy);
+               result = rpz_get_p_name(client, p_name, rpz, rpz_type,
+                                       trig_name);
+               if (result != ISC_R_SUCCESS)
+                       continue;
+               result = rpz_find_p(client, trig_name, qtype, p_name,
+                                   rpz, rpz_type,
+                                   &p_zone, &p_db, &p_version, &p_node,
+                                   rdatasetp, &policy);
                switch (result) {
                case DNS_R_NXDOMAIN:
-               case DNS_R_EMPTYNAME:
-                       break;
+                       /*
+                        * Continue after a missing policy record
+                        * contrary to the summary data.  The summary
+                        * data can out of date during races with and among
+                        * policy zone updates.
+                        */
+                       continue;
                case DNS_R_SERVFAIL:
-                       rpz_clean(&zone, &db, &node, rdatasetp);
+                       rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
                        st->m.policy = DNS_RPZ_POLICY_ERROR;
                        return (DNS_R_SERVFAIL);
                default:
                        /*
-                        * We are dealing with names here.
                         * With more than one applicable policy, prefer
                         * the earliest configured policy,
                         * QNAME over IP over NSDNAME over NSIP,
                         * and the smallest name.
-                        * Because of the testing above,
-                        * we known st->m.rpz->num >= rpz->num  and either
+                        * We known st->m.rpz->num >= rpz->num  and either
                         * st->m.rpz->num > rpz->num or st->m.type >= rpz_type
                         */
                        if (st->m.policy != DNS_RPZ_POLICY_MISS &&
                            rpz->num == st->m.rpz->num &&
                            (st->m.type < rpz_type ||
                             (st->m.type == rpz_type &&
-                             0 >= dns_name_compare(rpz_qname, st->qname))))
+                             0 >= dns_name_compare(p_name, st->p_name))))
                                continue;
-
+#if 0
                        /*
-                        * Merely log DNS_RPZ_POLICY_DISABLED hits.
+                        * This code would block a customer reported information
+                        * leak of rpz rules by rewriting requests in the
+                        * rpz-ip, rpz-nsip, rpz-nsdname,and rpz-passthru TLDs.
+                        * Without this code, a bad guy could request
+                        * 24.0.3.2.10.rpz-ip. to find the policy rule for
+                        * 10.2.3.0/14.  It is an insignificant leak and this
+                        * code is not worth its cost, because the bad guy
+                        * could publish "evil.com A 10.2.3.4" and request
+                        * evil.com to get the same information.
+                        * Keep code with "#if 0" in case customer demand
+                        * is irresistible.
+                        *
+                        * We have the less frequent case of a triggered
+                        * policy.  Check that we have not trigger on one
+                        * of the pretend RPZ TLDs.
+                        * This test would make it impossible to rewrite
+                        * names in TLDs that start with "rpz-" should
+                        * ICANN ever allow such TLDs.
                         */
-                       if (rpz->policy == DNS_RPZ_POLICY_DISABLED) {
-                               rpz_log_rewrite(client, "disabled ",
-                                               policy, rpz_type, rpz_qname);
-                               continue;
+                       unsigned int labels;
+                       labels = dns_name_countlabels(trig_name);
+                       if (labels >= 2) {
+                               dns_label_t label;
+
+                               dns_name_getlabel(trig_name, labels-2, &label);
+                               if (label.length >= sizeof(DNS_RPZ_PREFIX)-1 &&
+                                   strncasecmp((const char *)label.base+1,
+                                               DNS_RPZ_PREFIX,
+                                               sizeof(DNS_RPZ_PREFIX)-1) == 0)
+                                       continue;
                        }
-
-                       rpz_match_clear(st);
-                       st->m.rpz = rpz;
-                       st->m.type = rpz_type;
-                       st->m.prefix = 0;
-                       st->m.policy = policy;
-                       st->m.result = result;
-                       dns_name_copy(rpz_qname, st->qname, NULL);
-                       if (*rdatasetp != NULL &&
-                           dns_rdataset_isassociated(*rdatasetp)) {
-                               dns_rdataset_t *trdataset;
-
-                               trdataset = st->m.rdataset;
-                               st->m.rdataset = *rdatasetp;
-                               *rdatasetp = trdataset;
-                               st->m.ttl = ISC_MIN(st->m.rdataset->ttl,
-                                                   rpz->max_policy_ttl);
-                       } else {
-                               st->m.ttl = ISC_MIN(DNS_RPZ_TTL_DEFAULT,
-                                                   rpz->max_policy_ttl);
+#endif
+                       if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
+                               rpz_save_p(st, rpz, rpz_type,
+                                          policy, p_name, 0, result,
+                                          &p_zone, &p_db, &p_node,
+                                          rdatasetp, p_version);
+                               /*
+                                * After a hit, higher numbered policy zones
+                                * are irrelevant
+                                */
+                               rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
+                               return (ISC_R_SUCCESS);
                        }
-                       st->m.node = node;
-                       node = NULL;
-                       st->m.db = db;
-                       db = NULL;
-                       st->m.version = version;
-                       st->m.zone = zone;
-                       zone = NULL;
+                       /*
+                        * Log DNS_RPZ_POLICY_DISABLED zones
+                        * and try the next eligible policy zone.
+                        */
+                       rpz_log_rewrite(client, ISC_TRUE, policy, rpz_type,
+                                       p_zone, p_name);
+                       break;
                }
        }
 
-       rpz_clean(&zone, &db, &node, rdatasetp);
+       rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
        return (ISC_R_SUCCESS);
 }
 
@@ -4551,7 +4883,7 @@ rpz_rewrite_ns_skip(ns_client_t *client, dns_name_t *nsname,
        st = client->query.rpz_st;
 
        if (str != NULL)
-               rpz_log_fail(client, level, DNS_RPZ_TYPE_NSIP, nsname,
+               rpz_log_fail(client, level, nsname, DNS_RPZ_TYPE_NSIP,
                             str, result);
        if (st->r.ns_rdataset != NULL &&
            dns_rdataset_isassociated(st->r.ns_rdataset))
@@ -4571,7 +4903,8 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
        dns_rdataset_t *rdataset;
        dns_fixedname_t nsnamef;
        dns_name_t *nsname;
-       isc_boolean_t ck_ip;
+       int qresult_type;
+       dns_rpz_zbits_t zbits;
        isc_result_t result;
 
        st = client->query.rpz_st;
@@ -4585,10 +4918,10 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                st->m.policy = DNS_RPZ_POLICY_MISS;
                memset(&st->r, 0, sizeof(st->r));
                memset(&st->q, 0, sizeof(st->q));
-               dns_fixedname_init(&st->_qnamef);
+               dns_fixedname_init(&st->_p_namef);
                dns_fixedname_init(&st->_r_namef);
                dns_fixedname_init(&st->_fnamef);
-               st->qname = dns_fixedname_name(&st->_qnamef);
+               st->p_name = dns_fixedname_name(&st->_p_namef);
                st->r_name = dns_fixedname_name(&st->_r_namef);
                st->fname = dns_fixedname_name(&st->_fnamef);
                client->query.rpz_st = st;
@@ -4601,7 +4934,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
        case ISC_R_SUCCESS:
        case DNS_R_GLUE:
        case DNS_R_ZONECUT:
-               ck_ip = ISC_TRUE;
+               qresult_type = 0;
                break;
        case DNS_R_EMPTYNAME:
        case DNS_R_NXRRSET:
@@ -4611,22 +4944,22 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
        case DNS_R_NCACHENXRRSET:
        case DNS_R_CNAME:
        case DNS_R_DNAME:
-               ck_ip = ISC_FALSE;
+               qresult_type = 1;
                break;
        case DNS_R_DELEGATION:
        case ISC_R_NOTFOUND:
-               return (ISC_R_SUCCESS);
+               qresult_type = 2;
+               break;
        case ISC_R_FAILURE:
        case ISC_R_TIMEDOUT:
        case DNS_R_BROKENCHAIN:
-               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, DNS_RPZ_TYPE_QNAME,
-                            client->query.qname,
-                            "stop on qresult in rpz_rewrite() ",
-                            qresult);
+               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, client->query.qname,
+                            DNS_RPZ_TYPE_QNAME,
+                            "stop on qresult in rpz_rewrite() ", qresult);
                return (ISC_R_SUCCESS);
        default:
-               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, DNS_RPZ_TYPE_QNAME,
-                            client->query.qname,
+               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, client->query.qname,
+                            DNS_RPZ_TYPE_QNAME,
                             "stop on unrecognized qresult in rpz_rewrite() ",
                             qresult);
                return (ISC_R_SUCCESS);
@@ -4635,49 +4968,89 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
        rdataset = NULL;
        if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
                /*
-                * Check rules for the query name if this it the first time
-                * for the current qname, i.e. we've not been recursing.
+                * Check triggers for the query name if this is the first time
+                * for the current qname.
                 * There is a first time for each name in a CNAME chain.
                 */
-               result = rpz_rewrite_name(client, qtype, client->query.qname,
-                                         DNS_RPZ_TYPE_QNAME, &rdataset);
+               if (qresult_type == 2) {
+                       /*
+                        * This request needs recursion that has not been done.
+                        * Get bits for the qname policies that do not need
+                        * recursion the results of recursion.
+                        * Bail out to wait for recursion if there are no
+                        * such policies.
+                        */
+                       zbits = client->view->rpzs->have.qname_skip_recurse;
+                       if (zbits == 0)
+                               return (ISC_R_SUCCESS);
+               } else {
+                       zbits = DNS_RPZ_ALL_ZBITS;
+               }
+               result = rpz_rewrite_name(client, client->query.qname, qtype,
+                                         DNS_RPZ_TYPE_QNAME, zbits, &rdataset);
                if (result != ISC_R_SUCCESS)
                        goto cleanup;
 
+               /*
+                * This may have been an attempt to find a qname trigger
+                * before required recursion (qresult_type == 2).
+                * If so and and no pre-recursion triggers hit, quit and
+                * do everything again after recursing. Do not bother saving
+                * the work already done, because recursion is so slow.
+                * If a pre-recursion trigger hit, then no other triggers
+                * can be relevant and so quit.
+                */
+               if (qresult_type == 2)
+                       goto cleanup;
+
                st->r.label = dns_name_countlabels(client->query.qname);
 
+               /*
+                * Check IP addresses for the qname next,
+                * starting with IPv4 addresses.
+                */
                st->state &= ~(DNS_RPZ_DONE_QNAME_IP | DNS_RPZ_DONE_IPv4);
                st->state |= DNS_RPZ_DONE_QNAME;
        }
 
        /*
-        * Check known IP addresses for the query name.
+        * Check known IP addresses for the query name if the database
+        * lookup resulted in some addresses (qresult_type == 0)
+        * and if we have not already checked them.
         * Any recursion required for the query has already happened.
         * Do not check addresses that will not be in the ANSWER section.
         */
-       if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 &&
-           (st->state & DNS_RPZ_HAVE_IP) != 0 && ck_ip) {
-               result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_IP,
-                                           client->query.qname, qtype,
-                                           &rdataset, resuming);
+       if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 && qresult_type == 0 &&
+           rpz_get_zbits(client, qtype, DNS_RPZ_TYPE_IP) != 0) {
+               result = rpz_rewrite_ip_rrsets(client,
+                                              client->query.qname,
+                                              qtype, DNS_RPZ_TYPE_IP,
+                                              &rdataset, resuming);
                if (result != ISC_R_SUCCESS)
                        goto cleanup;
-               st->state &= ~DNS_RPZ_DONE_IPv4;
+               /*
+                * We are finished checking the IP addresses for the qname.
+                * Start with IPv4 if we will check NS IP addesses.
+                */
                st->state |= DNS_RPZ_DONE_QNAME_IP;
+               st->state &= ~DNS_RPZ_DONE_IPv4;
        }
 
        /*
-        * Stop looking for rules if there are none of the other kinds.
+        * Stop looking for rules if there are none of the other kinds
+        * that could override what we already have.
         */
-       if ((st->state & (DNS_RPZ_HAVE_NSIPv4 | DNS_RPZ_HAVE_NSIPv6 |
-                         DNS_RPZ_HAVE_NSDNAME)) == 0) {
+       if (rpz_get_zbits(client, dns_rdatatype_any,
+                         DNS_RPZ_TYPE_NSDNAME) == 0 &&
+           rpz_get_zbits(client, dns_rdatatype_any,
+                         DNS_RPZ_TYPE_NSIP) == 0) {
                result = ISC_R_SUCCESS;
                goto cleanup;
        }
 
        dns_fixedname_init(&nsnamef);
        dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef));
-       while (st->r.label > 1) {
+       while (st->r.label > client->view->rpzs->p.min_ns_labels) {
                /*
                 * Get NS rrset for each domain in the current qname.
                 */
@@ -4691,8 +5064,8 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                if (st->r.ns_rdataset == NULL ||
                    !dns_rdataset_isassociated(st->r.ns_rdataset)) {
                        dns_db_t *db = NULL;
-                       result = rpz_rrset_find(client, DNS_RPZ_TYPE_NSDNAME,
-                                               nsname, dns_rdatatype_ns,
+                       result = rpz_rrset_find(client, nsname, dns_rdatatype_ns,
+                                               DNS_RPZ_TYPE_NSDNAME,
                                                &db, NULL, &st->r.ns_rdataset,
                                                resuming);
                        if (db != NULL)
@@ -4747,7 +5120,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                        dns_rdata_reset(&nsrdata);
                        if (result != ISC_R_SUCCESS) {
                                rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
-                                            DNS_RPZ_TYPE_NSIP, nsname,
+                                            nsname, DNS_RPZ_TYPE_NSIP,
                                             "rdata_tostruct() ", result);
                                st->m.policy = DNS_RPZ_POLICY_ERROR;
                                goto cleanup;
@@ -4764,11 +5137,11 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                         * Check this NS name if we did not handle it
                         * during a previous recursion.
                         */
-                       if ((st->state & DNS_RPZ_DONE_NSDNAME) == 0 &&
-                           (st->state & DNS_RPZ_HAVE_NSDNAME) != 0) {
-                               result = rpz_rewrite_name(client, qtype,
-                                                       &ns.name,
+                       if ((st->state & DNS_RPZ_DONE_NSDNAME) == 0) {
+                               result = rpz_rewrite_name(client, &ns.name,
+                                                       qtype,
                                                        DNS_RPZ_TYPE_NSDNAME,
+                                                       DNS_RPZ_ALL_ZBITS,
                                                        &rdataset);
                                if (result != ISC_R_SUCCESS) {
                                        dns_rdata_freestruct(&ns);
@@ -4779,9 +5152,9 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                        /*
                         * Check all IP addresses for this NS name.
                         */
-                       result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_NSIP,
-                                                   &ns.name, dns_rdatatype_any,
-                                                   &rdataset, resuming);
+                       result = rpz_rewrite_ip_rrsets(client, &ns.name, qtype,
+                                                      DNS_RPZ_TYPE_NSIP,
+                                                      &rdataset, resuming);
                        dns_rdata_freestruct(&ns);
                        if (result != ISC_R_SUCCESS)
                                goto cleanup;
@@ -4791,10 +5164,16 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                } while (result == ISC_R_SUCCESS);
                dns_rdataset_disassociate(st->r.ns_rdataset);
                st->r.label--;
+
+               if (rpz_get_zbits(client, dns_rdatatype_any,
+                                 DNS_RPZ_TYPE_NSDNAME) == 0 &&
+                   rpz_get_zbits(client, dns_rdatatype_any,
+                                 DNS_RPZ_TYPE_NSIP) == 0)
+                       break;
        }
 
        /*
-        * Use the best, if any, hit.
+        * Use the best hit, if any.
         */
        result = ISC_R_SUCCESS;
 
@@ -4808,8 +5187,8 @@ cleanup:
            st->m.policy == DNS_RPZ_POLICY_ERROR) {
                if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU &&
                    result != DNS_R_DELEGATION)
-                       rpz_log_rewrite(client, "", st->m.policy, st->m.type,
-                                       st->qname);
+                       rpz_log_rewrite(client, ISC_FALSE, st->m.policy,
+                                       st->m.type, st->m.zone, st->p_name);
                rpz_match_clear(st);
        }
        if (st->m.policy == DNS_RPZ_POLICY_ERROR) {
@@ -4824,23 +5203,29 @@ cleanup:
 }
 
 /*
- * See if response policy zone rewriting is allowed a lack of interest
+ * See if response policy zone rewriting is allowed by a lack of interest
  * by the client in DNSSEC or a lack of signatures.
  */
 static isc_boolean_t
-rpz_ck_dnssec(ns_client_t *client, isc_result_t result,
+rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
              dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
 {
        dns_fixedname_t fixed;
        dns_name_t *found;
        dns_rdataset_t trdataset;
        dns_rdatatype_t type;
+       isc_result_t result;
 
-       if (client->view->rpz_break_dnssec)
+       if (client->view->rpzs->p.break_dnssec || !WANTDNSSEC(client))
                return (ISC_TRUE);
+
        /*
-        * sigrdataset == NULL if and only !WANTDNSSEC(client)
+        * We do not know if there are signatures if we have not recursed
+        * for them.
         */
+       if (qresult == DNS_R_DELEGATION || qresult == ISC_R_NOTFOUND)
+               return (ISC_FALSE);
+
        if (sigrdataset == NULL)
                return (ISC_TRUE);
        if (dns_rdataset_isassociated(sigrdataset))
@@ -4919,7 +5304,8 @@ rpz_add_cname(ns_client_t *client, dns_rpz_st_t *st,
                                 fname, dns_trust_authanswer, st->m.ttl);
        if (result != ISC_R_SUCCESS)
                return (result);
-       rpz_log_rewrite(client, "", st->m.policy, st->m.type, st->qname);
+       rpz_log_rewrite(client, ISC_FALSE, st->m.policy,
+                       st->m.type, st->m.zone, st->p_name);
        ns_client_qnamereplace(client, fname);
        /*
         * Turn off DNSSEC because the results of a
@@ -5956,13 +6342,15 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                }
        }
 
-       if (!ISC_LIST_EMPTY(client->view->rpz_zones) &&
-           (RECURSIONOK(client) || !client->view->rpz_recursive_only) &&
+       if (client->view->rpzs != NULL &&
+           client->view->rpzs->p.num_zones != 0 &&
+           (RECURSIONOK(client) || client->view->rpzs->p.no_rd_ok != 0) &&
            rpz_ck_dnssec(client, result, rdataset, sigrdataset) &&
            !RECURSING(client) &&
            (client->query.rpz_st == NULL ||
             (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) &&
-           !dns_name_equal(client->query.qname, dns_rootname)) {
+           !dns_name_equal(client->query.qname, dns_rootname))
+       {
                isc_result_t rresult;
 
                rresult = rpz_rewrite(client, qtype, result, resuming);
@@ -6092,8 +6480,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        query_putrdataset(client, &sigrdataset);
                        rpz_st->q.is_zone = is_zone;
                        is_zone = ISC_TRUE;
-                       rpz_log_rewrite(client, "", rpz_st->m.policy,
-                                       rpz_st->m.type, rpz_st->qname);
+                       rpz_log_rewrite(client, ISC_FALSE, rpz_st->m.policy,
+                                       rpz_st->m.type, zone, rpz_st->p_name);
                }
        }
 
index 78a6ae0dcac6709f5a2a46435354b6e238c1e847..9af5770576d7b698f70f26f24c4dce168f4e1e87 100644 (file)
@@ -369,7 +369,8 @@ configure_alternates(const cfg_obj_t *config, dns_view_t *view,
 static isc_result_t
 configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
               const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
-              cfg_aclconfctx_t *aclconf, isc_boolean_t added);
+              cfg_aclconfctx_t *aclconf, isc_boolean_t added,
+              isc_boolean_t old_rpz_ok);
 
 static isc_result_t
 add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
@@ -1557,47 +1558,78 @@ cleanup:
 }
 
 static isc_result_t
-configure_rpz(dns_view_t *view, const cfg_listelt_t *element,
-             isc_boolean_t recursive_only_def, dns_ttl_t ttl_def)
+configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
+                  const char *str, const char *msg)
 {
-       const cfg_obj_t *rpz_obj, *policy_obj, *obj;
+       isc_result_t result;
+
+       result = dns_name_fromstring(name, str, DNS_NAME_DOWNCASE, view->mctx);
+       if (result != ISC_R_SUCCESS)
+               cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                           "invalid %s '%s'", msg, str);
+       return (result);
+}
+
+static isc_result_t
+configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
+                   const char *str, const dns_name_t *origin)
+{
+       isc_result_t result;
+
+       result = dns_name_fromstring2(name, str, origin, DNS_NAME_DOWNCASE,
+                                     view->mctx);
+       if (result != ISC_R_SUCCESS)
+               cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                           "invalid zone '%s'", str);
+       return (result);
+}
+
+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)
+{
+       const cfg_obj_t *rpz_obj, *obj;
        const char *str;
-       dns_rpz_zone_t *old, *new;
-       dns_zone_t *zone = NULL;
+       dns_rpz_zone_t *new;
        isc_result_t result;
+       dns_rpz_num_t rpz_num;
+
+       REQUIRE(old != NULL || !*old_rpz_okp);
+
+       rpz_obj = cfg_listelt_value(element);
+
+       if (view->rpzs->p.num_zones >= DNS_RPZ_MAX_ZONES)
+               return (ISC_R_NOMEMORY);
 
-       new = isc_mem_get(view->mctx, sizeof(*new));
+       new = isc_mem_get(view->rpzs->mctx, sizeof(*new));
        if (new == NULL) {
-               result = ISC_R_NOMEMORY;
-               goto cleanup;
+               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);
+       if (result != ISC_R_SUCCESS) {
+               isc_mem_put(view->rpzs->mctx, new, sizeof(*new));
+               return (result);
+       }
        dns_name_init(&new->origin, NULL);
+       dns_name_init(&new->ip, NULL);
        dns_name_init(&new->nsdname, NULL);
-       dns_name_init(&new->cname, NULL);
+       dns_name_init(&new->nsip, NULL);
        dns_name_init(&new->passthru, NULL);
-       ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link);
-
-       rpz_obj = cfg_listelt_value(element);
-       policy_obj = cfg_tuple_get(rpz_obj, "policy");
-       if (cfg_obj_isvoid(policy_obj)) {
-               new->policy = DNS_RPZ_POLICY_GIVEN;
-       } else {
-               str = cfg_obj_asstring(cfg_tuple_get(policy_obj,
-                                                    "policy name"));
-               new->policy = dns_rpz_str2policy(str);
-               INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
-       }
+       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)) {
-               new->recursive_only = recursive_only_def;
+       if (cfg_obj_isvoid(obj) ? recursive_only_def : cfg_obj_asboolean(obj)) {
+               view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(new->num);
        } else {
-               new->recursive_only = cfg_obj_asboolean(obj);
+               view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(new->num);
        }
-       if (!new->recursive_only)
-               view->rpz_recursive_only = ISC_FALSE;
 
        obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
        if (cfg_obj_isuint32(obj)) {
@@ -1605,77 +1637,177 @@ configure_rpz(dns_view_t *view, const cfg_listelt_t *element,
        } else {
                new->max_policy_ttl = ttl_def;
        }
+       if (*old_rpz_okp && new->max_policy_ttl != old->max_policy_ttl)
+               *old_rpz_okp = ISC_FALSE;
 
        str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name"));
-       result = dns_name_fromstring(&new->origin, str, DNS_NAME_DOWNCASE,
-                                    view->mctx);
-       if (result != ISC_R_SUCCESS) {
+       result = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone");
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       if (dns_name_equal(&new->origin, dns_rootname)) {
                cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                           "invalid zone '%s'", str);
-               goto cleanup;
+                           "invalid zone name '%s'", str);
+               return (DNS_R_EMPTYLABEL);
        }
-
-       result = dns_name_fromstring2(&new->nsdname, DNS_RPZ_NSDNAME_ZONE,
-                                     &new->origin, DNS_NAME_DOWNCASE,
-                                     view->mctx);
-       if (result != ISC_R_SUCCESS) {
-               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                           "invalid zone '%s'", str);
-               goto cleanup;
+       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)) {
+                       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))
+               *old_rpz_okp = ISC_FALSE;
 
-       result = dns_name_fromstring(&new->passthru, DNS_RPZ_PASSTHRU_ZONE,
-                                    DNS_NAME_DOWNCASE, view->mctx);
-       if (result != ISC_R_SUCCESS) {
-               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                           "invalid zone '%s'", str);
-               goto cleanup;
-       }
+       result = configure_rpz_name2(view, rpz_obj, &new->ip,
+                                    DNS_RPZ_IP_ZONE, &new->origin);
+       if (result != ISC_R_SUCCESS)
+               return (result);
 
-       result = dns_view_findzone(view, &new->origin, &zone);
-       if (result != ISC_R_SUCCESS) {
-               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                           "unknown zone '%s'", str);
-               goto cleanup;
-       }
-       if (dns_zone_gettype(zone) != dns_zone_master &&
-           dns_zone_gettype(zone) != dns_zone_slave) {
-               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                            "zone '%s' is neither master nor slave", str);
-               dns_zone_detach(&zone);
-               result = DNS_R_NOTMASTER;
-               goto cleanup;
-       }
-       dns_zone_detach(&zone);
+       result = configure_rpz_name2(view, rpz_obj, &new->nsdname,
+                                    DNS_RPZ_NSDNAME_ZONE, &new->origin);
+       if (result != ISC_R_SUCCESS)
+               return (result);
 
-       for (old = ISC_LIST_HEAD(view->rpz_zones);
-            old != new;
-            old = ISC_LIST_NEXT(old, link)) {
-               ++new->num;
-               if (dns_name_equal(&old->origin, &new->origin)) {
-                       cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                                   "duplicate '%s'", str);
-                       result = DNS_R_DUPLICATE;
-                       goto cleanup;
+       result = configure_rpz_name2(view, rpz_obj, &new->nsip,
+                                    DNS_RPZ_NSIP_ZONE, &new->origin);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       result = configure_rpz_name(view, rpz_obj, &new->passthru,
+                                   DNS_RPZ_PASSTHRU_ZONE, "zone");
+       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;
+       } 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) {
+                       str = cfg_obj_asstring(cfg_tuple_get(obj, "cname"));
+                       result = configure_rpz_name(view, rpz_obj, &new->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)))
+               *old_rpz_okp = ISC_FALSE;
+
+       return (ISC_R_SUCCESS);
+}
 
-       if (new->policy == DNS_RPZ_POLICY_CNAME) {
-               str = cfg_obj_asstring(cfg_tuple_get(policy_obj, "cname"));
-               result = dns_name_fromstring(&new->cname, str,
-                                            DNS_NAME_DOWNCASE, view->mctx);
+static isc_result_t
+configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
+             isc_boolean_t *old_rpz_okp)
+{
+       const cfg_listelt_t *zone_element;
+       const cfg_obj_t *sub_obj;
+       isc_boolean_t recursive_only_def;
+       dns_ttl_t ttl_def;
+       dns_rpz_zones_t *new;
+       const dns_rpz_zones_t *old;
+       dns_view_t *pview;
+       const dns_rpz_zone_t *old_zone;
+       isc_result_t result;
+       int i;
+
+       *old_rpz_okp = ISC_FALSE;
+
+       zone_element = cfg_list_first(cfg_tuple_get(rpz_obj, "zone list"));
+       if (zone_element == NULL)
+               return (ISC_R_SUCCESS);
+
+       result = dns_rpz_new_zones(&view->rpzs, view->mctx);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       new = view->rpzs;
+
+       sub_obj = cfg_tuple_get(rpz_obj, "recursive-only");
+       if (!cfg_obj_isvoid(sub_obj) &&
+           !cfg_obj_asboolean(sub_obj))
+               recursive_only_def = ISC_FALSE;
+       else
+               recursive_only_def = ISC_TRUE;
+
+       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;
+       else
+               new->p.break_dnssec = ISC_FALSE;
+
+       sub_obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
+       if (cfg_obj_isuint32(sub_obj))
+               ttl_def = cfg_obj_asuint32(sub_obj);
+       else
+               ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
+
+       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;
+       else
+               new->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;
+       else
+               new->p.qname_wait_recurse = ISC_FALSE;
+
+       pview = NULL;
+       result = dns_viewlist_find(&ns_g_server->viewlist,
+                                  view->name, view->rdclass, &pview);
+       if (result == ISC_R_SUCCESS) {
+               old = pview->rpzs;
+       } else {
+               old = NULL;
+       }
+       if (old == NULL)
+               *old_rpz_okp = ISC_FALSE;
+       else
+               *old_rpz_okp = ISC_TRUE;
+
+       for (i = 0;
+            zone_element != NULL;
+            ++i, zone_element = cfg_list_next(zone_element)) {
+               if (*old_rpz_okp && i < old->p.num_zones) {
+                       old_zone = old->zones[i];
+               } else {
+                       *old_rpz_okp = ISC_FALSE;
+                       old_zone = NULL;
+               }
+               result = configure_rpz_zone(view, zone_element,
+                                           recursive_only_def, ttl_def,
+                                           old_zone, old_rpz_okp);
                if (result != ISC_R_SUCCESS) {
-                       cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                                   "invalid cname '%s'", str);
-                       goto cleanup;
+                       if (pview != NULL)
+                               dns_view_detach(&pview);
+                       return (result);
                }
        }
 
-       return (ISC_R_SUCCESS);
+       /*
+        * If this is a reloading and the parameters and list of policy
+        * 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)
+               *old_rpz_okp = ISC_FALSE;
+       if (*old_rpz_okp) {
+               dns_rpz_detach_rpzs(&view->rpzs);
+               dns_rpz_attach_rpzs(pview->rpzs, &view->rpzs);
+       }
+       if (pview != NULL)
+               dns_view_detach(&pview);
 
- cleanup:
-       dns_rpz_view_destroy(view);
-       return (result);
+       return (ISC_R_SUCCESS);
 }
 
 #define CHECK_RRL(obj, cond, pat, val1, val2)                          \
@@ -1938,6 +2070,7 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
        unsigned int query_timeout, ndisp;
        struct cfg_context *nzctx;
+       isc_boolean_t old_rpz_ok = ISC_FALSE;
 
        REQUIRE(DNS_VIEW_VALID(view));
 
@@ -2035,6 +2168,16 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
                                         &view->queryacl));
        }
 
+       /*
+        * Make the list of response policy zone names for a view that
+        * is used for real lookups and so cares about hints.
+        */
+       obj = NULL;
+       if (view->rdclass == dns_rdataclass_in && need_hints &&
+           ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
+               CHECK(configure_rpz(view, obj, &old_rpz_ok));
+       }
+
        /*
         * Configure the zones.
         */
@@ -2053,7 +2196,30 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        {
                const cfg_obj_t *zconfig = cfg_listelt_value(element);
                CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
-                                    actx, ISC_FALSE));
+                                    actx, ISC_FALSE, old_rpz_ok));
+       }
+
+       /*
+        * Check that a master or slave zone was found for each
+        * zone named in the response policy statement.
+        */
+       if (view->rpzs != NULL) {
+               dns_rpz_num_t n;
+
+               for (n = 0; n < view->rpzs->p.num_zones; ++n)
+               {
+                       if ((view->rpzs->defined & DNS_RPZ_ZBIT(n)) == 0) {
+                               char namebuf[DNS_NAME_FORMATSIZE];
+
+                               dns_name_format(&view->rpzs->zones[n]->origin,
+                                               namebuf, sizeof(namebuf));
+                               cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                                           "'%s' is not a master or slave zone",
+                                           namebuf);
+                               result = ISC_R_NOTFOUND;
+                               goto cleanup;
+                       }
+               }
        }
 
        /*
@@ -2078,7 +2244,7 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
                        const cfg_obj_t *zconfig = cfg_listelt_value(element);
                        CHECK(configure_zone(config, zconfig, vconfig,
                                             mctx, view, actx,
-                                            ISC_TRUE));
+                                            ISC_TRUE, ISC_FALSE));
                }
        }
 
@@ -3250,49 +3416,6 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
                }
        }
 
-       /*
-        * Make the list of response policy zone names for views that
-        * are used for real lookups and so care about hints.
-        */
-       obj = NULL;
-       if (view->rdclass == dns_rdataclass_in && need_hints &&
-           ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
-               const cfg_obj_t *recursive_only_obj;
-               const cfg_obj_t *break_dnssec_obj, *ttl_obj;
-               isc_boolean_t recursive_only_def;
-               dns_ttl_t ttl_def;
-
-               recursive_only_obj = cfg_tuple_get(obj, "recursive-only");
-               if (!cfg_obj_isvoid(recursive_only_obj) &&
-                   !cfg_obj_asboolean(recursive_only_obj))
-                       recursive_only_def = ISC_FALSE;
-               else
-                       recursive_only_def = ISC_TRUE;
-
-               break_dnssec_obj = cfg_tuple_get(obj, "break-dnssec");
-               if (!cfg_obj_isvoid(break_dnssec_obj) &&
-                   cfg_obj_asboolean(break_dnssec_obj))
-                       view->rpz_break_dnssec = ISC_TRUE;
-               else
-                       view->rpz_break_dnssec = ISC_FALSE;
-
-               ttl_obj = cfg_tuple_get(obj, "max-policy-ttl");
-               if (cfg_obj_isuint32(ttl_obj))
-                       ttl_def = cfg_obj_asuint32(ttl_obj);
-               else
-                       ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
-
-               for (element = cfg_list_first(cfg_tuple_get(obj, "zone list"));
-                    element != NULL;
-                    element = cfg_list_next(element)) {
-                       result = configure_rpz(view, element,
-                                              recursive_only_def, ttl_def);
-                       if (result != ISC_R_SUCCESS)
-                               goto cleanup;
-                       dns_rpz_set_need(ISC_TRUE);
-               }
-       }
-
        obj = NULL;
        result = ns_config_get(maps, "rate-limit", &obj);
        if (result == ISC_R_SUCCESS) {
@@ -3633,7 +3756,8 @@ create_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
 static isc_result_t
 configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
               const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
-              cfg_aclconfctx_t *aclconf, isc_boolean_t added)
+              cfg_aclconfctx_t *aclconf, isc_boolean_t added,
+              isc_boolean_t old_rpz_ok)
 {
        dns_view_t *pview = NULL;       /* Production view */
        dns_zone_t *zone = NULL;        /* New or reused zone */
@@ -3654,6 +3778,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
        const char *zname;
        dns_rdataclass_t zclass;
        const char *ztypestr;
+       dns_rpz_num_t rpz_num;
 
        options = NULL;
        (void)cfg_map_get(config, "options", &options);
@@ -3814,6 +3939,18 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
        }
        INSIST(dupzone == NULL);
 
+       /*
+        * Note whether this is a response policy zone and which one if so.
+        */
+       for (rpz_num = 0; ; ++rpz_num) {
+               if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones) {
+                       rpz_num = DNS_RPZ_INVALID_NUM;
+                       break;
+               }
+               if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin, origin))
+                       break;
+       }
+
        /*
         * See if we can reuse an existing zone.  This is
         * only possible if all of these are true:
@@ -3822,6 +3959,9 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
         *   - The zone is compatible with the config
         *     options (e.g., an existing master zone cannot
         *     be reused if the options specify a slave zone)
+        *   - The zone was not and is still not a response policy zone
+        *     or the zone is a policy zone with an unchanged number
+        *     and we are using the old policy zone summary data.
         */
        result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
                                   view->rdclass, &pview);
@@ -3835,6 +3975,10 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
        if (zone != NULL && !ns_zone_reusable(zone, zconfig))
                dns_zone_detach(&zone);
 
+       if (zone != NULL && (rpz_num != dns_zone_get_rpz_num(zone) ||
+                            (rpz_num != DNS_RPZ_INVALID_NUM && !old_rpz_ok)))
+               dns_zone_detach(&zone);
+
        if (zone != NULL) {
                /*
                 * We found a reusable zone.  Make it use the
@@ -3857,6 +4001,19 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                dns_zone_setstats(zone, ns_g_server->zonestats);
        }
 
+       if (rpz_num != DNS_RPZ_INVALID_NUM) {
+               result = dns_zone_rpz_enable(zone, view->rpzs, rpz_num);
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                     NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                                     "zone '%s': incompatible"
+                                     " masterfile-format or database"
+                                     " for a response policy zone",
+                                     zname);
+                       goto cleanup;
+               }
+       }
+
        /*
         * If the zone contains a 'forwarders' statement, configure
         * selective forwarding.
@@ -8057,7 +8214,8 @@ ns_server_add_zone(ns_server_t *server, char *args) {
        isc_task_beginexclusive(server->task);
        dns_view_thaw(view);
        result = configure_zone(cfg->config, parms, vconfig,
-                               server->mctx, view, cfg->actx, ISC_FALSE);
+                               server->mctx, view, cfg->actx, ISC_FALSE,
+                               ISC_FALSE);
        dns_view_freeze(view);
        isc_task_endexclusive(server->task);
        if (result != ISC_R_SUCCESS)
index 4b2f38767ffe97478044a07e06591972cfe4565a..325b9d63a78297abe87b31a71e8f931aa4816daf 100644 (file)
@@ -211,6 +211,8 @@ init_desc(void) {
                       "RateDropped");
        SET_NSSTATDESC(rateslipped, "responses truncated for rate limits",
                       "RateSlipped");
+       SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
+                      "RPZRewrites");
        INSIST(i == dns_nsstatscounter_max);
 
        /* Initialize resolver statistics */
index 1fa844f17beedb0cd2913096eb1f35d9da71693e..dcbffc25c00b92f2c37fbe709b94aeea8f32fcab 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 #
 # Permission to use, copy, modify, and/or distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.3 2011/01/13 04:59:24 tbox Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
index 82cfcb29094b6e4044b6dd902dfde8de15e15476..6a22b13d1f49b3009bf135283cffbed0b1798cc5 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 #
 # Permission to use, copy, modify, and/or distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: clean.sh,v 1.6 2012/01/07 23:46:53 tbox Exp $
 
 
 # Clean up after rpz tests.
 
-rm -f proto.*  dsset-* random.data trusted.conf dig.out* nsupdate.tmp ns*/*tmp
+rm -f proto.* dsset-* random.data trusted.conf dig.out* nsupdate.tmp ns*/*tmp
 rm -f ns*/*.key ns*/*.private ns2/tld2s.db
 rm -f ns3/bl*.db ns*/*switch ns5/requests ns5/example.db ns5/bl.db ns5/*.perf
-rm -f */named.memstats */named.run */named.rpz */session.key
+rm -f */named.memstats */named.run */named.stats */session.key
 rm -f */*.jnl */*.core */*.pid
index 82de8029d534929f8a48eb5c457e89f03a528181..9b3b8a33f535657aab33a1b10aeae56ce31e6200 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: named.conf,v 1.3 2011/01/13 04:59:24 tbox Exp $ */
 
 controls { /* empty */ };
 
index c8d8671c7f6b60751cd181a8c1e6f4ac205d5e7f..b59b312829fde85c927932a2de9d80df56409c9a 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: root.db,v 1.6 2012/01/07 23:46:53 tbox Exp $
 
 $TTL   120
-@              SOA     ns. hostmaster.ns. ( 1 3600 1200 604800 60 )
-@              NS      ns.
+.              SOA     ns. hostmaster.ns. ( 1 3600 1200 604800 60 )
+               NS      ns.
 ns.            A       10.53.0.1
-.              A       10.53.0.1
 
 ; rewrite responses from this zone
 tld2.          NS      ns.tld2.
@@ -34,3 +32,7 @@ ns.tld3.      A       10.53.0.3
 ; rewrite responses from this zone
 tld4.          NS      ns.tld4.
 ns.tld4.       A       10.53.0.4
+
+; performance test
+tld5.          NS      ns.tld5.
+ns.tld5.       A       10.53.0.5
index 463f2cee3a2f3b77d11099b34e6aa517074164b2..991a5040e46cfe39dafc4fcd161a98481412671d 100644 (file)
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: base-tld2s.db,v 1.1.2.1 2012/02/24 17:22:37 vjs Exp $
 
 
 ; RPZ rewrite responses from this signed zone
index 849e2e3ab4904ef3ab78c3c95536f7f0936e83d0..2393dd49924bcdf988a28f700b6677208aeac09a 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: hints,v 1.4 2011/10/13 01:32:33 vjs Exp $
 
-
-.      0       NS      ns1.
-ns1.   0       A       10.53.0.1
+.      120     NS      ns.
+ns.    120     A       10.53.0.1
index a2fcd8254f8baad573884b558d35feb9483e87ed..80f00f0c4d8ea024e7eea186edff01facf2d39f5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: named.conf,v 1.6 2012/01/07 23:46:53 tbox Exp $ */
 
 
 controls { /* empty */ };
index 932a168581e6e332f1fe5b95fc46eeab7d7e4a93..f9d103dcdc237d82c6cbc46162f8f9c720da53bd 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: tld2.db,v 1.4.4.2 2012/02/24 17:22:37 vjs Exp $
 
 
 ; RPZ rewrite responses from this zone
@@ -110,6 +109,9 @@ a5-1-2              A       192.168.5.1
                A       192.168.5.2
                TXT     "a5-1-2 tld2 text"
 
+a5-2           A       192.168.5.2
+               TXT     "a5-2 tld2 text"
+
 a5-3           A       192.168.5.3
                TXT     "a5-3 tld2 text"
 
index 18710f90fd1fb488a90e859125ecff30ea42e2d3..c60181a5e86639905bdbe54011070258b27db5bc 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: base.db,v 1.6.4.1 2011/10/15 23:03:38 vjs Exp $
 
 
 ; RPZ test
@@ -40,3 +39,10 @@ ns   A       10.53.0.3
 redirect       A       127.0.0.1
 *.redirect     A       127.0.0.1
 *.credirect    CNAME   google.com.
+
+
+; names in the RPZ TLDs that some say should not be rewritten.
+; This is not a bug, because any data leaked by writing 24.4.3.2.10.rpz-ip
+; (or whatever) is available by publishing "foo A 10.2.3.4" and then
+; resolving foo.
+32.3.2.1.127.rpz-ip                                    CNAME   walled.invalid.
index c241f75a210eec083680833739806d5fec2efe48..d0862edc9713c68f72a35149e43862e8f64f4601 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: crash1,v 1.2 2011/10/13 04:53:06 marka Exp $
 
 ; a bad zone that caused a crash related to dns_rdataset_disassociate()
 
index cd613bce90549e108d1438c79e96f0433fdb8b93..25f4fcf6c0c0acc9ba36cf80dee97848563248a0 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: crash2,v 1.3 2011/11/18 19:32:13 each Exp $
 
 ; a valid zone containing records that caused crashes
 
index 849e2e3ab4904ef3ab78c3c95536f7f0936e83d0..2393dd49924bcdf988a28f700b6677208aeac09a 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: hints,v 1.4 2011/10/13 01:32:33 vjs Exp $
 
-
-.      0       NS      ns1.
-ns1.   0       A       10.53.0.1
+.      120     NS      ns.
+ns.    120     A       10.53.0.1
index 0906e7b32b1ee3e96bc1f8a84ebc9695964176c4..bc5bacdc487f3af0c1cf5ce1dc1c34176b4199f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: named.conf,v 1.5 2011/10/28 11:46:50 marka Exp $ */
 
 
 /*
@@ -27,6 +26,7 @@ options {
        transfer-source 10.53.0.3;
        port 5300;
        pid-file "named.pid";
+       statistics-file "named.stats";
        session-keyfile "session.key";
        listen-on { 10.53.0.3; };
        listen-on-v6 { none; };
@@ -44,7 +44,9 @@ options {
            zone "bl-cname"     policy cname txt-only.tld2.;
            zone "bl-wildcname" policy cname *.tld4.;
            zone "bl-garden"    policy cname a12.tld2.;
-       };
+       } min-ns-dots 0
+       # qname-wait-recurse no
+       ;
 };
 
 key rndc_key {
@@ -55,19 +57,7 @@ controls {
        inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
 };
 
-logging {
-       # change "-c named.conf -d 99 -g" to "-c named.conf -d 99 -f"
-       #   in ../start.pl to check the rpz log category
-       channel rpz { severity debug 10;
-               print-category yes; print-time yes; print-severity yes;
-               file "named.rpz";};
-       category rpz { default_stderr; rpz; };
-       category queries { default_stderr; rpz; };
-       category query-errors { default_stderr; };
-};
-
 
-// include "../trusted.conf";
 zone "." { type hint; file "hints"; };
 
 zone "bl."             {type master; file "bl.db";
index cbcff7c988447bc67ea6fa853348d19db7149e37..2393dd49924bcdf988a28f700b6677208aeac09a 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: hints,v 1.2 2011/10/13 04:53:06 marka Exp $
 
-.      0       NS      ns1.
-ns1.   0       A       10.53.0.1
+.      120     NS      ns.
+ns.    120     A       10.53.0.1
index 6828e86eb859a29d4391f8f2095d141159a6d149..2ab7c3ca4cb96c0730f271fc9adf0e256cc4041e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: named.conf,v 1.2.6.1 2012/02/24 17:22:37 vjs Exp $ */
 
 controls { /* empty */ };
 
index 810189a0374183a66fe642d548cf4694215a77cb..5f94f494c478819d3fc53d3d31758c77dbc79d56 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: tld4.db,v 1.2.6.1 2012/02/24 17:22:37 vjs Exp $
 
 ; RPZ rewrite responses from this zone
 
index ae0e569e7f3bed980b3a3d2f4aae767f2cfb9e52..2393dd49924bcdf988a28f700b6677208aeac09a 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: hints,v 1.1.2.1 2011/10/15 23:00:04 vjs Exp $
 
-
-.      0       NS      ns1.
-ns1.   0       A       10.53.0.1
+.      120     NS      ns.
+ns.    120     A       10.53.0.1
diff --git a/bin/tests/system/rpz/ns5/named.args b/bin/tests/system/rpz/ns5/named.args
new file mode 100644 (file)
index 0000000..acf35ae
--- /dev/null
@@ -0,0 +1,3 @@
+# run the performace test close to real life
+
+-c named.conf -g
index 533dce8216c503d3e1e23f82670d27a52ea794d6..486d4cb7b9af9988d6ea07dc65db804d83218e8c 100644 (file)
@@ -14,7 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: named.conf,v 1.1.2.2 2012/02/24 17:22:38 vjs Exp $ */
 
 
 /*
@@ -27,12 +26,13 @@ options {
        transfer-source 10.53.0.5;
        port 5300;
        pid-file "named.pid";
+       statistics-file "named.stats";
        session-keyfile "session.key";
        listen-on { 10.53.0.5; };
        listen-on-v6 { none; };
        notify no;
 
-       # Eventually turn rpz on.
+       # turn rpz on or off
        include "rpz-switch";
 };
 
@@ -40,12 +40,34 @@ key rndc_key {
        secret "1234abcd8765";
        algorithm hmac-md5;
 };
-controls { inet 10.53.0.5 port 9953 allow { any; } keys { rndc_key; }; };
+controls {
+       inet 10.53.0.5 port 9953 allow { any; } keys { rndc_key; };
+};
 
 
 include "../trusted.conf";
 zone "."               {type hint; file "hints"; };
 
-zone "example.com."    {type master; file "example.db"; };
+zone "tld5."           {type master; file "tld5.db"; };
+zone "example.tld5."   {type master; file "example.db"; };
 
-zone "bl."             {type master; file "bl.db"; };
+zone "bl0."            {type master; file "bl.db"; };
+zone "bl1."            {type master; file "bl.db"; };
+zone "bl2."            {type master; file "bl.db"; };
+zone "bl3."            {type master; file "bl.db"; };
+zone "bl4."            {type master; file "bl.db"; };
+zone "bl5."            {type master; file "bl.db"; };
+zone "bl6."            {type master; file "bl.db"; };
+zone "bl7."            {type master; file "bl.db"; };
+zone "bl8."            {type master; file "bl.db"; };
+zone "bl9."            {type master; file "bl.db"; };
+zone "bl10."           {type master; file "bl.db"; };
+zone "bl11."           {type master; file "bl.db"; };
+zone "bl12."           {type master; file "bl.db"; };
+zone "bl13."           {type master; file "bl.db"; };
+zone "bl14."           {type master; file "bl.db"; };
+zone "bl15."           {type master; file "bl.db"; };
+zone "bl16."           {type master; file "bl.db"; };
+zone "bl17."           {type master; file "bl.db"; };
+zone "bl18."           {type master; file "bl.db"; };
+zone "bl19."           {type master; file "bl.db"; };
diff --git a/bin/tests/system/rpz/ns5/tld5.db b/bin/tests/system/rpz/ns5/tld5.db
new file mode 100644 (file)
index 0000000..dd90aa1
--- /dev/null
@@ -0,0 +1,35 @@
+; Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+
+
+; RPZ preformance test
+
+$TTL   120
+@      SOA     .  hostmaster.ns.example.tld5. ( 1 3600 1200 604800 60 )
+       NS      ns
+       NS      ns1
+       NS      ns2
+       NS      ns3
+ns     A       10.53.0.5
+ns1    A       10.53.0.5
+ns2    A       10.53.0.5
+ns3    A       10.53.0.5
+
+
+$ORIGIN        example.tld5.
+example.tld5.  NS      ns
+               NS      ns1
+ns             A       10.53.0.5
+ns1            A       10.53.0.5
index a823f1b690957acec03691ddf5e9b1f9a6db1708..c5877516fd61499e5b80ca726bf2b65d5814a747 100644 (file)
@@ -14,7 +14,6 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: qperf.sh,v 1.1.2.1 2011/10/15 23:03:37 vjs Exp $
 
 for QDIR in `echo "$PATH" | tr : ' '` ../../../../contrib/queryperf; do
     QPERF=$QDIR/queryperf
index 8a357ddbafbc84e7145fe6e34bbb958210f922a4..2fae00a4f8637776ad998ba28af305ce202b745c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rpz.c,v 1.3.226.1 2011/10/15 23:03:37 vjs Exp $ */
 
 #include <config.h>
 
index 1bc879c78972636bcb6b1bc4ec0d97acad6d81b6..521db65c4d95bbddc03da47ccb7c01b249e2479e 100644 (file)
@@ -1,6 +1,6 @@
 #! /bin/sh
 #
-# Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 #
 # Permission to use, copy, modify, and/or distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,6 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: setup.sh,v 1.6 2012/01/07 23:46:53 tbox Exp $
 
 set -e
 
@@ -48,19 +47,25 @@ signzone ns2 tld2s. base-tld2s.db tld2s.db
 
 
 # Performance checks.
-# First with rpz off.
 cat <<EOF >ns5/rpz-switch
-response-policy {zone "bl";}
-    recursive-only no
-    max-policy-ttl 90
-    break-dnssec yes;
+response-policy {
+       zone "bl0"; zone "bl1"; zone "bl2"; zone "bl3"; zone "bl4";
+       zone "bl5"; zone "bl6"; zone "bl7"; zone "bl8"; zone "bl9";
+       zone "bl10"; zone "bl11"; zone "bl12"; zone "bl13"; zone "bl14";
+       zone "bl15"; zone "bl16"; zone "bl17"; zone "bl18"; zone "bl19";
+    } recursive-only no
+       max-policy-ttl 90
+       # min-ns-dots 0
+       break-dnssec yes;
 EOF
 
 cat <<EOF >ns5/example.db
 \$TTL  120
-@      SOA     .  hostmaster.ns.example. ( 1 3600 1200 604800 60 )
+@      SOA     .  hostmaster.ns.example.tld5. ( 1 3600 1200 604800 60 )
        NS      ns
+       NS      ns1
 ns     A       10.53.0.5
+ns1    A       10.53.0.5
 EOF
 
 cat <<EOF >ns5/bl.db
@@ -71,31 +76,26 @@ ns          A       10.53.0.5
 
 ; used only in failure for "recursive-only no" in #8 test5
 a3-5.tld2      CNAME   *.
-; for "break-dnssec" in #9 test5
+; for "break-dnssec" in #9 & #10 test5
 a3-5.tld2s     CNAME   *.
-; for "max-policy-ttl 90" in test5
+; for "max-policy-ttl 90" in #17 test5
 a3-17.tld2     500 A   17.17.17.17
 
-; dummy NSDNAME policies to trigger lookups
-ns-1.example.com.rpz-nsdname   CNAME   .
-ns-2.example.com.rpz-nsdname   CNAME   .
-ns-3.example.com.rpz-nsdname   CNAME   .
-ns-4.example.com.rpz-nsdname   CNAME   .
-ns-5.example.com.rpz-nsdname   CNAME   .
+; dummy NSDNAME policy to trigger lookups
+ns1.x.rpz-nsdname      CNAME   .
 EOF
 
 if test -n "$QPERF"; then
     # do not build the full zones if we will not use them to avoid the long
     # time otherwise required to shut down the server
     $PERL -e 'for ($val = 1; $val <= 65535; ++$val) {
-       printf("host-%d-%d\tA    192.168.%d.%d\n",
-               $val/256, $val%256, $val/256, $val%256);
+       printf("host-%05d\tA    192.168.%d.%d\n", $val, $val/256, $val%256);
        }' >>ns5/example.db
 
     echo >>ns5/bl.db
     echo "; rewrite some names" >>ns5/bl.db
     $PERL -e 'for ($val = 2; $val <= 65535; $val += 69) {
-       printf("host-%d.sub%d.example.com\tCNAME\t.\n", $val/256, $val%256);
+       printf("host-%05d.example.tld5\tCNAME\t.\n", $val);
        }' >>ns5/bl.db
 
     echo >>ns5/bl.db
@@ -103,13 +103,11 @@ if test -n "$QPERF"; then
     $PERL -e 'for ($val = 3; $val <= 65535; $val += 69) {
        printf("32.%d.%d.168.192.rpz-ip  \tCNAME\t.\n",
                $val%256, $val/256);
-       printf("32.%d.%d.168.192.rpz-nsip\tCNAME\t.\n",
-               ($val+1)%256, ($val+1)/256);
        }' >>ns5/bl.db
 fi
 
 # some psuedo-random queryperf requests
-$PERL -e 'for ($cnt = $val = 1; $cnt <= 2000; ++$cnt) {
-       printf("host-%d.sub%d.example.com A\n", $val%256, $val/256);
-               $val = ($val * 9 + 32771) % 65536;
+$PERL -e 'for ($cnt = $val = 1; $cnt <= 3000; ++$cnt) {
+       printf("host-%05d.example.tld5 A\n", $val);
+       $val = ($val * 9 + 32771) % 65536;
        }' >ns5/requests
index aa885c3ed8965166fc66c90f4f32f913d2d9cce0..68dd59fa4b9773d1979c244d72402ebfb1eb5fbe 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: test1,v 1.8 2012/01/07 00:19:59 each Exp $
 
 
 ; Use comment lines instead of blank lines to combine update requests into
@@ -25,13 +24,13 @@ server 10.53.0.3 5300
 ; QNAME tests
 
 ; NXDOMAIN
-;      2, 20, 25
+;      2, 25
 update add  a0-1.tld2.bl.      300 CNAME .
 ; NODATA
-;      3, 21
+;      3
 update add  a3-1.tld2.bl.      300 CNAME *.
 ; and no assert-botch
-;      5, 22
+;      4, 5
 update add  a3-2.tld2.bl.      300 DNAME example.com.
 ;
 ; NXDOMAIN for a4-2-cname.tld2 via its target a4-2.tld2.
@@ -78,6 +77,14 @@ update add  a4-5.tld2.bl.    300 A   127.0.0.16
 ;      19
 update add  a4-6.tld2.bl.      300 CNAME .
 update add  a4-6-cname.tld2.bl.        300 A   127.0.0.17
+; no change instead of NXDOMAIN because +norecurse
+;      20
+update add  a5-2.tld2.bl.      300 CNAME .
+; no change instead of NODATA because +norecurse
+;      21
+update add  a5-3.tld2.bl.      300 CNAME *.
+;      22, 23
+update add  a5-4.tld2.bl.      300 DNAME example.com.
 ;
 ; assert in rbtdb.c
 ;      24
index 2a467af930dda44a2a8fc891e514972bb0119840..68425b1a4225592d8ee6e7ab04b8a4d4349d66b3 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: test2,v 1.6 2011/11/18 19:32:13 each Exp $
 
 
 ; Use comment lines instead of blank lines to combine update requests into
@@ -45,7 +44,7 @@ update add  32.3.4.168.192.rpz-ip.bl  300 CNAME *.
 ;      9
 update add  128.1.zz.3.2.2001.rpz-ip.bl        300 CNAME .
 ;
-; apply the policy with the lexically smallest address of 192.168.5.1
+; apply the policy with the lexically smaller trigger address of 192.168.5.1
 ; to an RRset of more than one A RR
 ;      11
 update add  32.1.5.168.192.rpz-ip.bl   300 A   127.0.0.1
@@ -59,7 +58,7 @@ update add 32.3.5.168.192.rpz-ip.bl-2 300 A   127.0.0.2
 send
 
 ; prefer QNAME to IP for a5-4.tld2
-;      13
+;      13, 14
 update add 32.4.5.168.192.rpz-ip.bl    300 CNAME a12.tld2.
 update add a5-4.tld2.bl                        300 CNAME a14.tld4.
 ;
index fa8575f1b1be75aeae9a305bf4accff23c3d9f7d..fe7ec0ba68cb9d14254e026dfdc89f2da7be19d8 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: test3,v 1.5 2011/10/13 01:32:32 vjs Exp $
 
 
 ; Use comment lines instead of blank lines to combine update requests into
 
 server 10.53.0.3 5300
 
+;      3, 4, 5
 ; NXDOMAIN for *.sub1.tld2 by NSDNAME
 update add  *.sub1.tld2.rpz-nsdname.bl.        300 CNAME .
 ;
+;      6
 ; walled garden for *.sub2.tld2
 update add  *.sub2.tld2.rpz-nsdname.bl.        300 CNAME a12-cname.tld2.
 ;
+;      7, 8
 ; exempt a3-2.tld2 and anything in 192.168.0.0/24
 ;   also checks that IP policies are preferred over NSDNAME policies
 update add  a3-2.tld2.bl               300 CNAME a3-2.tld2.
 update add  24.0.0.168.192.rpz-ip.bl   300 CNAME 24.0.0.168.192.
 ;
+;      9
 ; prefer QNAME policy to NSDNAME policy
 update add  a4-1.tld2.bl.              300 A 12.12.12.12
-;
+;      10
 ; prefer policy for largest NS name
 update add  ns.sub3.tld2.rpz-nsdname.bl.       300 A   127.0.0.1
 update add  ns.subsub.sub3.tld2.rpz-nsdname.bl. 300 A  127.0.0.2
index 40bd83fa9dd2ee9ed73354877d7bbd420f1ffe2e..d383362dfca5cc6f76ba3adfc1a6585a32398ce5 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: test4,v 1.5 2011/10/13 01:32:33 vjs Exp $
 
 
 ; Use comment lines instead of blank lines to combine update requests into
diff --git a/bin/tests/system/rpz/test4a b/bin/tests/system/rpz/test4a
new file mode 100644 (file)
index 0000000..7360f62
--- /dev/null
@@ -0,0 +1,30 @@
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+
+
+; Use comment lines instead of blank lines to combine update requests into
+;      single requests
+; Separate update requests for distinct TLDs with blank lines or 'send'
+; End the file with a blank line or 'send'
+
+; walled-garden NSIP tests
+
+server 10.53.0.3 5300
+
+; rewrite all of tld2 based on its server IP address
+update add  32.2.0.53.10.rpz-nsip.bl.  300 A       41.41.41.41
+update add  32.2.0.53.10.rpz-nsip.bl.  300 AAAA    2041::41
+update add  32.2.0.53.10.rpz-nsip.bl.  300 TXT     "NSIP walled garden"
+send
index a6510d30bde75eb64a4ec13a8f970796d9217574..42dbac8ba422597f597ce062b393ddd0111c0da8 100644 (file)
@@ -1,4 +1,4 @@
-; Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 ;
 ; Permission to use, copy, modify, and/or distribute this software for any
 ; purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: test5,v 1.5.4.1 2012/02/24 17:22:37 vjs Exp $
 
 
 ; Use comment lines instead of blank lines to combine update requests into
index a669ed4c7b58aebd27b27698ab2f3035b137088d..90b1ea947667dfedbbe0dbf8ac94e8caa9844323 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 #
 # Permission to use, copy, modify, and/or distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -12,7 +12,6 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: tests.sh,v 1.12 2012/01/07 23:46:53 tbox Exp $
 
 # test response policy zones (RPZ)
 
@@ -20,13 +19,16 @@ SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
 ns=10.53.0
-ns1=$ns.1                          # root, defining the others
-ns2=$ns.2                          # server whose answers are rewritten
-ns3=$ns.3                          # resolve that does the rewriting
-ns4=$ns.4                          # another server that is rewritten
-ns5=$ns.5                          # check performance with this server
+ns1=$ns.1              # root, defining the others
+ns2=$ns.2              # authoritative server whose answers are rewritten
+ns3=$ns.3              # resolver that does the rewriting
+ns4=$ns.4              # another authoritative server that is rewritten
+ns5=$ns.5              # resolver to check performance and give un-rewritten
+                       #       responses for comparisons
 
 HAVE_CORE=
+SAVE_RESULTS=
+NS3_STATS=47
 
 USAGE="$0: [-x]"
 while getopts "x" c; do
@@ -43,11 +45,18 @@ fi
 # really quit on control-C
 trap 'exit 1' 1 2 15
 
+TS='%H:%M:%S '
+TS=
+comment () {
+    if test -n "$TS"; then
+       date "+I:${TS}$*"
+    fi
+}
 
 RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
 
 digcmd () {
-    digcmd_args="+noadd +nosearch +time=1 +tries=1 -p 5300 $*"
+    digcmd_args="+noadd +time=1 +tries=1 -p 5300 $*"
     expr "$digcmd_args" : '.*@' >/dev/null || \
        digcmd_args="$digcmd_args @$ns3"
     expr "$digcmd_args" : '.*+[no]*auth' >/dev/null || \
@@ -70,13 +79,14 @@ make_dignm () {
 
 setret () {
     ret=1
+    status=`expr $status + 1`
     echo "$*"
 }
 
 # (re)load the reponse policy zones with the rules in the file $TEST_FILE
 load_db () {
     if test -n "$TEST_FILE"; then
-       if $NSUPDATE -v $TEST_FILE; then : ; else
+       if ! $NSUPDATE -v $TEST_FILE; then
            echo "I:failed to update policy zone with $TEST_FILE"
            exit 1
        fi
@@ -122,10 +132,21 @@ ckalive () {
     return 1
 }
 
+# check that statistics for $1 in $2 = $3
+ckstats () {
+    $RNDCCMD $1 stats
+    CNT=`sed -n -e 's/[         ]*\([0-9]*\).response policy.*/\1/p'  \
+                   $2/named.stats`
+    CNT=`expr 0$CNT + 0`
+    if test "$CNT" -ne $3; then
+       setret "I:wrong $2 statistics of $CNT instead of $3"
+    fi
+}
+
 # $1=message  $2=optional test file name
 start_group () {
     ret=0
-    test -n "$1" && echo "I:checking $1"
+    test -n "$1" && date "+I:${TS}checking $1"
     TEST_FILE=$2
     if test -n "$TEST_FILE"; then
        GROUP_NM="-$TEST_FILE"
@@ -138,33 +159,25 @@ start_group () {
 
 end_group () {
     if test -n "$TEST_FILE"; then
+       # remove the previous set of test rules
        sed -e 's/[      ]add[   ]/ delete /' $TEST_FILE | $NSUPDATE
        TEST_FILE=
     fi
     ckalive $ns3 "I:failed; ns3 server crashed and restarted"
-    if test "$status" -eq 0; then
-       # look for complaints from rpz.c
-       EMSGS=`grep -l 'invalid rpz' */*.run`
-       if test -n "$EMSGS"; then
-           setret "I:'invalid rpz' complaints in $EMSGS starting with:"
-           grep 'invalid rpz' */*.run | sed -e '4,$d' -e 's/^/I:    /'
-       fi
-       # look for complaints from rpz.c and query.c
-       EMSGS=`grep -l 'rpz .*failed' */*.run`
-       if test -n "$EMSGS"; then
-           setret "I:'rpz failed' complaints in $EMSGS starting with:"
-           grep 'rpz .*failed' */*.run | sed -e '4,$d' -e 's/^/I:    /'
-       fi
-    fi
-    status=`expr $status + $ret`
     GROUP_NM=
 }
 
+clean_result () {
+    if test -z "$SAVE_RESULTS"; then
+       rm -f $*
+    fi
+}
+
 # $1=dig args $2=other dig output file
 ckresult () {
     #ckalive "$1" "I:server crashed by 'dig $1'" || return 1
     if $PERL $SYSTEMTESTTOP/digcomp.pl $DIGNM $2 >/dev/null; then
-       rm -f ${DIGNM}*
+       clean_result ${DIGNM}*
        return 0
     fi
     setret "I:'dig $1' wrong; diff $DIGNM $2"
@@ -208,7 +221,7 @@ addr () {
     digcmd $2 >$DIGNM
     #ckalive "$2" "I:server crashed by 'dig $2'" || return 1
     ADDR_ESC=`echo "$ADDR" | sed -e 's/\./\\\\./g'`
-    ADDR_TTL=`sed -n -e  "s/^[-.a-z0-9]\{1,\}  *\([0-9]*\)     IN      A\{1,4\}        ${ADDR_ESC}\$/\1/p" $DIGNM`
+    ADDR_TTL=`sed -n -e "s/^[-.a-z0-9]\{1,\}   *\([0-9]*\)     IN      AA*     ${ADDR_ESC}\$/\1/p" $DIGNM`
     if test -z "$ADDR_TTL"; then
        setret "I:'dig $2' wrong; no address $ADDR record in $DIGNM"
        return 1
@@ -217,7 +230,7 @@ addr () {
        setret "I:'dig $2' wrong; TTL=$ADDR_TTL instead of $3 in $DIGNM"
        return 1
     fi
-    rm -f ${DIGNM}*
+    clean_result ${DIGNM}*
 }
 
 # check that a response is not rewritten
@@ -225,8 +238,8 @@ addr () {
 nochange () {
     make_dignm
     digcmd $* >$DIGNM
-    digcmd $* @$ns2 >${DIGNM}_OK
-    ckresult "$*" ${DIGNM}_OK && rm -f ${DIGNM}_OK
+    digcmd $* @$ns5 >${DIGNM}_OK
+    ckresult "$*" ${DIGNM}_OK && clean_result ${DIGNM}_OK
 }
 
 # check against a 'here document'
@@ -248,8 +261,8 @@ start_group "QNAME rewrites" test1
 nochange .                             # 1 do not crash or rewrite root
 nxdomain a0-1.tld2                     # 2
 nodata a3-1.tld2                       # 3
-nodata a3-2.tld2                       # 4 no crash on DNAME
-nodata sub.a3-2.tld2
+nodata a3-2.tld2                       # 4 nodata at DNAME itself
+nochange sub.a3-2.tld2                 # 5 miss where DNAME might work
 nxdomain a4-2.tld2                     # 6 rewrite based on CNAME target
 nxdomain a4-2-cname.tld2               # 7
 nodata a4-3-cname.tld2                 # 8
@@ -264,18 +277,18 @@ addr 56.56.56.56  a3-6.tld2               # 16 wildcard CNAME
 addr 57.57.57.57  a3-7.sub1.tld2       # 17 wildcard CNAME
 addr 127.0.0.16          a4-5-cname3.tld2      # 18 CNAME chain
 addr 127.0.0.17          a4-6-cname3.tld2      # 19 stop short in CNAME chain
-nochange a0-1.tld2         +norecurse  # 20 check that RD=1 is required
-nochange a3-1.tld2         +norecurse  # 21
-nochange a3-2.tld2         +norecurse  # 22
-nochange sub.a3-2.tld2     +norecurse  # 23
+nochange a5-2.tld2         +norecurse  # 20 check that RD=1 is required
+nochange a5-3.tld2         +norecurse  # 21
+nochange a5-4.tld2         +norecurse  # 22
+nochange sub.a5-4.tld2     +norecurse  # 23
 nxdomain c1.crash2.tld3                        # 24 assert in rbtdb.c
 nxdomain a0-1.tld2         +dnssec     # 25 simple DO=1 without signatures
-nxdomain a0-1.tld2s                    # 26 simple DO=0 with signatures
+nxdomain a0-1.tld2s        +nodnssec   # 26 simple DO=0 with signatures
 nochange a0-1.tld2s        +dnssec     # 27 simple DO=1 with signatures
 nxdomain a0-1s-cname.tld2s  +dnssec    # 28 DNSSEC too early in CNAME chain
 nochange a0-1-scname.tld2   +dnssec    # 29 DNSSEC on target in CNAME chain
-nochange a0-1.tld2s srv +auth +dnssec  # 30 no write for +DNSSEC and no record
-nxdomain a0-1.tld2s srv                        # 31
+nochange a0-1.tld2s srv +auth +dnssec  # 30 no write for DNSSEC and no record
+nxdomain a0-1.tld2s srv            +nodnssec   # 31
 end_group
 
 start_group "IP rewrites" test2
@@ -291,8 +304,8 @@ nxdomain a3-1.tld2 -taaaa           # 9 IPv6 policy
 nochange a4-1-aaaa.tld2 -taaaa         # 10
 addr 127.0.0.1  a5-1-2.tld2            # 11 prefer smallest policy address
 addr 127.0.0.1  a5-3.tld2              # 12 prefer first conflicting IP zone
-addr 14.14.14.14 a5-4.tld2             # 13 prefer QNAME to IP
-nochange a5-4.tld2         +norecurse  # 14 check that RD=1 is required
+nochange a5-4.tld2         +norecurse  # 13 check that RD=1 is required for #14
+addr 14.14.14.14 a5-4.tld2             # 14 prefer QNAME to IP
 nochange a4-4.tld2                     # 15 PASSTHRU
 nxdomain c2.crash2.tld3                        # 16 assert in rbtdb.c
 end_group
@@ -313,8 +326,9 @@ nochange a5-1-2.tld2
 end_group
 
 if ./rpz nsdname; then
+    # these tests assume "min-ns-dots 0"
     start_group "NSDNAME rewrites" test3
-    nochange a3-1.tld2
+    nochange a3-1.tld2                 # 1
     nochange a3-1.tld2     +dnssec     # 2 this once caused problems
     nxdomain a3-1.sub1.tld2            # 3 NXDOMAIN *.sub1.tld2 by NSDNAME
     nxdomain a3-1.subsub.sub1.tld2
@@ -327,19 +341,31 @@ if ./rpz nsdname; then
     addr 127.0.0.2 a3-1.subsub.sub3.tld2
     nxdomain xxx.crash1.tld2           # 12 dns_db_detachnode() crash
     end_group
+    NS3_STATS=`expr $NS3_STATS + 7`
 else
-    echo "I:NSDNAME not checked; named not configured with --enable-rpz-nsdname"
+    echo "I:NSDNAME not checked; named configured with --disable-rpz-nsdname"
 fi
 
 if ./rpz nsip; then
+    # these tests assume "min-ns-dots 0"
     start_group "NSIP rewrites" test4
-    nxdomain a3-1.tld2                 # 1 NXDOMAIN for all of tld2 by NSIP
+    nxdomain a3-1.tld2                 # 1 NXDOMAIN for all of tld2
     nochange a3-2.tld2.                        # 2 exempt rewrite by name
     nochange a0-1.tld2.                        # 3 exempt rewrite by address block
     nochange a3-1.tld4                 # 4 different NS IP address
     end_group
+
+    start_group "walled garden NSIP rewrites" test4a
+    addr 41.41.41.41 a3-1.tld2         # 1 walled garden for all of tld2
+    addr 2041::41   'a3-1.tld2 AAAA'   # 2 walled garden for all of tld2
+    here a3-1.tld2 TXT <<'EOF'         # 3 text message for all of tld2
+    ;; status: NOERROR, x
+    a3-1.tld2.     x   IN      TXT   "NSIP walled garden"
+EOF
+    end_group
+    NS3_STATS=`expr $NS3_STATS + 4`
 else
-    echo "I:NSIP not checked; named not configured with --enable-rpz-nsip"
+    echo "I:NSIP not checked; named configured with --disable-rpz-nsip"
 fi
 
 # policies in ./test5 overridden by response-policy{} in ns3/named.conf
@@ -377,6 +403,11 @@ for Q in RRSIG SIG ANY 'ANY +dnssec'; do
     nocrash www.redirect -t$Q
     nocrash www.credirect -t$Q
 done
+
+# This is not a bug, because any data leaked by writing 24.4.3.2.10.rpz-ip
+# (or whatever) is available by publishing "foo A 10.2.3.4" and then
+# resolving foo.
+# nxdomain 32.3.2.1.127.rpz-ip
 end_group
 
 
@@ -384,55 +415,56 @@ end_group
 QPERF=`sh qperf.sh`
 if test -n "$QPERF"; then
     perf () {
-       echo "I:checking performance $1"
-       # don't measure the costs of -d99
-       $RNDCCMD $ns5 notrace >/dev/null
-       $QPERF -1 -l2 -d ns5/requests -s $ns5 -p 5300 >ns5/$2.perf
+       date "+I:${TS}checking performance $1"
+       # Dry run to prime everything
+       comment "before dry run $1"
+       $QPERF -c -1 -l30 -d ns5/requests -s $ns5 -p 5300 >/dev/null
+       comment "before real test $1"
+       PFILE="ns5/$2.perf"
+       $QPERF -c -1 -l30 -d ns5/requests -s $ns5 -p 5300 >$PFILE
+       comment "after test $1"
+       X=`sed -n -e 's/.*Returned *\([^ ]*:\) *\([0-9]*\) .*/\1\2/p' $PFILE \
+               | tr '\n' ' '`
+       if test "$X" != "$3"; then
+           setret "I:wrong results '$X' in $PFILE"
+       fi
        ckalive $ns5 "I:failed; server #5 crashed"
     }
     trim () {
        sed -n -e 's/.*Queries per second: *\([0-9]*\).*/\1/p' ns5/$1.perf
     }
 
-    # Dry run to prime disk cache
-    #  Otherwise a first test of either flavor is 25% low
-    perf 'to prime disk cache' rpz
-
-    # get queries/second with rpz
-    perf 'with rpz' rpz
+    # get qps with rpz
+    perf 'with rpz' rpz 'NOERROR:2900 NXDOMAIN:100 '
+    RPZ=`trim rpz`
 
-    # turn off rpz and measure queries/second again
-    # Don't wait for a clean stop.  Clean stops of this server need seconds
-    # until the sockets are closed.  5 or 10 seconds after that, the
-    # server really stops and deletes named.pid.
+    # turn off rpz and measure qps again
     echo "# rpz off" >ns5/rpz-switch
-    PID=`cat ns5/named.pid`
-    test -z "$PID" || kill -9 "$PID"
-    $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns5
-    perf 'without rpz' norpz
-
-    # Don't wait for a clean stop.  Clean stops of this server need seconds
-    # until the sockets are closed.  5 or 10 seconds after that, the
-    # server really stops and deletes named.pid.
-    echo "# rpz off" >ns5/rpz-switch
-    PID=`cat ns5/named.pid`
-    test -z "$PID" || kill -9 "$PID" && rm -f ns5/named.pid
-
+    RNDCCMD_OUT=`$RNDCCMD $ns5 reload`
+    perf 'without rpz' norpz 'NOERROR:3000 '
     NORPZ=`trim norpz`
-    RPZ=`trim rpz`
-    echo "I:$RPZ qps with RPZ versus $NORPZ qps without"
 
-    # fail if RPZ costs more than 100%
-    NORPZ2=`expr "$NORPZ" / 2`
-    if test "$RPZ" -le "$NORPZ2"; then
-       echo "I:rpz $RPZ qps too far below non-RPZ $NORPZ qps"
-       status=`expr $status + 1`
+    PERCENT=`expr \( "$RPZ" \* 100 + \( $NORPZ / 2 \) \) / $NORPZ`
+    echo "I:$RPZ qps with rpz is $PERCENT% of $NORPZ qps without rpz"
+
+    MIN_PERCENT=30
+    if test "$PERCENT" -lt $MIN_PERCENT; then
+       setret "I:$RPZ qps with rpz or $PERCENT% is below $MIN_PERCENT% of $NORPZ qps"
+    fi
+
+    if test "$PERCENT" -ge 100; then
+       setret "I:$RPZ qps with RPZ or $PERCENT% of $NORPZ qps without RPZ is too high"
     fi
+
+    ckstats $ns5 ns5 203
+
 else
     echo "I:performance not checked; queryperf not available"
 fi
 
 
+ckstats $ns3 ns3 $NS3_STATS
+
 # restart the main test RPZ server to see if that creates a core file
 if test -z "$HAVE_CORE"; then
     $PERL $SYSTEMTESTTOP/stop.pl . ns3
@@ -441,6 +473,12 @@ if test -z "$HAVE_CORE"; then
     test -z "$HAVE_CORE" || setret "I:found $HAVE_CORE; memory leak?"
 fi
 
+# look for complaints from lib/dns/rpz.c and bin/name/query.c
+EMSGS=`egrep -l 'invalid rpz|rpz.*failed' ns*/named.run`
+if test -n "$EMSGS"; then
+    setret "I:error messages in $EMSGS starting with:"
+    egrep 'invalid rpz|rpz.*failed' ns*/named.run | sed -e '10,$d' -e 's/^/I:  /'
+fi
 
 echo "I:exit status: $status"
 exit $status
index b116a2623837b54b2dd5da9d00fb8e5962b95d61..6e016dbdb09cf0a6b08231b946fbbc90991e793a 100755 (executable)
--- a/configure
+++ b/configure
@@ -2149,8 +2149,8 @@ Optional Features:
                          [default=autodetect]
   --enable-fixed-rrset    enable fixed rrset ordering
                          [default=no]
-  --enable-rpz-nsip      enable rpz-nsip rules [default=no]
-  --enable-rpz-nsdname   enable rpz-nsdname rules [default=no]
+  --disable-rpz-nsip     disable rpz-nsip rules [default=enabled]
+  --disable-rpz-nsdname          disable rpz-nsdname rules [default=enabled]
   --enable-filter-aaaa    enable filtering of AAAA records
                          [default=no]
 
@@ -11862,8 +11862,6 @@ yes)
        test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
        test "${with_atf+set}" = set || with_atf=yes
        test "${enable_filter_aaaa+set}" = set || enable_filter_aaaa=yes
-       test "${enable_rpz_nsip+set}" = set || enable_rpz_nsip=yes
-       test "${enable_rpz_nsdname+set}" = set || enable_rpz_nsdname=yes
        test "${with_dlz_filesystem+set}" = set || with_dlz_filesystem=yes
        case "$host" in
        *-darwin*)
@@ -18611,7 +18609,7 @@ esac
 if test "${enable_rpz_nsip+set}" = set; then :
   enableval=$enable_rpz_nsip; enable_nsip="$enableval"
 else
-  enable_nsip="no"
+  enable_nsip="yes"
 fi
 
 case "$enable_nsip" in
@@ -18633,7 +18631,7 @@ esac
 if test "${enable_rpz_nsdname+set}" = set; then :
   enableval=$enable_rpz_nsdname; enable_nsdname="$enableval"
 else
-  enable_nsdname="no"
+  enable_nsdname="yes"
 fi
 
 case "$enable_nsdname" in
index c3d9c6f9b3494d7bd8acf938185befc526e57843..d0507de9e920b590a4d3faa073b0a9bdfe325bc8 100644 (file)
@@ -70,8 +70,6 @@ yes)
        test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
        test "${with_atf+set}" = set || with_atf=yes
        test "${enable_filter_aaaa+set}" = set || enable_filter_aaaa=yes
-       test "${enable_rpz_nsip+set}" = set || enable_rpz_nsip=yes
-       test "${enable_rpz_nsdname+set}" = set || enable_rpz_nsdname=yes
        test "${with_dlz_filesystem+set}" = set || with_dlz_filesystem=yes
        case "$host" in
        *-darwin*)
@@ -2956,9 +2954,9 @@ esac
 # Enable response policy rewriting using NS IP addresses
 #
 AC_ARG_ENABLE(rpz-nsip,
-       [  --enable-rpz-nsip      enable rpz-nsip rules [[default=no]]],
+       [  --disable-rpz-nsip     disable rpz-nsip rules [[default=enabled]]],
                        enable_nsip="$enableval",
-                       enable_nsip="no")
+                       enable_nsip="yes")
 case "$enable_nsip" in
        yes)
                AC_DEFINE(ENABLE_RPZ_NSIP, 1,
@@ -2974,9 +2972,9 @@ esac
 # Enable response policy rewriting using NS name
 #
 AC_ARG_ENABLE(rpz-nsdname,
-       [  --enable-rpz-nsdname   enable rpz-nsdname rules [[default=no]]],
+       [  --disable-rpz-nsdname          disable rpz-nsdname rules [[default=enabled]]],
                        enable_nsdname="$enableval",
-                       enable_nsdname="no")
+                       enable_nsdname="yes")
 case "$enable_nsdname" in
        yes)
                AC_DEFINE(ENABLE_RPZ_NSDNAME, 1,
index 257cef91da436733327a37509f7cb57b5c35cdab..85cf919814dcc566251cff8f797c34dee116185e 100644 (file)
@@ -5408,7 +5408,8 @@ badresp:1,adberr:0,findfail:0,valfail:0]
        <optional> policy given | disabled | passthru | nxdomain | nodata | cname <replaceable>domain</replaceable> </optional>
        <optional> recursive-only <replaceable>yes_or_no</replaceable> </optional> <optional> max-policy-ttl <replaceable>number</replaceable> </optional> ;
     } <optional> recursive-only <replaceable>yes_or_no</replaceable> </optional> <optional> max-policy-ttl <replaceable>number</replaceable> </optional>
-       <optional> break-dnssec <replaceable>yes_or_no</replaceable> </optional> ; </optional>
+       <optional> break-dnssec <replaceable>yes_or_no</replaceable> </optional> <optional> min-ns-dots <replaceable>number</replaceable> </optional>
+       <optional> qname-wait-recurse <replaceable>yes_or_no</replaceable> </optional> ; </optional>
 };
 </programlisting>
 
@@ -9685,7 +9686,7 @@ deny-answer-aliases { "example.net"; };
            Response policy zones are named in the
            <command>response-policy</command> option for the view or among the
            global options if there is no response-policy option for the view.
-           RPZs are ordinary DNS zones containing RRsets
+           Response policy zones are ordinary DNS zones containing RRsets
            that can be queried normally if allowed.
            It is usually best to restrict those queries with something like
            <command>allow-query { localhost; };</command>.
@@ -9731,18 +9732,19 @@ deny-answer-aliases { "example.net"; };
            They are encoded as subdomains of
            <userinput>rpz-nsdomain</userinput> relativized
            to the RPZ origin name.
-         </para>
-
-         <para>
            NSIP triggers match IP addresses in A and
            AAAA RRsets for domains that can be checked against NSDNAME
            policy records.
            NSIP triggers are encoded like IP triggers except as subdomains of
            <userinput>rpz-nsip</userinput>.
+           NSDNAME and NSIP triggers are checked only for names with at
+           least <command>min-ns-dots</command> dots.
+           The default value of <command>min-ns-dots</command> is 1 to
+           exclude top level domains.
          </para>
 
          <para>
-           The query response is checked against all RPZs, so
+           The query response is checked against all response policy zones, so
            two or more policy records can be triggered by a response.
            Because DNS responses can be rewritten according to at most one
            policy record, a single record encoding an action (other than
@@ -9773,19 +9775,8 @@ deny-answer-aliases { "example.net"; };
            When the processing of a response is restarted to resolve
            DNAME or CNAME records and a policy record set has
            not been triggered,
-           all RPZs are again consulted for the DNAME or CNAME names
-           and addresses.
-         </para>
-
-         <para>
-           Authority verification issues and variations in authority data
-           can cause inconsistent results for NSIP and NSDNAME policy records.
-           Glue NS records often differ from authoritative NS records.
-           So they are available
-           only when <acronym>BIND</acronym> is built with the
-           <userinput>--enable-rpz-nsip</userinput> or
-           <userinput>--enable-rpz-nsdname</userinput> options
-           on the "configure" command line.
+           all response policy zones are again consulted for the
+           DNAME or CNAME names and addresses.
          </para>
 
          <para>
@@ -9823,11 +9814,12 @@ deny-answer-aliases { "example.net"; };
          </para>
 
          <para>
-           The actions specified in an RPZ can be overridden with a
+           The actions specified in a policy zone can be overridden with a
            <command>policy</command> clause in the
            <command>response-policy</command> option.
-           An organization using an RPZ provided by another organization might
-           use this mechanism to redirect domains to its own walled garden.
+           An organization using a policy zone provided by another
+           organization might use this mechanism to redirect domains
+           to its own walled garden.
            <itemizedlist>
              <listitem><command>GIVEN</command> says "do not override but
                perform the action specified in the zone."
@@ -9858,9 +9850,10 @@ deny-answer-aliases { "example.net"; };
          </para>
 
          <para>
-           By default, the actions encoded in an RPZ are applied
-           only to queries that ask for recursion (RD=1).
-           That default can be changed for a single RPZ or all RPZs in a view
+           By default, the actions encoded in a response policy zone
+           are applied only to queries that ask for recursion (RD=1).
+           That default can be changed for a single policy zone or
+           all response policy zones in a view
            with a <command>recursive-only no</command> clause.
            This feature is useful for serving the same zone files
            both inside and outside an RFC 1918 cloud and using RPZ to
@@ -9869,15 +9862,37 @@ deny-answer-aliases { "example.net"; };
          </para>
 
          <para>
-           Also by default, RPZ actions are applied only to DNS requests that
-           either do not request DNSSEC metadata (DO=0) or when no DNSSEC
-           records are available for request name in the original zone (not
-           the response policy zone).
-           This default can be changed for all RPZs in a view with a
-           <command>break-dnssec yes</command> clause.
-           In that case, RPZ actions are applied regardless of DNSSEC.
-           The name of the clause option reflects the fact that results
-           rewritten by RPZ actions cannot verify.
+            Also by default, RPZ actions are applied only to DNS requests
+            that either do not request DNSSEC metadata (DO=0) or when no
+            DNSSEC records are available for request name in the original
+            zone (not the response policy zone).  This default can be
+            changed for all response policy zones in a view with a
+            <command>break-dnssec yes</command> clause.  In that case, RPZ
+            actions are applied regardless of DNSSEC.  The name of the
+            clause option reflects the fact that results rewritten by RPZ
+            actions cannot verify.
+         </para>
+
+         <para>
+            No DNS records are needed to trigger a QNAME action.  The name
+            itself is sufficient, so in principle the query name need not
+            be recursively resolved.  However, not resolving the requested
+            name leaks the fact that response policy rewriting is in use
+            and that the name is listed in a policy zone to operators of
+            servers for listed names.  To prevent that information leak, by
+            default any recursion needed for a request is done before any
+            policy triggers are considered.  Because listed domains often
+            have slow authoritative servers, this default behavior can cost
+            significant time.  The <command>qname-wait-recurse no</command>
+            option overrides the default behavior when recursion cannot
+            change the response.  <command>qname-wait-recurse no</command>
+            does not affect QNAME triggers in policy zones listed after
+            other zones containing IP, NSIP and NSDNAME triggers, because
+            those may depend on the A, AAAA, and NS records that would be
+            found during recursive resolution.  It also does not affect
+            DNSSEC requests (DO=1) unless <command>break-dnssec yes</command>
+            is in use, because the response would depend on whether or not
+            RRSIG records were found during resolution.
          </para>
 
          <para>
@@ -9926,20 +9941,26 @@ bzone.domain.com        CNAME   garden.example.com.
 ns.domain.com.rpz-nsdname   CNAME   .
 48.zz.2.2001.rpz-nsip       CNAME   .
 </programlisting>
-         <para>
-            Note: RPZ may impact server performance.  Each configured
-            response policy zone requires the server to perform one to four
-            additional database lookups before a query can be answered.
+          <para>
+            RPZ can affect server performance.
+            Each configured response policy zone requires the server to
+            perform one to four additional database lookups before a
+            query can be answered.
             For example, a DNS server with four policy zones, each with all
-            four kinds of response triggers &mdash; QNAME, IP, NSIP, and
-            NSDNAME &mdash; requires a total of 17 times as many database
+            four kinds of response triggers, QNAME, IP, NSIP, and
+            NSDNAME, requires a total of 17 times as many database
             lookups as a similar DNS server with no response policy zones.
             A <acronym>BIND9</acronym> server with adequate memory and one
             response policy zone with QNAME and IP triggers might achieve a
-            maximum queries-per-second rate about 20% lower.  A server with
-            four response policy zones with QNAME and IP triggers might
-            have a maximum QPS rate about 50% lower.
-         </para>
+            maximum queries-per-second rate about 20% lower.
+            A server with four response policy zones with QNAME and IP
+            triggers might have a maximum QPS rate about 50% lower.
+          </para>
+
+          <para>
+            Responses rewritten by RPZ are counted in the
+            <command>RPZRewrites</command> statistics.
+          </para>
         </sect3>
 
        <sect3>
@@ -14855,6 +14876,19 @@ HOST-127.EXAMPLE. MX 0 .
                      </para>
                    </entry>
                  </row>
+                 <row rowsep="0">
+                   <entry colname="1">
+                     <para><command>RPZRewrites</command></para>
+                   </entry>
+                   <entry colname="2">
+                     <para><command></command></para>
+                   </entry>
+                   <entry colname="3">
+                     <para>
+                       Response policy zone rewrites.
+                     </para>
+                   </entry>
+                 </row>
                </tbody>
               </tgroup>
             </informaltable>
index 52d1b690a91cb3594901506f27ac10dbcc0c4546..e78a0b2cce2efc6334087f73cac82247c1931a27 100644 (file)
@@ -1033,20 +1033,23 @@ dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset,
                (db->methods->resigned)(db, rdataset, version);
 }
 
+/*
+ * Attach a database to policy zone databases.
+ * This should only happen when the caller has already ensured that
+ * it is dealing with a database that understands response policy zones.
+ */
 void
-dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st)
-{
-       if (db->methods->rpz_enabled != NULL)
-               (db->methods->rpz_enabled)(db, st);
+dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+       REQUIRE(db->methods->rpz_attach != NULL);
+       (db->methods->rpz_attach)(db, rpzs, rpz_num);
 }
 
-void
-dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
-                  dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
-                  dns_rdataset_t *ardataset, dns_rpz_st_t *st,
-                  dns_name_t *query_qname)
-{
-       if (db->methods->rpz_findips != NULL)
-               (db->methods->rpz_findips)(rpz, rpz_type, zone, db, version,
-                                          ardataset, st, query_qname);
+/*
+ * Finish loading a response policy zone.
+ */
+isc_result_t
+dns_db_rpz_ready(dns_db_t *db) {
+       if (db->methods->rpz_ready == NULL)
+               return (ISC_R_SUCCESS);
+       return ((db->methods->rpz_ready)(db));
 }
index 62ac78473eb409a303d0238406cc041d005305cd..9cc475aa06b7f0a9255ef3748e29ff9822c6fe17 100644 (file)
@@ -576,8 +576,8 @@ static dns_dbmethods_t ecdb_methods = {
        NULL,                   /* resigned */
        NULL,                   /* isdnssec */
        NULL,                   /* getrrsetstats */
-       NULL,                   /* rpz_enabled */
-       NULL,                   /* rpz_findips */
+       NULL,                   /* rpz_attach */
+       NULL,                   /* rpz_ready */
        NULL,                   /* findnodeext */
        NULL,                   /* findext */
        NULL,                   /* setcachestats */
index fd0c42bcd3ad07ff99eb0ba6a07c279369ada970..229083a18245d2639bb4306e32f827870f7103f7 100644 (file)
@@ -176,14 +176,9 @@ 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_enabled)(dns_db_t *db, dns_rpz_st_t *st);
-       void            (*rpz_findips)(dns_rpz_zone_t *rpz,
-                                      dns_rpz_type_t rpz_type,
-                                      dns_zone_t *zone, dns_db_t *db,
-                                      dns_dbversion_t *version,
-                                      dns_rdataset_t *ardataset,
-                                      dns_rpz_st_t *st,
-                                      dns_name_t *query_qname);
+       void            (*rpz_attach)(dns_db_t *db, dns_rpz_zones_t *rpzs,
+                                     dns_rpz_num_t rpz_num);
+       isc_result_t    (*rpz_ready)(dns_db_t *db);
        isc_result_t    (*findnodeext)(dns_db_t *db, dns_name_t *name,
                                     isc_boolean_t create,
                                     dns_clientinfomethods_t *methods,
@@ -1602,29 +1597,16 @@ dns_db_setcachestats(dns_db_t *db, isc_stats_t *stats);
  */
 
 void
-dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st);
+dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
 /*%<
- * See if a policy database has DNS_RPZ_TYPE_IP, DNS_RPZ_TYPE_NSIP, or
- * DNS_RPZ_TYPE_NSDNAME records.
+ * Attach the response policy information for a view to a database for a
+ * zone for the view.
  */
 
-void
-dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
-                  dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
-                  dns_rdataset_t *ardataset, dns_rpz_st_t *st,
-                  dns_name_t *query_qname);
+isc_result_t
+dns_db_rpz_ready(dns_db_t *db);
 /*%<
- * Search the CDIR block tree of a response policy tree of trees for the best
- * match to any of the IP addresses in an A or AAAA rdataset.
- *
- * Requires:
- * \li search in policy zone 'rpz' for a match of 'rpz_type' either
- *         DNS_RPZ_TYPE_IP or DNS_RPZ_TYPE_NSIP
- * \li 'zone' and 'db' are the database corresponding to 'rpz'
- * \li 'version' is the required version of the database
- * \li 'ardataset' is an A or AAAA rdataset of addresses to check
- * \li 'found' specifies the previous best match if any or
- *         or NULL, an empty name, 0, DNS_RPZ_POLICY_MISS, and 0
+ * Finish loading a response policy zone.
  */
 
 ISC_LANG_ENDDECLS
index 03db8d33127e259b9c24d4a37195613d5d23471a..73ab302d32766cd5967008aa9a2d49fa28fe43dd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rpz.h,v 1.5 2011/10/28 11:46:50 marka Exp $ */
 
 #ifndef DNS_RPZ_H
 #define DNS_RPZ_H 1
 #include <dns/fixedname.h>
 #include <dns/rdata.h>
 #include <dns/types.h>
+#include <isc/refcount.h>
 
 ISC_LANG_BEGINDECLS
 
-#define DNS_RPZ_IP_ZONE                "rpz-ip"
-#define DNS_RPZ_NSIP_ZONE      "rpz-nsip"
-#define DNS_RPZ_NSDNAME_ZONE   "rpz-nsdname"
-#define DNS_RPZ_PASSTHRU_ZONE  "rpz-passthru"
+#define DNS_RPZ_PREFIX         "rpz-"
+#define DNS_RPZ_IP_ZONE                DNS_RPZ_PREFIX"ip"
+#define DNS_RPZ_NSIP_ZONE      DNS_RPZ_PREFIX"nsip"
+#define DNS_RPZ_NSDNAME_ZONE   DNS_RPZ_PREFIX"nsdname"
+#define DNS_RPZ_PASSTHRU_ZONE  DNS_RPZ_PREFIX"passthru"
 
-typedef isc_uint8_t            dns_rpz_cidr_bits_t;
+typedef isc_uint8_t            dns_rpz_prefix_t;
 
 typedef enum {
        DNS_RPZ_TYPE_BAD,
@@ -49,7 +50,7 @@ typedef enum {
  */
 typedef enum {
        DNS_RPZ_POLICY_GIVEN = 0,       /* 'given': what policy record says */
-       DNS_RPZ_POLICY_DISABLED = 1,    /* 'cname x': answer with x's rrsets */
+       DNS_RPZ_POLICY_DISABLED = 1,    /* log what would have happened */
        DNS_RPZ_POLICY_PASSTHRU = 2,    /* 'passthru': do not rewrite */
        DNS_RPZ_POLICY_NXDOMAIN = 3,    /* 'nxdomain': answer with NXDOMAIN */
        DNS_RPZ_POLICY_NODATA = 4,      /* 'nodata': answer with ANCOUNT=0 */
@@ -60,27 +61,120 @@ typedef enum {
        DNS_RPZ_POLICY_ERROR
 } dns_rpz_policy_t;
 
+typedef isc_uint8_t        dns_rpz_num_t;
+
+#define DNS_RPZ_MAX_ZONES   32
+#if DNS_RPZ_MAX_ZONES > 32
+# if DNS_RPZ_MAX_ZONES > 64
+#  error "rpz zone bit masks must fit in a word"
+# endif
+typedef isc_uint64_t       dns_rpz_zbits_t;
+#else
+typedef isc_uint32_t       dns_rpz_zbits_t;
+#endif
+
+#define DNS_RPZ_ALL_ZBITS   ((dns_rpz_zbits_t)-1)
+
+#define DNS_RPZ_INVALID_NUM DNS_RPZ_MAX_ZONES
+
+#define DNS_RPZ_ZBIT(n)            (((dns_rpz_zbits_t)1) << (dns_rpz_num_t)(n))
+
 /*
- * Specify a response policy zone.
+ * Mask of the specified and higher numbered policy zones
+ * Avoid hassles with (1<<33) or (1<<65)
  */
-typedef struct dns_rpz_zone dns_rpz_zone_t;
+#define DNS_RPZ_ZMASK(n)    ((dns_rpz_zbits_t)((((n) >= DNS_RPZ_MAX_ZONES-1) ? \
+                                               0 : (1<<((n)+1))) -1))
 
+/*
+ * A single response policy zone.
+ */
+typedef struct dns_rpz_triggers dns_rpz_triggers_t;
+struct dns_rpz_triggers {
+       int             qname;
+       int             ipv4;
+       int             ipv6;
+       int             nsdname;
+       int             nsipv4;
+       int             nsipv6;
+};
+typedef struct dns_rpz_zone dns_rpz_zone_t;
 struct dns_rpz_zone {
-       ISC_LINK(dns_rpz_zone_t) link;
-       int                      num;     /* ordinal in list of policy zones */
-       dns_name_t               origin;  /* Policy zone name */
-       dns_name_t               nsdname; /* DNS_RPZ_NSDNAME_ZONE.origin */
-       dns_name_t               passthru;/* DNS_RPZ_PASSTHRU_ZONE. */
-       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_boolean_t            recursive_only;
+       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      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_ZONE. */
+       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 */
+       dns_rpz_triggers_t  triggers;
 };
 
 /*
- * Radix trees for response policy IP addresses.
+ * Radix tree node for response policy IP addresses
  */
-typedef struct dns_rpz_cidr    dns_rpz_cidr_t;
+typedef struct dns_rpz_cidr_node dns_rpz_cidr_node_t;
+
+/*
+ * Response policy zones known to a view.
+ */
+typedef struct dns_rpz_zones dns_rpz_zones_t;
+struct dns_rpz_zones {
+       struct {
+               dns_rpz_zbits_t     no_rd_ok;
+               isc_boolean_t       break_dnssec;
+               isc_boolean_t       qname_wait_recurse;
+               unsigned int        min_ns_labels;
+               dns_rpz_num_t       num_zones;
+       } p;
+       dns_rpz_zone_t          *zones[DNS_RPZ_MAX_ZONES];
+
+       dns_rpz_zbits_t         defined;
+
+       /*
+        * The set of records for a policy zone are in one of these states:
+        *      never loaded                load_begun=0  have=0
+        *      during initial loading      load_begun=1  have=0
+        *                              and rbtdb->rpzsp == rbtdb->load_rpzsp
+        *      after good load             load_begun=1  have!=0
+        *      after failed initial load   load_begun=1  have=0
+        *                              and rbtdb->load_rpzsp == NULL
+        *      reloading after failure     load_begun=1  have=0
+        *      reloading after success
+        *              main rpzs           load_begun=1  have!=0
+        *              load rpzs           load_begun=1  have=0
+        */
+       dns_rpz_zbits_t         load_begun;
+       struct {
+               dns_rpz_zbits_t     qname;
+               dns_rpz_zbits_t     ipv4;
+               dns_rpz_zbits_t     ipv6;
+               dns_rpz_zbits_t     ip;
+               dns_rpz_zbits_t     nsdname;
+               dns_rpz_zbits_t     nsipv4;
+               dns_rpz_zbits_t     nsipv6;
+               dns_rpz_zbits_t     nsip;
+               dns_rpz_zbits_t     qname_skip_recurse;
+       } have;
+
+       isc_mem_t               *mctx;
+       isc_refcount_t          refs;
+       /*
+        * One lock for short term read-only search that guarantees the
+        * consistency of the pointers.
+        * A second lock for maintenance that guarantees no other thread
+        * is adding or deleting nodes.
+        */
+       isc_mutex_t             search_lock;
+       isc_mutex_t             maint_lock;
+
+       dns_rpz_cidr_node_t     *cidr;
+       dns_rbt_t               *rbt;
+};
+
 
 /*
  * context for finding the best policy
@@ -91,19 +185,15 @@ typedef struct {
 # define DNS_RPZ_DONE_QNAME    0x0002  /* qname checked */
 # define DNS_RPZ_DONE_QNAME_IP 0x0004  /* IP addresses of qname checked */
 # define DNS_RPZ_DONE_NSDNAME  0x0008  /* NS name missed; checking addresses */
-# define DNS_RPZ_DONE_IPv4     0x0010
+# define DNS_RPZ_DONE_IPv4     0x0010
 # define DNS_RPZ_RECURSING     0x0020
-# define DNS_RPZ_HAVE_IP       0x0040  /* a policy zone has IP addresses */
-# define DNS_RPZ_HAVE_NSIPv4   0x0080  /*                IPv4 NISP addresses */
-# define DNS_RPZ_HAVE_NSIPv6   0x0100  /*                IPv6 NISP addresses */
-# define DNS_RPZ_HAVE_NSDNAME  0x0200  /*                NS names */
        /*
         * Best match so far.
         */
        struct {
                dns_rpz_type_t          type;
                dns_rpz_zone_t          *rpz;
-               dns_rpz_cidr_bits_t     prefix;
+               dns_rpz_prefix_t        prefix;
                dns_rpz_policy_t        policy;
                dns_ttl_t               ttl;
                isc_result_t            result;
@@ -138,10 +228,15 @@ typedef struct {
                dns_rdataset_t          *sigrdataset;
                dns_rdatatype_t         qtype;
        } q;
-       dns_name_t              *qname;
+       /*
+        * p_name: current policy owner name
+        * r_name: recursing for this name to possible policy triggers
+        * f_name: saved found name from before recursion
+        */
+       dns_name_t              *p_name;
        dns_name_t              *r_name;
        dns_name_t              *fname;
-       dns_fixedname_t         _qnamef;
+       dns_fixedname_t         _p_namef;
        dns_fixedname_t         _r_namef;
        dns_fixedname_t         _fnamef;
 } dns_rpz_st_t;
@@ -168,38 +263,41 @@ dns_rpz_str2policy(const char *str);
 const char *
 dns_rpz_policy2str(dns_rpz_policy_t policy);
 
-void
-dns_rpz_set_need(isc_boolean_t need);
+dns_rpz_policy_t
+dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
+                    dns_name_t *selfname);
 
-isc_boolean_t
-dns_rpz_needed(void);
+isc_result_t
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx);
 
 void
-dns_rpz_cidr_free(dns_rpz_cidr_t **cidr);
+dns_rpz_attach_rpzs(dns_rpz_zones_t *source, dns_rpz_zones_t **target);
 
 void
-dns_rpz_view_destroy(dns_view_t *view);
+dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp);
 
 isc_result_t
-dns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin,
-                dns_rpz_cidr_t **rbtdb_cidr);
-void
-dns_rpz_enabled(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st);
+dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
+                 dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
 
-void
-dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name);
+isc_result_t
+dns_rpz_ready(dns_rpz_zones_t *rpzs,
+             dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num);
+
+isc_result_t
+dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *name);
 
 void
-dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name);
+dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *name);
 
-isc_result_t
-dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr,
-                 dns_rpz_type_t type, dns_name_t *canon_name,
-                 dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix);
+dns_rpz_num_t
+dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+               dns_rpz_zbits_t zbits, const isc_netaddr_t *netaddr,
+               dns_name_t *ip_name, dns_rpz_prefix_t *prefixp);
 
-dns_rpz_policy_t
-dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
-                    dns_name_t *selfname);
+dns_rpz_zbits_t
+dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+                 dns_rpz_zbits_t zbits, dns_name_t *trig_name);
 
 ISC_LANG_ENDDECLS
 
index e2194d69ce4c559355c23807388a3f58e96b07fa..aa6888c727db9e1f1fff9c8c625870b426bc7275 100644 (file)
@@ -166,9 +166,7 @@ struct dns_view {
        dns_acl_t *                     aaaa_acl;
        dns_dns64list_t                 dns64;
        unsigned int                    dns64cnt;
-       ISC_LIST(dns_rpz_zone_t)        rpz_zones;
-       isc_boolean_t                   rpz_recursive_only;
-       isc_boolean_t                   rpz_break_dnssec;
+       dns_rpz_zones_t                 *rpzs;
        dns_dlzdblist_t                 dlz_searched;
        dns_dlzdblist_t                 dlz_unsearched;
 
index 59861dfab2a29a56d4d9b38b6db16ee906bf18eb..4abbc2fd538ff2943bfb19f2c111c686e1e3ac6f 100644 (file)
@@ -35,6 +35,7 @@
 #include <dns/master.h>
 #include <dns/masterdump.h>
 #include <dns/rdatastruct.h>
+#include <dns/rpz.h>
 #include <dns/types.h>
 #include <dns/zt.h>
 
@@ -2098,6 +2099,16 @@ dns_zone_getincludes(dns_zone_t *zone, char ***includesp);
  * The array and its contents need to be freed using isc_mem_free.
  */
 
+isc_result_t
+dns_zone_rpz_enable(dns_zone_t *zone, dns_rpz_zones_t *rpzs,
+                   dns_rpz_num_t rpz_num);
+/*%
+ * Set the response policy associated with a zone.
+ */
+
+dns_rpz_num_t
+dns_zone_get_rpz_num(dns_zone_t *zone);
+
 ISC_LANG_ENDDECLS
 
 
index fb0c13e9a9d1c45057bdd1f65eae53030201dad9..350eb74c35104cc42fb81c6e29ab093fe31cfe87 100644 (file)
@@ -508,7 +508,9 @@ struct dns_rbtdb {
        dns_rbt_t *                     tree;
        dns_rbt_t *                     nsec;
        dns_rbt_t *                     nsec3;
-       dns_rpz_cidr_t *                rpz_cidr;
+       dns_rpz_zones_t                 *rpzs;
+       dns_rpz_num_t                   rpz_num;
+       dns_rpz_zones_t                 *load_rpzs;
 
        /* Unlocked */
        unsigned int                    quantum;
@@ -1053,8 +1055,18 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
                isc_stats_detach(&rbtdb->cachestats);
 
 #ifdef BIND9
-       if (rbtdb->rpz_cidr != NULL)
-               dns_rpz_cidr_free(&rbtdb->rpz_cidr);
+       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);
+       }
 #endif
 
        isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks,
@@ -1646,11 +1658,11 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node)
        switch (node->nsec) {
        case DNS_RBT_NSEC_NORMAL:
 #ifdef BIND9
-               if (rbtdb->rpz_cidr != NULL) {
+               if (rbtdb->rpzs != NULL) {
                        dns_fixedname_init(&fname);
                        name = dns_fixedname_name(&fname);
                        dns_rbt_fullnamefromnode(node, name);
-                       dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name);
+                       dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
                }
 #endif
                result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
@@ -1681,14 +1693,15 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node)
                                              DNS_LOGCATEGORY_DATABASE,
                                              DNS_LOGMODULE_CACHE,
                                              ISC_LOG_WARNING,
-                                             "delete_nsecnode(): "
+                                             "delete_node(): "
                                              "dns_rbt_deletenode(nsecnode): %s",
                                              isc_result_totext(result));
                        }
                }
                result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
 #ifdef BIND9
-               dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name);
+               if (rbtdb->rpzs != NULL)
+                       dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
 #endif
                break;
        case DNS_RBT_NSEC_NSEC:
@@ -1703,7 +1716,7 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node)
                              DNS_LOGCATEGORY_DATABASE,
                              DNS_LOGMODULE_CACHE,
                              ISC_LOG_WARNING,
-                             "delete_nsecnode(): "
+                             "delete_node(): "
                              "dns_rbt_deletenode: %s",
                              isc_result_totext(result));
        }
@@ -2668,14 +2681,15 @@ findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, dns_name_t *name,
                result = dns_rbt_addnode(tree, name, &node);
                if (result == ISC_R_SUCCESS) {
 #ifdef BIND9
-                       if (tree == rbtdb->tree && rbtdb->rpz_cidr != NULL) {
+                       if (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);
-                               dns_rpz_cidr_addip(rbtdb->rpz_cidr, fname);
+                               result = dns_rpz_add(rbtdb->rpzs,
+                                                    rbtdb->rpz_num, fname);
                        }
 #endif
                        dns_rbt_namefromnode(node, &nodename);
@@ -4674,218 +4688,45 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
        return (result);
 }
 
+#ifdef BIND9
 /*
- * Mark a database for response policy rewriting.
+ * Connect this RBTDB to the response policy zone summary data for the view.
  */
-#ifdef BIND9
 static void
-get_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st)
-{
-       dns_rbtdb_t *rbtdb;
+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_read);
-       dns_rpz_enabled(rbtdb->rpz_cidr, st);
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+       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);
 }
 
 /*
- * Search the CDIR block tree of a response policy tree of trees for all of
- * the IP addresses in an A or AAAA rdataset.
- * Among the policies for all IPv4 and IPv6 addresses for a name, choose
- *     the earliest configured policy,
- *     QNAME over IP over NSDNAME over NSIP,
- *     the longest prefix,
- *     the lexically smallest address.
- * The caller must have already checked that any existing policy was not
- * configured earlier than this policy zone and does not have a higher
- * precedence type.
+ * Enable this RBTDB as a response policy zone.
  */
-static void
-rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
-           dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
-           dns_rdataset_t *ardataset, dns_rpz_st_t *st,
-           dns_name_t *query_qname)
-{
-       dns_rbtdb_t *rbtdb;
-       struct in_addr ina;
-       struct in6_addr in6a;
-       isc_netaddr_t netaddr;
-       dns_fixedname_t selfnamef, qnamef;
-       dns_name_t *selfname, *qname;
-       dns_rbtnode_t *node;
-       dns_rdataset_t zrdataset;
-       dns_rpz_cidr_bits_t prefix;
+static isc_result_t
+rpz_ready(dns_db_t *db) {
+       dns_rbtdb_t * rbtdb;
        isc_result_t result;
-       dns_rpz_policy_t rpz_policy;
-       dns_ttl_t ttl;
 
        rbtdb = (dns_rbtdb_t *)db;
        REQUIRE(VALID_RBTDB(rbtdb));
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-
-       if (rbtdb->rpz_cidr == NULL) {
-               RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-               return;
-       }
-
-       dns_fixedname_init(&selfnamef);
-       dns_fixedname_init(&qnamef);
-       selfname = dns_fixedname_name(&selfnamef);
-       qname = dns_fixedname_name(&qnamef);
-
-       for (result = dns_rdataset_first(ardataset);
-            result == ISC_R_SUCCESS;
-            result = dns_rdataset_next(ardataset)) {
-               dns_rdata_t rdata = DNS_RDATA_INIT;
-               dns_rdataset_current(ardataset, &rdata);
-               switch (rdata.type) {
-               case dns_rdatatype_a:
-                       INSIST(rdata.length == 4);
-                       memcpy(&ina.s_addr, rdata.data, 4);
-                       isc_netaddr_fromin(&netaddr, &ina);
-                       break;
-               case dns_rdatatype_aaaa:
-                       INSIST(rdata.length == 16);
-                       memcpy(in6a.s6_addr, rdata.data, 16);
-                       isc_netaddr_fromin6(&netaddr, &in6a);
-                       break;
-               default:
-                       continue;
-               }
-
-               result = dns_rpz_cidr_find(rbtdb->rpz_cidr, &netaddr, rpz_type,
-                                          selfname, qname, &prefix);
-               if (result != ISC_R_SUCCESS)
-                       continue;
-
-               /*
-                * If we already have a rule, discard this new rule if
-                * is not better.
-                * The caller has checked that st->m.rpz->num > rpz->num
-                * or st->m.rpz->num == rpz->num and st->m.type >= rpz_type
-                */
-               if (st->m.policy != DNS_RPZ_POLICY_MISS &&
-                   st->m.rpz->num == rpz->num &&
-                   (st->m.type < rpz_type ||
-                    (st->m.type == rpz_type &&
-                     (st->m.prefix > prefix ||
-                      (st->m.prefix == prefix &&
-                       0 > dns_name_rdatacompare(st->qname, qname))))))
-                       continue;
-
-               /*
-                * We have rpz_st an entry with a prefix at least as long as
-                * the prefix of the entry we had before.  Find the node
-                * corresponding to CDIR tree entry.
-                */
-               node = NULL;
-               result = dns_rbt_findnode(rbtdb->tree, qname, NULL,
-                                         &node, NULL, 0, NULL, NULL);
-               if (result != ISC_R_SUCCESS) {
-                       char namebuf[DNS_NAME_FORMATSIZE];
-
-                       dns_name_format(qname, namebuf, sizeof(namebuf));
-                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
-                                     DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
-                                     "rpz_findips findnode(%s) failed: %s",
-                                     namebuf, isc_result_totext(result));
-                       continue;
-               }
-               /*
-                * First look for a simple rewrite of the IP address.
-                * If that fails, look for a CNAME.  If we cannot find
-                * a CNAME or the CNAME is neither of the special forms
-                * "*" or ".", treat it like a real CNAME.
-                */
-               dns_rdataset_init(&zrdataset);
-               result = dns_db_findrdataset(db, node, version, ardataset->type,
-                                            0, 0, &zrdataset, NULL);
-               if (result != ISC_R_SUCCESS)
-                       result = dns_db_findrdataset(db, node, version,
-                                                    dns_rdatatype_cname,
-                                                    0, 0, &zrdataset, NULL);
-               if (result == ISC_R_SUCCESS) {
-                       if (zrdataset.type != dns_rdatatype_cname) {
-                               rpz_policy = DNS_RPZ_POLICY_RECORD;
-                       } else {
-                               rpz_policy = dns_rpz_decode_cname(rpz,
-                                                                 &zrdataset,
-                                                                 selfname);
-                               if (rpz_policy == DNS_RPZ_POLICY_RECORD ||
-                                   rpz_policy == DNS_RPZ_POLICY_WILDCNAME)
-                                       result = DNS_R_CNAME;
-                       }
-                       ttl = zrdataset.ttl;
-               } else {
-                       rpz_policy = DNS_RPZ_POLICY_RECORD;
-                       result = DNS_R_NXRRSET;
-                       ttl = DNS_RPZ_TTL_DEFAULT;
-               }
-
-               /*
-                * Use an overriding action specified in the configuration file
-                */
-               if (rpz->policy != DNS_RPZ_POLICY_GIVEN) {
-                       /*
-                        * only log DNS_RPZ_POLICY_DISABLED hits
-                        */
-                       if (rpz->policy == DNS_RPZ_POLICY_DISABLED) {
-                               if (isc_log_wouldlog(dns_lctx,
-                                                    DNS_RPZ_INFO_LEVEL)) {
-                                       char qname_buf[DNS_NAME_FORMATSIZE];
-                                       char rpz_qname_buf[DNS_NAME_FORMATSIZE];
-                                       dns_name_format(query_qname, qname_buf,
-                                                       sizeof(qname_buf));
-                                       dns_name_format(qname, rpz_qname_buf,
-                                                       sizeof(rpz_qname_buf));
-
-                                       isc_log_write(dns_lctx,
-                                               DNS_LOGCATEGORY_RPZ,
-                                               DNS_LOGMODULE_RBTDB,
-                                               DNS_RPZ_INFO_LEVEL,
-                                               "disabled rpz %s %s rewrite"
-                                               " %s via %s",
-                                               dns_rpz_type2str(rpz_type),
-                                               dns_rpz_policy2str(rpz_policy),
-                                               qname_buf, rpz_qname_buf);
-                               }
-                               continue;
-                       }
 
-                       rpz_policy = rpz->policy;
-               }
-
-               if (dns_rdataset_isassociated(st->m.rdataset))
-                       dns_rdataset_disassociate(st->m.rdataset);
-               if (st->m.node != NULL)
-                       dns_db_detachnode(st->m.db, &st->m.node);
-               if (st->m.db != NULL)
-                       dns_db_detach(&st->m.db);
-               if (st->m.zone != NULL)
-                       dns_zone_detach(&st->m.zone);
-               st->m.rpz = rpz;
-               st->m.type = rpz_type;
-               st->m.prefix = prefix;
-               st->m.policy = rpz_policy;
-               st->m.ttl = ISC_MIN(ttl, rpz->max_policy_ttl);
-               st->m.result = result;
-               dns_name_copy(qname, st->qname, NULL);
-               if ((rpz_policy == DNS_RPZ_POLICY_RECORD ||
-                   rpz_policy == DNS_RPZ_POLICY_WILDCNAME) &&
-                   result != DNS_R_NXRRSET) {
-                       dns_rdataset_clone(&zrdataset,st->m.rdataset);
-                       dns_db_attachnode(db, node, &st->m.node);
-               }
-               dns_db_attach(db, &st->m.db);
-               st->m.version = version;
-               dns_zone_attach(zone, &st->m.zone);
-               if (dns_rdataset_isassociated(&zrdataset))
-                       dns_rdataset_disassociate(&zrdataset);
+       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_read);
+       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+       return (result);
 }
 #endif
 
@@ -6988,8 +6829,9 @@ loadnode(dns_rbtdb_t *rbtdb, dns_name_t *name, dns_rbtnode_t **nodep,
        noderesult = dns_rbt_addnode(rbtdb->tree, name, nodep);
 
 #ifdef BIND9
-       if (noderesult == ISC_R_SUCCESS)
-               dns_rpz_cidr_addip(rbtdb->rpz_cidr, name);
+       if (rbtdb->rpzs != NULL && noderesult == ISC_R_SUCCESS)
+               noderesult = dns_rpz_add(rbtdb->load_rpzs, rbtdb->rpz_num,
+                                        name);
 #endif
 
        if (!hasnsec)
@@ -7278,6 +7120,20 @@ beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
 
        RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
 
+#ifdef BIND9
+       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);
+               }
+       }
+#endif
+
        REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING))
                == 0);
        rbtdb->attributes |= RBTDB_ATTR_LOADING;
@@ -7879,8 +7735,8 @@ static dns_dbmethods_t zone_methods = {
        isdnssec,
        NULL,
 #ifdef BIND9
-       get_rpz_enabled,
-       rpz_findips,
+       rpz_attach,
+       rpz_ready,
 #else
        NULL,
        NULL,
@@ -8123,24 +7979,6 @@ dns_rbtdb_create
                return (result);
        }
 
-#ifdef BIND9
-       /*
-        * Get ready for response policy IP address searching if at least one
-        * zone has been configured as a response policy zone and this
-        * is not a cache zone.
-        * It would be better to know that this database is for a policy
-        * zone named for a view, but that would require knowledge from
-        * above such as an argv[] set from data in the zone.
-        */
-       if (type == dns_dbtype_zone && !dns_name_equal(origin, dns_rootname)) {
-               result = dns_rpz_new_cidr(mctx, origin, &rbtdb->rpz_cidr);
-               if (result != ISC_R_SUCCESS) {
-                       free_rbtdb(rbtdb, ISC_FALSE, NULL);
-                       return (result);
-               }
-       }
-#endif
-
        /*
         * In order to set the node callback bit correctly in zone databases,
         * we need to know if the node has the origin name of the zone.
@@ -8218,6 +8056,9 @@ 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 65c94baf83517d05f6148b7821adb3386e7d35bf..dd28987c8fb21e79e2ebd6b81ad1451238d343fc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
 
 /*! \file */
 
@@ -36,6 +35,7 @@
 #include <dns/rdataset.h>
 #include <dns/rdatastruct.h>
 #include <dns/result.h>
+#include <dns/rbt.h>
 #include <dns/rpz.h>
 #include <dns/view.h>
 
 /*
  * Parallel radix trees for databases of response policy IP addresses
  *
- * The radix or Patricia trees are somewhat specialized to handle response
- * policy addresses by representing the two test of IP IP addresses and name
- * server IP addresses in a single tree.
+ * The radix or patricia trees are somewhat specialized to handle response
+ * policy addresses by representing the two sets of IP addresses and name
+ * server IP addresses in a single tree.  One set of IP addresses is
+ * for rpz-ip policies or policies triggered by addresses in A or
+ * AAAA records in responses.
+ * The second set is for rpz-nsip policies or policies triggered by addresses
+ * in A or AAAA records for NS records that are authorities for responses.
  *
  * Each leaf indicates that an IP address is listed in the IP address or the
  * name server IP address policy sub-zone (or both) of the corresponding
@@ -54,7 +58,8 @@
  * tree, the node in the policy zone's database is found by converting
  * the IP address to a domain name in a canonical form.
  *
- * The response policy zone canonical form of IPv6 addresses is one of:
+ *
+ * The response policy zone canonical form of an IPv6 address is one of:
  *     prefix.W.W.W.W.W.W.W.W
  *     prefix.WORDS.zz
  *     prefix.WORDS.zz.WORDS
@@ -71,7 +76,7 @@
  *     prefix  is the prefix length of the address between 1 and 32
  *     B       is a number between 0 and 255
  *
- * IPv4 addresses are distinguished from IPv6 addresses by having
+ * Names for IPv4 addresses are distinguished from IPv6 addresses by having
  * 5 labels all of which are numbers, and a prefix between 1 and 32.
  */
 
@@ -89,41 +94,72 @@ typedef struct {
 } dns_rpz_cidr_key_t;
 
 #define ADDR_V4MAPPED          0xffff
+#define KEY_IS_IPV4(prefix,ip) ((prefix) >= 96 && (ip)->w[0] == 0 &&   \
+                               (ip)->w[1] == 0 && (ip)->w[2] == ADDR_V4MAPPED)
+
+#define DNS_RPZ_WORD_MASK(b) ((b) == 0 ? (dns_rpz_cidr_word_t)(-1)     \
+                             : ((dns_rpz_cidr_word_t)(-1)              \
+                                << (DNS_RPZ_CIDR_WORD_BITS - (b))))
 
-#define DNS_RPZ_WORD_MASK(b)                           \
-       ((b) == 0 ? (dns_rpz_cidr_word_t)(-1)           \
-                 : ((dns_rpz_cidr_word_t)(-1)          \
-                   << (DNS_RPZ_CIDR_WORD_BITS - (b))))
+/*
+ * Get bit #n from the array of words of an IP address.
+ */
+#define DNS_RPZ_IP_BIT(ip, n) (1 & ((ip)->w[(n)/DNS_RPZ_CIDR_WORD_BITS] >>  \
+                                   (DNS_RPZ_CIDR_WORD_BITS                 \
+                                    - 1 - ((n) % DNS_RPZ_CIDR_WORD_BITS))))
 
-#define DNS_RPZ_IP_BIT(ip, bitno) \
-       (1 & ((ip)->w[(bitno)/DNS_RPZ_CIDR_WORD_BITS] >> \
-           (DNS_RPZ_CIDR_WORD_BITS - 1 - ((bitno) % DNS_RPZ_CIDR_WORD_BITS))))
+/*
+ * A pair of arrays of bits flagging the existence of
+ * IP or QNAME (d) and NSIP or NSDNAME (ns) policy triggers.
+ */
+typedef struct dns_rpz_pair_zbits dns_rpz_pair_zbits_t;
+struct dns_rpz_pair_zbits {
+       dns_rpz_zbits_t     d;
+       dns_rpz_zbits_t     ns;
+};
 
-typedef struct dns_rpz_cidr_node       dns_rpz_cidr_node_t;
-typedef isc_uint8_t                    dns_rpz_cidr_flags_t;
+/*
+ * A CIDR or radix tree node.
+ */
 struct dns_rpz_cidr_node {
-       dns_rpz_cidr_node_t             *parent;
-       dns_rpz_cidr_node_t             *child[2];
-       dns_rpz_cidr_key_t              ip;
-       dns_rpz_cidr_bits_t             bits;
-       dns_rpz_cidr_flags_t            flags;
-#define        DNS_RPZ_CIDR_FG_IP       0x01   /* has IP data or is parent of IP */
-#define        DNS_RPZ_CIDR_FG_IP_DATA  0x02   /* has IP data */
-#define        DNS_RPZ_CIDR_FG_NSIPv4   0x04   /* has or is parent of NSIPv4 data */
-#define        DNS_RPZ_CIDR_FG_NSIPv6   0x08   /* has or is parent of NSIPv6 data */
-#define        DNS_RPZ_CIDR_FG_NSIP_DATA 0x10  /* has NSIP data */
+       dns_rpz_cidr_node_t     *parent;
+       dns_rpz_cidr_node_t     *child[2];
+       dns_rpz_cidr_key_t      ip;
+       dns_rpz_prefix_t        prefix;
+       dns_rpz_pair_zbits_t    pair;
+       dns_rpz_pair_zbits_t    sum;
 };
 
-struct dns_rpz_cidr {
-       isc_mem_t               *mctx;
-       isc_boolean_t           have_nsdname;   /* zone has NSDNAME record */
-       dns_rpz_cidr_node_t     *root;
-       dns_name_t              ip_name;        /* RPZ_IP_ZONE.origin. */
-       dns_name_t              nsip_name;      /* RPZ_NSIP_ZONE.origin. */
-       dns_name_t              nsdname_name;   /* RPZ_NSDNAME_ZONE.origin */
+/*
+ * The data in a RBT node has two pairs of bits for policy zones.
+ * One pair is for the corresponding name of the node such as example.com
+ * and the other pair is for a wildcard child such as *.example.com.
+ */
+typedef struct dns_rpz_nm_data dns_rpz_nm_data_t;
+struct dns_rpz_nm_data {
+       dns_rpz_pair_zbits_t    pair;
+       dns_rpz_pair_zbits_t    wild;
 };
 
-static isc_boolean_t           have_rpz_zones = ISC_FALSE;
+#if 0
+/*
+ * Catch a name while debugging.
+ */
+static void
+catch_name(const dns_name_t *src_name, const char *tgt, const char *str) {
+       dns_fixedname_t tgt_namef;
+       dns_name_t *tgt_name;
+
+       dns_fixedname_init(&tgt_namef);
+       tgt_name = dns_fixedname_name(&tgt_namef);
+       dns_name_fromstring(tgt_name, tgt, DNS_NAME_DOWNCASE, NULL);
+       if (dns_name_equal(src_name, tgt_name)) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "rpz hit failed: %s %s", str, tgt);
+       }
+}
+#endif
 
 const char *
 dns_rpz_type2str(dns_rpz_type_t type) {
@@ -139,8 +175,7 @@ dns_rpz_type2str(dns_rpz_type_t type) {
        case DNS_RPZ_TYPE_BAD:
                break;
        }
-       FATAL_ERROR(__FILE__, __LINE__,
-                   "impossible rpz type %d", type);
+       FATAL_ERROR(__FILE__, __LINE__, "impossible rpz type %d", type);
        return ("impossible");
 }
 
@@ -197,261 +232,235 @@ dns_rpz_policy2str(dns_rpz_policy_t policy) {
        return (str);
 }
 
-/*
- * Free the radix tree of a response policy database.
- */
-void
-dns_rpz_cidr_free(dns_rpz_cidr_t **cidrp) {
-       dns_rpz_cidr_node_t *cur, *child, *parent;
-       dns_rpz_cidr_t *cidr;
-
-       REQUIRE(cidrp != NULL);
-
-       cidr = *cidrp;
-       if (cidr == NULL)
-               return;
-
-       cur = cidr->root;
-       while (cur != NULL) {
-               /* Depth first. */
-               child = cur->child[0];
-               if (child != NULL) {
-                       cur = child;
-                       continue;
-               }
-               child = cur->child[1];
-               if (child != NULL) {
-                       cur = child;
-                       continue;
-               }
-
-               /* Delete this leaf and go up. */
-               parent = cur->parent;
-               if (parent == NULL)
-                       cidr->root = NULL;
-               else
-                       parent->child[parent->child[1] == cur] = NULL;
-               isc_mem_put(cidr->mctx, cur, sizeof(*cur));
-               cur = parent;
+static int
+zbit_to_num(dns_rpz_zbits_t zbit) {
+       dns_rpz_num_t rpz_num;
+
+       INSIST(zbit != 0);
+       rpz_num = 0;
+#if DNS_RPZ_MAX_ZONES > 32
+       if ((zbit & 0xffffffff00000000L) != 0) {
+               zbit >>= 32;
+               rpz_num += 32;
        }
-
-       dns_name_free(&cidr->ip_name, cidr->mctx);
-       dns_name_free(&cidr->nsip_name, cidr->mctx);
-       dns_name_free(&cidr->nsdname_name, cidr->mctx);
-       isc_mem_put(cidr->mctx, cidr, sizeof(*cidr));
-       *cidrp = NULL;
-}
-
-/*
- * Forget a view's list of policy zones.
- */
-void
-dns_rpz_view_destroy(dns_view_t *view) {
-       dns_rpz_zone_t *zone;
-
-       REQUIRE(view != NULL);
-
-       while (!ISC_LIST_EMPTY(view->rpz_zones)) {
-               zone = ISC_LIST_HEAD(view->rpz_zones);
-               ISC_LIST_UNLINK(view->rpz_zones, zone, link);
-               if (dns_name_dynamic(&zone->origin))
-                       dns_name_free(&zone->origin, view->mctx);
-               if (dns_name_dynamic(&zone->passthru))
-                       dns_name_free(&zone->passthru, view->mctx);
-               if (dns_name_dynamic(&zone->nsdname))
-                       dns_name_free(&zone->nsdname, view->mctx);
-               if (dns_name_dynamic(&zone->cname))
-                       dns_name_free(&zone->cname, view->mctx);
-               isc_mem_put(view->mctx, zone, sizeof(*zone));
+#endif
+       if ((zbit & 0xffff0000) != 0) {
+               zbit >>= 16;
+               rpz_num += 16;
        }
+       if ((zbit & 0xff00) != 0) {
+               zbit >>= 8;
+               rpz_num += 8;
+       }
+       if ((zbit & 0xf0) != 0) {
+               zbit >>= 4;
+               rpz_num += 4;
+       }
+       if ((zbit & 0xc) != 0) {
+               zbit >>= 2;
+               rpz_num += 2;
+       }
+       if ((zbit & 2) != 0)
+               ++rpz_num;
+       return (rpz_num);
 }
 
-/*
- * Note that we have at least one response policy zone.
- * It would be better for something to tell the rbtdb code that the
- * zone is in at least one view's list of policy zones.
- */
-void
-dns_rpz_set_need(isc_boolean_t need) {
-       have_rpz_zones = need;
-}
-
-isc_boolean_t
-dns_rpz_needed(void) {
-       return (have_rpz_zones);
+static inline void
+make_pair(dns_rpz_pair_zbits_t *pair, dns_rpz_zbits_t zbits,
+         dns_rpz_type_t type)
+{
+       switch (type) {
+       case DNS_RPZ_TYPE_QNAME:
+       case DNS_RPZ_TYPE_IP:
+               pair->d = zbits;
+               pair->ns = 0;
+               break;
+       case DNS_RPZ_TYPE_NSDNAME:
+       case DNS_RPZ_TYPE_NSIP:
+               pair->d = 0;
+               pair->ns = zbits;
+               break;
+       default:
+               INSIST(0);
+               break;
+       }
 }
 
 /*
- * Start a new radix tree for a response policy zone.
+ * Mark a node and all of its parents as having IP or NSIP data
  */
-isc_result_t
-dns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin,
-                dns_rpz_cidr_t **rbtdb_cidr)
-{
-       isc_result_t result;
-       dns_rpz_cidr_t *cidr;
-
-       REQUIRE(rbtdb_cidr != NULL && *rbtdb_cidr == NULL);
-
-       /*
-        * Only if there is at least one response policy zone.
-        */
-       if (!have_rpz_zones)
-               return (ISC_R_SUCCESS);
-
-       cidr = isc_mem_get(mctx, sizeof(*cidr));
-       if (cidr == NULL)
-               return (ISC_R_NOMEMORY);
-       memset(cidr, 0, sizeof(*cidr));
-       cidr->mctx = mctx;
+static void
+set_sum_pair(dns_rpz_cidr_node_t *cnode) {
+       dns_rpz_cidr_node_t *child;
+       dns_rpz_pair_zbits_t sum;
 
-       dns_name_init(&cidr->ip_name, NULL);
-       result = dns_name_fromstring2(&cidr->ip_name, DNS_RPZ_IP_ZONE, origin,
-                                     DNS_NAME_DOWNCASE, mctx);
-       if (result != ISC_R_SUCCESS) {
-               isc_mem_put(mctx, cidr, sizeof(*cidr));
-               return (result);
-       }
+       do {
+               sum = cnode->pair;
 
-       dns_name_init(&cidr->nsip_name, NULL);
-       result = dns_name_fromstring2(&cidr->nsip_name, DNS_RPZ_NSIP_ZONE,
-                                     origin, DNS_NAME_DOWNCASE, mctx);
-       if (result != ISC_R_SUCCESS) {
-               dns_name_free(&cidr->ip_name, mctx);
-               isc_mem_put(mctx, cidr, sizeof(*cidr));
-               return (result);
-       }
+               child = cnode->child[0];
+               if (child != NULL) {
+                       sum.d |= child->sum.d;
+                       sum.ns |= child->sum.ns;
+               }
 
-       dns_name_init(&cidr->nsdname_name, NULL);
-       result = dns_name_fromstring2(&cidr->nsdname_name, DNS_RPZ_NSDNAME_ZONE,
-                                     origin, DNS_NAME_DOWNCASE, mctx);
-       if (result != ISC_R_SUCCESS) {
-               dns_name_free(&cidr->nsip_name, mctx);
-               dns_name_free(&cidr->ip_name, mctx);
-               isc_mem_put(mctx, cidr, sizeof(*cidr));
-               return (result);
-       }
+               child = cnode->child[1];
+               if (child != NULL) {
+                       sum.d |= child->sum.d;
+                       sum.ns |= child->sum.ns;
+               }
 
-       *rbtdb_cidr = cidr;
-       return (ISC_R_SUCCESS);
+               if (cnode->sum.d == sum.d &&
+                   cnode->sum.ns == sum.ns)
+                       break;
+               cnode->sum = sum;
+               cnode = cnode->parent;
+       } while (cnode != NULL);
 }
 
-/*
- * See if a policy zone has IP, NSIP, or NSDNAME rules or records.
- */
-void
-dns_rpz_enabled(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st) {
-       if (cidr == NULL)
-               return;
-       if (cidr->root != NULL &&
-           (cidr->root->flags & DNS_RPZ_CIDR_FG_IP) != 0)
-               st->state |= DNS_RPZ_HAVE_IP;
-       if (cidr->root != NULL &&
-           (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv4) != 0)
-               st->state |= DNS_RPZ_HAVE_NSIPv4;
-       if (cidr->root != NULL &&
-           (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv6) != 0)
-               st->state |= DNS_RPZ_HAVE_NSIPv6;
-       if (cidr->have_nsdname)
-               st->state |= DNS_RPZ_HAVE_NSDNAME;
-}
+static void
+fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
+       dns_rpz_zbits_t zbits;
 
-static inline dns_rpz_cidr_flags_t
-get_flags(const dns_rpz_cidr_key_t *ip, dns_rpz_cidr_bits_t prefix,
-       dns_rpz_type_t rpz_type)
-{
-       if (rpz_type == DNS_RPZ_TYPE_NSIP) {
-               if (prefix >= 96 &&
-                   ip->w[0] == 0 && ip->w[1] == 0 &&
-                   ip->w[2] == ADDR_V4MAPPED)
-                       return (DNS_RPZ_CIDR_FG_NSIP_DATA |
-                               DNS_RPZ_CIDR_FG_NSIPv4);
-               else
-                       return (DNS_RPZ_CIDR_FG_NSIP_DATA |
-                               DNS_RPZ_CIDR_FG_NSIPv6);
+       /*
+        * Get a mask covering all policy zones that are not subordinate to
+        * other policy zones containing triggers that require that the
+        * qname be resolved before they can be checked.
+        */
+       if (rpzs->p.qname_wait_recurse) {
+               zbits = 0;
        } else {
-               return (DNS_RPZ_CIDR_FG_IP | DNS_RPZ_CIDR_FG_IP_DATA);
+               zbits = (rpzs->have.ipv4 || rpzs->have.ipv6 ||
+                        rpzs->have.nsdname ||
+                        rpzs->have.nsipv4 || rpzs->have.nsipv6);
+               if (zbits == 0) {
+                       zbits = DNS_RPZ_ALL_ZBITS;
+               } else {
+                       zbits = DNS_RPZ_ZMASK(zbit_to_num(zbits));
+               }
        }
+       rpzs->have.qname_skip_recurse = zbits;
+
+       rpzs->have.ip = rpzs->have.ipv4 | rpzs->have.ipv6;
+       rpzs->have.nsip = rpzs->have.nsipv4 | rpzs->have.nsipv6;
 }
 
-/*
- * Mark a node as having IP or NSIP data and all of its parents
- * as members of the IP or NSIP tree.
- */
 static void
-set_node_flags(dns_rpz_cidr_node_t *node, dns_rpz_type_t rpz_type) {
-       dns_rpz_cidr_flags_t flags;
+adj_trigger_cnt(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+               dns_rpz_type_t rpz_type,
+               const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+               isc_boolean_t inc)
+{
+       dns_rpz_zone_t *rpz;
+       int *cnt;
+       dns_rpz_zbits_t *have;
 
-       flags = get_flags(&node->ip, node->bits, rpz_type);
-       node->flags |= flags;
-       flags &= ~(DNS_RPZ_CIDR_FG_NSIP_DATA | DNS_RPZ_CIDR_FG_IP_DATA);
-       for (;;) {
-               node = node->parent;
-               if (node == NULL)
-                       return;
-               node->flags |= flags;
+       rpz = rpzs->zones[rpz_num];
+       switch (rpz_type) {
+       case DNS_RPZ_TYPE_QNAME:
+               cnt = &rpz->triggers.qname;
+               have = &rpzs->have.qname;
+               break;
+       case DNS_RPZ_TYPE_IP:
+               REQUIRE(tgt_ip != NULL);
+               if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+                       cnt = &rpz->triggers.ipv4;
+                       have = &rpzs->have.ipv4;
+               } else {
+                       cnt = &rpz->triggers.ipv6;
+                       have = &rpzs->have.ipv6;
+               }
+               break;
+       case DNS_RPZ_TYPE_NSDNAME:
+               cnt = &rpz->triggers.nsdname;
+               have = &rpzs->have.nsdname;
+               break;
+       case DNS_RPZ_TYPE_NSIP:
+               REQUIRE(tgt_ip != NULL);
+               if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+                       cnt = &rpz->triggers.nsipv4;
+                       have = &rpzs->have.nsipv4;
+               } else {
+                       cnt = &rpz->triggers.nsipv6;
+                       have = &rpzs->have.nsipv6;
+               }
+               break;
+       default:
+               INSIST(0);
+       }
+
+       if (inc) {
+               if (++*cnt == 1) {
+                       *have |= DNS_RPZ_ZBIT(rpz_num);
+                       fix_qname_skip_recurse(rpzs);
+               }
+       } else {
+               REQUIRE(*cnt > 0);
+               if (--*cnt == 0) {
+                       *have &= ~DNS_RPZ_ZBIT(rpz_num);
+                       fix_qname_skip_recurse(rpzs);
+               }
        }
 }
 
-/*
- * Make a radix tree node.
- */
 static dns_rpz_cidr_node_t *
-new_node(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *ip,
-        dns_rpz_cidr_bits_t bits, dns_rpz_cidr_flags_t flags)
+new_node(dns_rpz_zones_t *rpzs,
+        const dns_rpz_cidr_key_t *ip, dns_rpz_prefix_t prefix,
+        const dns_rpz_cidr_node_t *child)
 {
-       dns_rpz_cidr_node_t *node;
+       dns_rpz_cidr_node_t *new;
        int i, words, wlen;
 
-       node = isc_mem_get(cidr->mctx, sizeof(*node));
-       if (node == NULL)
+       new = isc_mem_get(rpzs->mctx, sizeof(*new));
+       if (new == NULL)
                return (NULL);
-       memset(node, 0, sizeof(*node));
+       memset(new, 0, sizeof(*new));
 
-       node->flags = flags & ~(DNS_RPZ_CIDR_FG_IP_DATA |
-                               DNS_RPZ_CIDR_FG_NSIP_DATA);
+       if (child != NULL)
+               new->sum = child->sum;
 
-       node->bits = bits;
-       words = bits / DNS_RPZ_CIDR_WORD_BITS;
-       wlen = bits % DNS_RPZ_CIDR_WORD_BITS;
+       new->prefix = prefix;
+       words = prefix / DNS_RPZ_CIDR_WORD_BITS;
+       wlen = prefix % DNS_RPZ_CIDR_WORD_BITS;
        i = 0;
        while (i < words) {
-               node->ip.w[i] = ip->w[i];
+               new->ip.w[i] = ip->w[i];
                ++i;
        }
        if (wlen != 0) {
-               node->ip.w[i] = ip->w[i] & DNS_RPZ_WORD_MASK(wlen);
+               new->ip.w[i] = ip->w[i] & DNS_RPZ_WORD_MASK(wlen);
                ++i;
        }
        while (i < DNS_RPZ_CIDR_WORDS)
-               node->ip.w[i++] = 0;
+               new->ip.w[i++] = 0;
 
-       return (node);
+       return (new);
 }
 
 static void
 badname(int level, dns_name_t *name, const char *str1, const char *str2) {
-       char printname[DNS_NAME_FORMATSIZE];
+       char namebuf[DNS_NAME_FORMATSIZE];
 
+       /*
+        * bin/tests/system/rpz/tests.sh looks for "invalid rpz".
+        */
        if (level < DNS_RPZ_DEBUG_QUIET
            && isc_log_wouldlog(dns_lctx, level)) {
-               dns_name_format(name, printname, sizeof(printname));
+               dns_name_format(name, namebuf, sizeof(namebuf));
                isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
                              DNS_LOGMODULE_RBTDB, level,
                              "invalid rpz IP address \"%s\"%s%s",
-                             printname, str1, str2);
+                             namebuf, str1, str2);
        }
 }
 
 /*
  * Convert an IP address from radix tree binary (host byte order) to
- * to its canonical response policy domain name and its name in the
+ * to its canonical response policy domain name without the origin of the
  * policy zone.
  */
 static isc_result_t
-ip2name(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
-       dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type,
-       dns_name_t *canon_name, dns_name_t *search_name)
+ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+       dns_name_t *base_name, dns_name_t *ip_name)
 {
 #ifndef INET6_ADDRSTRLEN
 #define INET6_ADDRSTRLEN 46
@@ -459,22 +468,18 @@ ip2name(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
        int w[DNS_RPZ_CIDR_WORDS*2];
        char str[1+8+1+INET6_ADDRSTRLEN+1];
        isc_buffer_t buffer;
-       dns_name_t *name;
        isc_result_t result;
        isc_boolean_t zeros;
        int i, n, len;
 
-       if (tgt_prefix > 96 &&
-           tgt_ip->w[0] == 0 &&
-           tgt_ip->w[1] == 0 &&
-           tgt_ip->w[2] == ADDR_V4MAPPED) {
+       if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
                len = snprintf(str, sizeof(str), "%d.%d.%d.%d.%d",
                               tgt_prefix - 96,
                               tgt_ip->w[3] & 0xff,
                               (tgt_ip->w[3]>>8) & 0xff,
                               (tgt_ip->w[3]>>16) & 0xff,
                               (tgt_ip->w[3]>>24) & 0xff);
-               if (len == -1 || len > (int)sizeof(str))
+               if (len < 0 || len > (int)sizeof(str))
                        return (ISC_R_FAILURE);
        } else {
                for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) {
@@ -514,35 +519,19 @@ ip2name(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
                }
        }
 
-       if (canon_name != NULL) {
-               isc__buffer_init(&buffer, str, sizeof(str));
-               isc__buffer_add(&buffer, len);
-               result = dns_name_fromtext(canon_name, &buffer,
-                                          dns_rootname, 0, NULL);
-               if (result != ISC_R_SUCCESS)
-                       return (result);
-       }
-       if (search_name != NULL) {
-               isc__buffer_init(&buffer, str, sizeof(str));
-               isc__buffer_add(&buffer, len);
-               if (type == DNS_RPZ_TYPE_NSIP)
-                       name = &cidr->nsip_name;
-               else
-                       name = &cidr->ip_name;
-               result = dns_name_fromtext(search_name, &buffer, name, 0, NULL);
-               if (result != ISC_R_SUCCESS)
-                       return (result);
-       }
-       return (ISC_R_SUCCESS);
+       isc__buffer_init(&buffer, str, sizeof(str));
+       isc__buffer_add(&buffer, len);
+       result = dns_name_fromtext(ip_name, &buffer, base_name, 0, NULL);
+       return (result);
 }
 
 /*
- * Decide which kind of IP address response policy zone a name is in.
+ * Determine the type a of a name in a response policy zone.
  */
 static dns_rpz_type_t
-set_type(dns_rpz_cidr_t *cidr, dns_name_t *name) {
+type_from_name(dns_rpz_zone_t *rpz, dns_name_t *name) {
 
-       if (dns_name_issubdomain(name, &cidr->ip_name))
+       if (dns_name_issubdomain(name, &rpz->ip))
                return (DNS_RPZ_TYPE_IP);
 
        /*
@@ -550,12 +539,12 @@ set_type(dns_rpz_cidr_t *cidr, dns_name_t *name) {
         * until consistency problems are resolved.
         */
 #ifdef ENABLE_RPZ_NSIP
-       if (dns_name_issubdomain(name, &cidr->nsip_name))
+       if (dns_name_issubdomain(name, &rpz->nsip))
                return (DNS_RPZ_TYPE_NSIP);
 #endif
 
 #ifdef ENABLE_RPZ_NSDNAME
-       if (dns_name_issubdomain(name, &cidr->nsdname_name))
+       if (dns_name_issubdomain(name, &rpz->nsdname))
                return (DNS_RPZ_TYPE_NSDNAME);
 #endif
 
@@ -564,73 +553,80 @@ set_type(dns_rpz_cidr_t *cidr, dns_name_t *name) {
 
 /*
  * Convert an IP address from canonical response policy domain name form
- * to radix tree binary (host byte order).
+ * to radix tree binary (host byte order) for adding or deleting IP or NSIP
+ * data.
  */
 static isc_result_t
-name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name,
-          dns_rpz_type_t type, dns_rpz_cidr_key_t *tgt_ip,
-          dns_rpz_cidr_bits_t *tgt_prefix)
+name2ipkey(int log_level,
+          const dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+          dns_rpz_type_t rpz_type, dns_name_t *src_name,
+          dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t *tgt_prefix,
+          dns_rpz_pair_zbits_t *new_pair)
 {
-       isc_result_t result;
-       dns_fixedname_t fname;
-       dns_name_t *ipname;
-       char ipstr[DNS_NAME_FORMATSIZE];
+       dns_rpz_zone_t *rpz;
+       char ip_str[DNS_NAME_FORMATSIZE];
+       dns_offsets_t ip_name_offsets;
+       dns_fixedname_t ip_name2f;
+       dns_name_t ip_name, *ip_name2;
        const char *prefix_str, *cp, *end;
        char *cp2;
        int ip_labels;
-       dns_rpz_cidr_bits_t bits;
-       unsigned long prefix, l;
+       dns_rpz_prefix_t prefix;
+       unsigned long prefix_num, l;
+       isc_result_t result;
        int i;
 
-       /*
-        * Need at least enough labels for the shortest name,
-        * :: or 128.*.RPZ_x_ZONE.rpz.LOCALHOST.
-        */
+       REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+       rpz = rpzs->zones[rpz_num];
+       REQUIRE(rpz != NULL);
+
+       make_pair(new_pair, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+
        ip_labels = dns_name_countlabels(src_name);
-       ip_labels -= dns_name_countlabels(&cidr->ip_name);
-       ip_labels--;
-       if (ip_labels < 1) {
-               badname(level, src_name, "; too short", "");
+       if (rpz_type == DNS_RPZ_TYPE_QNAME)
+               ip_labels -= dns_name_countlabels(&rpz->origin);
+       else
+               ip_labels -= dns_name_countlabels(&rpz->nsdname);
+       if (ip_labels < 2) {
+               badname(log_level, src_name, "; too short", "");
                return (ISC_R_FAILURE);
        }
+       dns_name_init(&ip_name, ip_name_offsets);
+       dns_name_getlabelsequence(src_name, 0, ip_labels, &ip_name);
 
        /*
         * Get text for the IP address
         */
-       dns_fixedname_init(&fname);
-       ipname = dns_fixedname_name(&fname);
-       dns_name_split(src_name, dns_name_countlabels(&cidr->ip_name),
-                      ipname, NULL);
-       dns_name_format(ipname, ipstr, sizeof(ipstr));
-       end = &ipstr[strlen(ipstr)+1];
-       prefix_str = ipstr;
-
-       prefix = strtoul(prefix_str, &cp2, 10);
+       dns_name_format(&ip_name, ip_str, sizeof(ip_str));
+       end = &ip_str[strlen(ip_str)+1];
+       prefix_str = ip_str;
+
+       prefix_num = strtoul(prefix_str, &cp2, 10);
        if (*cp2 != '.') {
-               badname(level, src_name,
+               badname(log_level, src_name,
                        "; invalid leading prefix length", "");
                return (ISC_R_FAILURE);
        }
        *cp2 = '\0';
-       if (prefix < 1U || prefix > 128U) {
-               badname(level, src_name,
+       if (prefix_num < 1U || prefix_num > 128U) {
+               badname(log_level, src_name,
                        "; invalid prefix length of ", prefix_str);
                return (ISC_R_FAILURE);
        }
        cp = cp2+1;
 
-       if (ip_labels == 4 && !strchr(cp, 'z')) {
+       if (--ip_labels == 4 && !strchr(cp, 'z')) {
                /*
                 * Convert an IPv4 address
                 * from the form "prefix.w.z.y.x"
                 */
-               if (prefix > 32U) {
-                       badname(level, src_name,
+               if (prefix_num > 32U) {
+                       badname(log_level, src_name,
                                "; invalid IPv4 prefix length of ", prefix_str);
                        return (ISC_R_FAILURE);
                }
-               prefix += 96;
-               *tgt_prefix = (dns_rpz_cidr_bits_t)prefix;
+               prefix_num += 96;
+               *tgt_prefix = (dns_rpz_prefix_t)prefix_num;
                tgt_ip->w[0] = 0;
                tgt_ip->w[1] = 0;
                tgt_ip->w[2] = ADDR_V4MAPPED;
@@ -640,7 +636,7 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name,
                        if (l > 255U || (*cp2 != '.' && *cp2 != '\0')) {
                                if (*cp2 == '.')
                                        *cp2 = '\0';
-                               badname(level, src_name,
+                               badname(log_level, src_name,
                                        "; invalid IPv4 octet ", cp);
                                return (ISC_R_FAILURE);
                        }
@@ -651,7 +647,7 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name,
                /*
                 * Convert a text IPv6 address.
                 */
-               *tgt_prefix = (dns_rpz_cidr_bits_t)prefix;
+               *tgt_prefix = (dns_rpz_prefix_t)prefix_num;
                for (i = 0;
                     ip_labels > 0 && i < DNS_RPZ_CIDR_WORDS * 2;
                     ip_labels--) {
@@ -670,7 +666,7 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name,
                                    (*cp2 != '.' && *cp2 != '\0')) {
                                        if (*cp2 == '.')
                                            *cp2 = '\0';
-                                       badname(level, src_name,
+                                       badname(log_level, src_name,
                                                "; invalid IPv6 word ", cp);
                                        return (ISC_R_FAILURE);
                                }
@@ -684,36 +680,37 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name,
                }
        }
        if (cp != end) {
-               badname(level, src_name, "", "");
+               badname(log_level, src_name, "", "");
                return (ISC_R_FAILURE);
        }
 
        /*
         * Check for 1s after the prefix length.
         */
-       bits = (dns_rpz_cidr_bits_t)prefix;
-       while (bits < DNS_RPZ_CIDR_KEY_BITS) {
+       prefix = (dns_rpz_prefix_t)prefix_num;
+       while (prefix < DNS_RPZ_CIDR_KEY_BITS) {
                dns_rpz_cidr_word_t aword;
 
-               i = bits % DNS_RPZ_CIDR_WORD_BITS;
-               aword = tgt_ip->w[bits / DNS_RPZ_CIDR_WORD_BITS];
+               i = prefix % DNS_RPZ_CIDR_WORD_BITS;
+               aword = tgt_ip->w[prefix / DNS_RPZ_CIDR_WORD_BITS];
                if ((aword & ~DNS_RPZ_WORD_MASK(i)) != 0) {
-                       badname(level, src_name,
+                       badname(log_level, src_name,
                                "; too small prefix length of ", prefix_str);
                        return (ISC_R_FAILURE);
                }
-               bits -= i;
-               bits += DNS_RPZ_CIDR_WORD_BITS;
+               prefix -= i;
+               prefix += DNS_RPZ_CIDR_WORD_BITS;
        }
 
        /*
-        * Convert the address back to a canonical policy domain name
-        * to ensure that it is in canonical form.
+        * Convert the address back to a canonical domain name
+        * to ensure that the original name is in canonical form.
         */
-       result = ip2name(cidr, tgt_ip, (dns_rpz_cidr_bits_t) prefix,
-                        type, NULL, ipname);
-       if (result != ISC_R_SUCCESS || !dns_name_equal(src_name, ipname)) {
-               badname(level, src_name, "; not canonical", "");
+       dns_fixedname_init(&ip_name2f);
+       ip_name2 = dns_fixedname_name(&ip_name2f);
+       result = ip2name(tgt_ip, (dns_rpz_prefix_t)prefix_num, NULL, ip_name2);
+       if (result != ISC_R_SUCCESS || !dns_name_equal(&ip_name, ip_name2)) {
+               badname(log_level, src_name, "; not canonical", "");
                return (ISC_R_FAILURE);
        }
 
@@ -721,10 +718,55 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name,
 }
 
 /*
- * Find first differing bit.
+ * Get trigger name and data bits for adding or deleting summary NSDNAME
+ * or QNAME data.
  */
-static int
-ffbit(dns_rpz_cidr_word_t w) {
+static void
+name2data(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+         dns_rpz_type_t rpz_type, const dns_name_t *src_name,
+         dns_name_t *trig_name, dns_rpz_nm_data_t *new_data)
+{
+       static dns_rpz_pair_zbits_t zero = {0, 0};
+       dns_rpz_zone_t *rpz;
+       dns_offsets_t tmp_name_offsets;
+       dns_name_t tmp_name;
+       unsigned int prefix_len, n;
+
+       REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+       rpz = rpzs->zones[rpz_num];
+       REQUIRE(rpz != NULL);
+
+       /*
+        * Handle wildcards by putting only the parent into the
+        * summary RBT.  The summary database only causes a check of the
+        * real policy zone where wildcards will be handled.
+        */
+       if (dns_name_iswildcard(src_name)) {
+               prefix_len = 1;
+               new_data->pair = zero;
+               make_pair(&new_data->wild, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+       } else {
+               prefix_len = 0;
+               make_pair(&new_data->pair, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+               new_data->wild = zero;
+       }
+
+       dns_name_init(&tmp_name, tmp_name_offsets);
+       n = dns_name_countlabels(src_name);
+       n -= prefix_len;
+       if (rpz_type == DNS_RPZ_TYPE_QNAME)
+               n -= dns_name_countlabels(&rpz->origin);
+       else
+               n -= dns_name_countlabels(&rpz->nsdname);
+       dns_name_getlabelsequence(src_name, prefix_len, n, &tmp_name);
+       (void)dns_name_concatenate(&tmp_name, dns_rootname, trig_name, NULL);
+}
+
+/*
+ * Find the first differing bit in a key (IP address) word.
+ */
+static inline int
+ffs_keybit(dns_rpz_cidr_word_t w) {
        int bit;
 
        bit = DNS_RPZ_CIDR_WORD_BITS-1;
@@ -750,17 +792,17 @@ ffbit(dns_rpz_cidr_word_t w) {
 }
 
 /*
- * Find the first differing bit in two keys.
+ * Find the first differing bit in two keys (IP addresses).
  */
 static int
-diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_cidr_bits_t bits1,
-         const dns_rpz_cidr_key_t *key2, dns_rpz_cidr_bits_t bits2)
+diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_prefix_t prefix1,
+         const dns_rpz_cidr_key_t *key2, dns_rpz_prefix_t prefix2)
 {
        dns_rpz_cidr_word_t delta;
-       dns_rpz_cidr_bits_t maxbit, bit;
+       dns_rpz_prefix_t maxbit, bit;
        int i;
 
-       maxbit = ISC_MIN(bits1, bits2);
+       maxbit = ISC_MIN(prefix1, prefix2);
 
        /*
         * find the first differing words
@@ -770,141 +812,169 @@ diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_cidr_bits_t bits1,
             i++, bit += DNS_RPZ_CIDR_WORD_BITS) {
                delta = key1->w[i] ^ key2->w[i];
                if (delta != 0) {
-                       bit += ffbit(delta);
+                       bit += ffs_keybit(delta);
                        break;
                }
        }
        return (ISC_MIN(bit, maxbit));
 }
 
+/*
+ * Given a hit while searching the radix trees,
+ * clear all bits for higher numbered zones.
+ */
+static inline dns_rpz_zbits_t
+trim_zbits(dns_rpz_zbits_t zbits, dns_rpz_zbits_t found) {
+       dns_rpz_zbits_t x;
+
+       /*
+        * Isolate the first or smallest numbered hit bit.
+        * Make a mask of that bit and all smaller numbered bits.
+        */
+       x = zbits & found;
+       x &= -x;
+       x = (x << 1) - 1;
+       return (zbits &= x);
+}
+
 /*
  * Search a radix tree for an IP address for ordinary lookup
  *     or for a CIDR block adding or deleting an entry
- * The tree read (for simple search) or write lock must be held by the caller.
  *
- * Return ISC_R_SUCCESS, ISC_R_NOTFOUND, DNS_R_PARTIALMATCH, ISC_R_EXISTS,
- *     ISC_R_NOMEMORY
+ * Return ISC_R_SUCCESS, DNS_R_PARTIALMATCH, ISC_R_NOTFOUND,
+ *         and *found=longest match node
+ *     or with create==ISC_TRUE, ISC_R_EXISTS or ISC_R_NOMEMORY
  */
 static isc_result_t
-search(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
-       dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type,
-       isc_boolean_t create,
-       dns_rpz_cidr_node_t **found)            /* NULL or longest match node */
+search(dns_rpz_zones_t *rpzs,
+       const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+       const dns_rpz_pair_zbits_t *tgt_pair, isc_boolean_t create,
+       dns_rpz_cidr_node_t **found)
 {
        dns_rpz_cidr_node_t *cur, *parent, *child, *new_parent, *sibling;
+       dns_rpz_pair_zbits_t pair;
        int cur_num, child_num;
-       dns_rpz_cidr_bits_t dbit;
-       dns_rpz_cidr_flags_t flags, data_flag;
+       dns_rpz_prefix_t dbit;
        isc_result_t find_result;
 
-       flags = get_flags(tgt_ip, tgt_prefix, type);
-       data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA |
-                            DNS_RPZ_CIDR_FG_NSIP_DATA);
-
+       pair = *tgt_pair;
        find_result = ISC_R_NOTFOUND;
-       if (found != NULL)
-               *found = NULL;
-       cur = cidr->root;
+       *found = NULL;
+       cur = rpzs->cidr;
        parent = NULL;
        cur_num = 0;
        for (;;) {
                if (cur == NULL) {
                        /*
-                        * No child so we cannot go down.  Fail or
-                        * add the target as a child of the current parent.
+                        * No child so we cannot go down.
+                        * Quit with whatever we already found
+                        * or add the target as a child of the current parent.
                         */
                        if (!create)
                                return (find_result);
-                       child = new_node(cidr, tgt_ip, tgt_prefix, 0);
+                       child = new_node(rpzs, tgt_ip, tgt_prefix, NULL);
                        if (child == NULL)
                                return (ISC_R_NOMEMORY);
                        if (parent == NULL)
-                               cidr->root = child;
+                               rpzs->cidr = child;
                        else
                                parent->child[cur_num] = child;
                        child->parent = parent;
-                       set_node_flags(child, type);
-                       if (found != NULL)
-                               *found = cur;
+                       child->pair.d |= tgt_pair->d;
+                       child->pair.ns |= tgt_pair->ns;
+                       set_sum_pair(child);
+                       *found = cur;
                        return (ISC_R_SUCCESS);
                }
 
-               /*
-                * Pretend a node not in the correct tree does not exist
-                * if we are not adding to the tree,
-                * If we are adding, then continue down to eventually
-                * add a node and mark/put this node in the correct tree.
-                */
-               if ((cur->flags & flags) == 0 && !create)
-                       return (find_result);
+               if ((cur->sum.d & pair.d) == 0 &&
+                   (cur->sum.ns & pair.ns) == 0) {
+                       /*
+                        * This node has no relevant data
+                        * and is in none of the target trees.
+                        * Pretend it does not exist if we are not adding.
+                        *
+                        * If we are adding, continue down to eventually add
+                        * a node and mark/put this node in the correct tree.
+                        */
+                       if (!create)
+                               return (find_result);
+               }
 
-               dbit = diff_keys(tgt_ip, tgt_prefix, &cur->ip, cur->bits);
+               dbit = diff_keys(tgt_ip, tgt_prefix, &cur->ip, cur->prefix);
                /*
-                * dbit <= tgt_prefix and dbit <= cur->bits always.
+                * dbit <= tgt_prefix and dbit <= cur->prefix always.
                 * We are finished searching if we matched all of the target.
                 */
                if (dbit == tgt_prefix) {
-                       if (tgt_prefix == cur->bits) {
+                       if (tgt_prefix == cur->prefix) {
                                /*
-                                * The current node matches the target exactly.
-                                * It is the answer if it has data.
+                                * The node's key matches the target exactly.
                                 */
-                               if ((cur->flags & data_flag) != 0) {
-                                       if (create)
-                                               return (ISC_R_EXISTS);
-                                       if (found != NULL)
-                                               *found = cur;
-                                       return (ISC_R_SUCCESS);
+                               if ((cur->pair.d & pair.d) != 0 ||
+                                   (cur->pair.ns & pair.ns) != 0) {
+                                       /*
+                                        * It is the answer if it has data.
+                                        */
+                                       *found = cur;
+                                       if (create) {
+                                           find_result = ISC_R_EXISTS;
+                                       } else {
+                                           find_result = ISC_R_SUCCESS;
+                                       }
                                } else if (create) {
                                        /*
-                                        * The node had no data but does now.
+                                        * The node lacked relevant data,
+                                        * but will have it now.
                                         */
-                                       set_node_flags(cur, type);
-                                       if (found != NULL)
-                                               *found = cur;
-                                       return (ISC_R_SUCCESS);
+                                       cur->pair.d |= tgt_pair->d;
+                                       cur->pair.ns |= tgt_pair->ns;
+                                       set_sum_pair(cur);
+                                       *found = cur;
+                                       find_result = ISC_R_SUCCESS;
                                }
                                return (find_result);
                        }
 
                        /*
-                        * We know tgt_prefix < cur_bits which means that
+                        * We know tgt_prefix < cur->prefix which means that
                         * the target is shorter than the current node.
                         * Add the target as the current node's parent.
                         */
                        if (!create)
                                return (find_result);
 
-                       new_parent = new_node(cidr, tgt_ip, tgt_prefix,
-                                             cur->flags);
+                       new_parent = new_node(rpzs, tgt_ip, tgt_prefix, cur);
                        if (new_parent == NULL)
                                return (ISC_R_NOMEMORY);
                        new_parent->parent = parent;
                        if (parent == NULL)
-                               cidr->root = new_parent;
+                               rpzs->cidr = new_parent;
                        else
                                parent->child[cur_num] = new_parent;
                        child_num = DNS_RPZ_IP_BIT(&cur->ip, tgt_prefix+1);
                        new_parent->child[child_num] = cur;
                        cur->parent = new_parent;
-                       set_node_flags(new_parent, type);
-                       if (found != NULL)
-                               *found = new_parent;
+                       new_parent->pair = *tgt_pair;
+                       set_sum_pair(new_parent);
+                       *found = new_parent;
                        return (ISC_R_SUCCESS);
                }
 
-               if (dbit == cur->bits) {
-                       /*
-                        * We have a partial match by matching of all of the
-                        * current node but only part of the target.
-                        * Try to go down.
-                        */
-                       if ((cur->flags & data_flag) != 0) {
+               if (dbit == cur->prefix) {
+                       if ((cur->pair.d & pair.d) != 0 ||
+                           (cur->pair.ns & pair.ns) != 0) {
+                               /*
+                                * We have a partial match between of all of the
+                                * current node but only part of the target.
+                                * Continue searching for other hits in the
+                                * same or lower numbered trees.
+                                */
                                find_result = DNS_R_PARTIALMATCH;
-                               if (found != NULL)
-                                       *found = cur;
+                               *found = cur;
+                               pair.d = trim_zbits(pair.d, cur->pair.d);
+                               pair.ns = trim_zbits(pair.ns, cur->pair.ns);
                        }
-
                        parent = cur;
                        cur_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
                        cur = cur->child[cur_num];
@@ -913,7 +983,7 @@ search(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
 
 
                /*
-                * dbit < tgt_prefix and dbit < cur->bits,
+                * dbit < tgt_prefix and dbit < cur->prefix,
                 * so we failed to match both the target and the current node.
                 * Insert a fork of a parent above the current node and
                 * add the target as a sibling of the current node
@@ -921,17 +991,17 @@ search(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
                if (!create)
                        return (find_result);
 
-               sibling = new_node(cidr, tgt_ip, tgt_prefix, 0);
+               sibling = new_node(rpzs, tgt_ip, tgt_prefix, NULL);
                if (sibling == NULL)
                        return (ISC_R_NOMEMORY);
-               new_parent = new_node(cidr, tgt_ip, dbit, cur->flags);
+               new_parent = new_node(rpzs, tgt_ip, dbit, cur);
                if (new_parent == NULL) {
-                       isc_mem_put(cidr->mctx, sibling, sizeof(*sibling));
+                       isc_mem_put(rpzs->mctx, sibling, sizeof(*sibling));
                        return (ISC_R_NOMEMORY);
                }
                new_parent->parent = parent;
                if (parent == NULL)
-                       cidr->root = new_parent;
+                       rpzs->cidr = new_parent;
                else
                        parent->child[cur_num] = new_parent;
                child_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
@@ -939,128 +1009,668 @@ search(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
                new_parent->child[1-child_num] = cur;
                cur->parent = new_parent;
                sibling->parent = new_parent;
-               set_node_flags(sibling, type);
-               if (found != NULL)
-                       *found = sibling;
+               sibling->pair = *tgt_pair;
+               set_sum_pair(sibling);
+               *found = sibling;
                return (ISC_R_SUCCESS);
        }
 }
 
 /*
- * Add an IP address to the radix tree of a response policy database.
- *     The tree write lock must be held by the caller.
+ * Add an IP address to the radix tree.
  */
-void
-dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) {
-       isc_result_t result;
+static isc_result_t
+add_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+        dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
        dns_rpz_cidr_key_t tgt_ip;
-       dns_rpz_cidr_bits_t tgt_prefix;
-       dns_rpz_type_t type;
-
-       if (cidr == NULL)
-               return;
+       dns_rpz_prefix_t tgt_prefix;
+       dns_rpz_pair_zbits_t pair;
+       dns_rpz_cidr_node_t *found;
+       isc_result_t result;
 
+       result = name2ipkey(DNS_RPZ_ERROR_LEVEL, rpzs, rpz_num, rpz_type,
+                           src_name, &tgt_ip, &tgt_prefix, &pair);
        /*
-        * No worries if the new name is not an IP address.
+        * Log complaints about bad owner names but let the zone load.
         */
-       type = set_type(cidr, name);
-       switch (type) {
-       case DNS_RPZ_TYPE_IP:
-       case DNS_RPZ_TYPE_NSIP:
-               break;
-       case DNS_RPZ_TYPE_NSDNAME:
-               cidr->have_nsdname = ISC_TRUE;
-               return;
-       case DNS_RPZ_TYPE_QNAME:
-       case DNS_RPZ_TYPE_BAD:
-               return;
-       }
-       result = name2ipkey(cidr, DNS_RPZ_ERROR_LEVEL, name,
-                           type, &tgt_ip, &tgt_prefix);
        if (result != ISC_R_SUCCESS)
-               return;
+               return (ISC_R_SUCCESS);
 
-       result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_TRUE, NULL);
-       if (result == ISC_R_EXISTS &&
-           isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL))
-       {
-               char printname[DNS_NAME_FORMATSIZE];
+       result = search(rpzs, &tgt_ip, tgt_prefix, &pair, ISC_TRUE, &found);
+       if (result != ISC_R_SUCCESS) {
+               char namebuf[DNS_NAME_FORMATSIZE];
 
-               dns_name_format(name, printname, sizeof(printname));
+               /*
+                * 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,
                              DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
-                             "rpz add failed; \"%s\" is a duplicate name",
-                             printname);
+                             "rpz add_cidr(%s) failed: %s",
+                             namebuf, isc_result_totext(result));
+               return (result);
        }
+
+       adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, ISC_TRUE);
+       return (result);
 }
 
-/*
- * Delete an IP address from the radix tree of a response policy database.
- *     The tree write lock must be held by the caller.
- */
-void
-dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) {
+static isc_result_t
+add_nm(dns_rpz_zones_t *rpzs, dns_name_t *trig_name,
+        const dns_rpz_nm_data_t *new_data)
+{
+       dns_rbtnode_t *nmnode;
+       dns_rpz_nm_data_t *nm_data;
        isc_result_t result;
-       dns_rpz_cidr_key_t tgt_ip;
-       dns_rpz_cidr_bits_t tgt_prefix;
-       dns_rpz_type_t type;
-       dns_rpz_cidr_node_t *tgt = NULL, *parent, *child;
-       dns_rpz_cidr_flags_t flags, data_flag;
 
-       if (cidr == NULL)
-               return;
+       nmnode = NULL;
+       result = dns_rbt_addnode(rpzs->rbt, trig_name, &nmnode);
+       switch (result) {
+       case ISC_R_SUCCESS:
+       case ISC_R_EXISTS:
+               nm_data = nmnode->data;
+               if (nm_data == NULL) {
+                       nm_data = isc_mem_get(rpzs->mctx, sizeof(*nm_data));
+                       if (nm_data == NULL)
+                               return (ISC_R_NOMEMORY);
+                       *nm_data = *new_data;
+                       nmnode->data = nm_data;
+                       return (ISC_R_SUCCESS);
+               }
+               break;
+       default:
+               return (result);
+       }
 
        /*
-        * Decide which kind of policy zone IP address it is, if either
-        * and then find its node.
+        * Do not count bits that are already present
         */
-       type = set_type(cidr, name);
-       switch (type) {
-       case DNS_RPZ_TYPE_IP:
-       case DNS_RPZ_TYPE_NSIP:
-               break;
-       case DNS_RPZ_TYPE_NSDNAME:
+       if ((nm_data->pair.d & new_data->pair.d) != 0 ||
+           (nm_data->pair.ns & new_data->pair.ns) != 0 ||
+           (nm_data->wild.d & new_data->wild.d) != 0 ||
+           (nm_data->wild.ns & new_data->wild.ns) != 0) {
+               char namebuf[DNS_NAME_FORMATSIZE];
+
                /*
-                * We cannot easily count nsdnames because
-                * internal rbt nodes get deleted.
+                * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
                 */
-               return;
-       case DNS_RPZ_TYPE_QNAME:
-       case DNS_RPZ_TYPE_BAD:
-               return;
+               dns_name_format(trig_name, namebuf, sizeof(namebuf));
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "rpz add_nm(%s): bits already set", namebuf);
+               return (ISC_R_EXISTS);
        }
 
-       /*
-        * Do not get excited about the deletion of interior rbt nodes.
-        */
-       result = name2ipkey(cidr, DNS_RPZ_DEBUG_QUIET, name,
-                           type, &tgt_ip, &tgt_prefix);
-       if (result != ISC_R_SUCCESS)
-               return;
-
-       result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_FALSE, &tgt);
+       nm_data->pair.d |= new_data->pair.d;
+       nm_data->pair.ns |= new_data->pair.ns;
+       nm_data->wild.d |= new_data->wild.d;
+       nm_data->wild.ns |= new_data->wild.ns;
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+add_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+        dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+       dns_rpz_nm_data_t new_data;
+       dns_fixedname_t trig_namef;
+       dns_name_t *trig_name;
+       isc_result_t result;
+
+       dns_fixedname_init(&trig_namef);
+       trig_name = dns_fixedname_name(&trig_namef);
+       name2data(rpzs, rpz_num, rpz_type, src_name, trig_name, &new_data);
+
+       result = add_nm(rpzs, trig_name, &new_data);
+       if (result == ISC_R_SUCCESS)
+               adj_trigger_cnt(rpzs, rpz_num, rpz_type, NULL, 0, ISC_TRUE);
+       return (result);
+}
+
+/*
+ * Callback to free the data for a node in the summary RBT database.
+ */
+static void
+rpz_node_deleter(void *nm_data, void *mctx) {
+       isc_mem_put(mctx, nm_data, sizeof(dns_rpz_nm_data_t));
+}
+
+/*
+ * 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;
+       isc_result_t result;
+
+       REQUIRE(rpzsp != NULL && *rpzsp == NULL);
+
+       *rpzsp = NULL;
+
+       new = isc_mem_get(mctx, sizeof(*new));
+       if (new == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(new, 0, sizeof(*new));
+
+       result = isc_mutex_init(&new->search_lock);
        if (result != ISC_R_SUCCESS) {
-               badname(DNS_RPZ_ERROR_LEVEL, name, "; missing rpz node", "");
+               isc_mem_put(mctx, new, sizeof(*new));
+               return (result);
+       }
+
+       result = isc_mutex_init(&new->maint_lock);
+       if (result != ISC_R_SUCCESS) {
+               DESTROYLOCK(&new->search_lock);
+               isc_mem_put(mctx, new, sizeof(*new));
+               return (result);
+       }
+
+       result = isc_refcount_init(&new->refs, 1);
+       if (result != ISC_R_SUCCESS) {
+               DESTROYLOCK(&new->maint_lock);
+               DESTROYLOCK(&new->search_lock);
+               isc_mem_put(mctx, new, sizeof(*new));
+               return (result);
+       }
+
+       result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &new->rbt);
+       if (result != ISC_R_SUCCESS) {
+               isc_refcount_decrement(&new->refs, NULL);
+               isc_refcount_destroy(&new->refs);
+               DESTROYLOCK(&new->maint_lock);
+               DESTROYLOCK(&new->search_lock);
+               isc_mem_put(mctx, new, sizeof(*new));
+               return (result);
+       }
+
+       isc_mem_attach(mctx, &new->mctx);
+
+       *rpzsp = new;
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Free the radix tree of a response policy database.
+ */
+static void
+cidr_free(dns_rpz_zones_t *rpzs) {
+       dns_rpz_cidr_node_t *cur, *child, *parent;
+
+       cur = rpzs->cidr;
+       while (cur != NULL) {
+               /* Depth first. */
+               child = cur->child[0];
+               if (child != NULL) {
+                       cur = child;
+                       continue;
+               }
+               child = cur->child[1];
+               if (child != NULL) {
+                       cur = child;
+                       continue;
+               }
+
+               /* Delete this leaf and go up. */
+               parent = cur->parent;
+               if (parent == NULL)
+                       rpzs->cidr = NULL;
+               else
+                       parent->child[parent->child[1] == cur] = NULL;
+               isc_mem_put(rpzs->mctx, cur, sizeof(*cur));
+               cur = parent;
+       }
+}
+
+/*
+ * Discard a response policy zone blob
+ * before discarding the overall rpz structure.
+ */
+static void
+rpz_detach(dns_rpz_zone_t **rpzp, dns_rpz_zones_t *rpzs) {
+       dns_rpz_zone_t *rpz;
+       unsigned int refs;
+
+       rpz = *rpzp;
+       *rpzp = NULL;
+       isc_refcount_decrement(&rpz->refs, &refs);
+       if (refs != 0)
                return;
+       isc_refcount_destroy(&rpz->refs);
+
+       if (dns_name_dynamic(&rpz->origin))
+               dns_name_free(&rpz->origin, rpzs->mctx);
+       if (dns_name_dynamic(&rpz->ip))
+               dns_name_free(&rpz->ip, rpzs->mctx);
+       if (dns_name_dynamic(&rpz->nsdname))
+               dns_name_free(&rpz->nsdname, rpzs->mctx);
+       if (dns_name_dynamic(&rpz->nsip))
+               dns_name_free(&rpz->nsip, rpzs->mctx);
+       if (dns_name_dynamic(&rpz->passthru))
+               dns_name_free(&rpz->passthru, rpzs->mctx);
+       if (dns_name_dynamic(&rpz->cname))
+               dns_name_free(&rpz->cname, rpzs->mctx);
+
+       isc_mem_put(rpzs->mctx, rpz, sizeof(*rpz));
+}
+
+void
+dns_rpz_attach_rpzs(dns_rpz_zones_t *rpzs, dns_rpz_zones_t **rpzsp) {
+       REQUIRE(rpzsp != NULL && *rpzsp == NULL);
+       isc_refcount_increment(&rpzs->refs, NULL);
+       *rpzsp = rpzs;
+}
+
+/*
+ * Forget a view's policy zones.
+ */
+void
+dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) {
+       dns_rpz_zones_t *rpzs;
+       dns_rpz_zone_t *rpz;
+       dns_rpz_num_t rpz_num;
+       unsigned int refs;
+
+       REQUIRE(rpzsp != NULL);
+       rpzs = *rpzsp;
+       REQUIRE(rpzs != NULL);
+
+       *rpzsp = NULL;
+       isc_refcount_decrement(&rpzs->refs, &refs);
+
+       /*
+        * Forget the last of view's rpz machinery after the last reference.
+        */
+       if (refs == 0) {
+               for (rpz_num = 0; rpz_num < DNS_RPZ_MAX_ZONES; ++rpz_num) {
+                       rpz = rpzs->zones[rpz_num];
+                       rpzs->zones[rpz_num] = NULL;
+                       if (rpz != NULL)
+                               rpz_detach(&rpz, rpzs);
+               }
+
+               cidr_free(rpzs);
+               dns_rbt_destroy(&rpzs->rbt);
+               DESTROYLOCK(&rpzs->maint_lock);
+               DESTROYLOCK(&rpzs->search_lock);
+               isc_refcount_destroy(&rpzs->refs);
+               isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
        }
+}
+
+/*
+ * Create empty summary database to load one zone.
+ * The RBTDB write tree lock must be held.
+ */
+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);
 
        /*
-        * Mark the node and its parents to reflect the deleted IP address.
+        * 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 database and replace the
+        *      old summary database with the new.
+        *
+        * 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
         */
-       flags = get_flags(&tgt_ip, tgt_prefix, type);
-       data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA |
-                             DNS_RPZ_CIDR_FG_NSIP_DATA);
-       tgt->flags &= ~data_flag;
-       for (parent = tgt; parent != NULL; parent = parent->parent) {
-               if ((parent->flags & data_flag) != 0 ||
-                   (parent->child[0] != NULL &&
-                    (parent->child[0]->flags & flags) != 0) ||
-                   (parent->child[1] != NULL &&
-                    (parent->child[1]->flags & flags) != 0))
-                       break;
-               parent->flags &= ~flags;
+       tgt = DNS_RPZ_ZBIT(rpz_num);
+       LOCK(&rpzs->maint_lock);
+       LOCK(&rpzs->search_lock);
+       if ((rpzs->load_begun & tgt) == 0) {
+               rpzs->load_begun |= tgt;
+               dns_rpz_attach_rpzs(rpzs, load_rpzsp);
+               UNLOCK(&rpzs->search_lock);
+               UNLOCK(&rpzs->maint_lock);
+
+       } else {
+               UNLOCK(&rpzs->search_lock);
+               UNLOCK(&rpzs->maint_lock);
+
+               result = dns_rpz_new_zones(load_rpzsp, rpzs->mctx);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+               load_rpzs = *load_rpzsp;
+               load_rpzs->p.num_zones = rpzs->p.num_zones;
+               load_rpzs->zones[rpz_num] = rpz;
+               isc_refcount_increment(&rpz->refs, NULL);
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+static void
+fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_triggers_t *totals) {
+       dns_rpz_num_t rpz_num;
+       const dns_rpz_zone_t *rpz;
+       dns_rpz_zbits_t zbit;
+
+#      define SET_TRIG(type)                                           \
+       if (rpz == NULL || rpz->triggers.type == 0) {                   \
+               rpzs->have.type &= ~zbit;                               \
+       } else {                                                        \
+               totals->type += rpz->triggers.type;                     \
+               rpzs->have.type |= zbit;                                \
+       }
+
+       memset(totals, 0, sizeof(*totals));
+       for (rpz_num = 0; rpz_num < rpzs->p.num_zones; ++rpz_num) {
+               rpz = rpzs->zones[rpz_num];
+               zbit = DNS_RPZ_ZBIT(rpz_num);
+               SET_TRIG(nsdname);
+               SET_TRIG(qname);
+               SET_TRIG(ipv4);
+               SET_TRIG(ipv6);
+               SET_TRIG(nsipv4);
+               SET_TRIG(nsipv6);
+       }
+
+       fix_qname_skip_recurse(rpzs);
+
+#      undef SET_TRIG
+}
+
+static void
+load_unlock(dns_rpz_zones_t *rpzs, dns_rpz_zones_t **load_rpzsp) {
+       UNLOCK(&rpzs->maint_lock);
+       UNLOCK(&(*load_rpzsp)->search_lock);
+       UNLOCK(&(*load_rpzsp)->maint_lock);
+       dns_rpz_detach_rpzs(load_rpzsp);
+}
+
+/*
+ * Finish loading one zone.
+ * The RBTDB write tree lock must be held.
+ */
+isc_result_t
+dns_rpz_ready(dns_rpz_zones_t *rpzs,
+             dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num)
+{
+       char namebuf[DNS_NAME_FORMATSIZE];
+       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_pair_zbits_t load_pair, new_pair;
+       dns_rbt_t *rbt;
+       dns_rbtnodechain_t chain;
+       dns_rbtnode_t *nmnode;
+       dns_rpz_nm_data_t *nm_data, new_data;
+       dns_rpz_triggers_t old_totals, new_totals;
+       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.
+                */
+               fix_triggers(rpzs, &new_totals);
+               UNLOCK(&rpzs->maint_lock);
+               dns_rpz_detach_rpzs(load_rpzsp);
+
+               if (rpz_num == rpzs->p.num_zones-1)
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                                     DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL,
+                                     "loaded policy %d zones with"
+                                     " %d qname  %d nsdname"
+                                     "  % d IP  %d NSIP entries",
+                                     rpzs->p.num_zones,
+                                     new_totals.qname,
+                                     new_totals.nsdname,
+                                     new_totals.ipv4 + new_totals.ipv6,
+                                     new_totals.nsipv4 +
+                                     new_totals.nsipv6);
+               return (ISC_R_SUCCESS);
+       }
+
+       LOCK(&load_rpzs->maint_lock);
+       LOCK(&load_rpzs->search_lock);
+
+       /*
+        * Copy the other policy zones to the new summary databases
+        * unless there is only one policy zone.
+        */
+       if (rpzs->p.num_zones > 1) {
+               /*
+                * Copy to the radix tree.
+                */
+               load_pair.d = ~DNS_RPZ_ZBIT(rpz_num);
+               load_pair.ns = load_pair.d;
+               for (cnode = rpzs->cidr; cnode != NULL; cnode = next_cnode) {
+                       new_pair.d = cnode->pair.d & load_pair.d;
+                       new_pair.ns = cnode->pair.ns & load_pair.ns;
+                       if (new_pair.d != 0 || new_pair.ns != 0) {
+                               result = search(load_rpzs,
+                                               &cnode->ip, cnode->prefix,
+                                               &new_pair, ISC_TRUE,
+                                               &found);
+                               if (result == ISC_R_NOMEMORY) {
+                                       load_unlock(rpzs, load_rpzsp);
+                                       return (result);
+                               }
+                               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 branck 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.pair.d = (nm_data->pair.d &
+                                                  load_pair.d);
+                               new_data.pair.ns = (nm_data->pair.ns &
+                                                  load_pair.ns);
+                               new_data.wild.d = (nm_data->wild.d &
+                                                  load_pair.d);
+                               new_data.wild.ns = (nm_data->wild.ns &
+                                                  load_pair.ns);
+                               if (new_data.pair.d != 0 ||
+                                   new_data.pair.ns != 0 ||
+                                   new_data.wild.d != 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) {
+                                           load_unlock(rpzs, load_rpzsp);
+                                           return (result);
+                                       }
+                               }
+                       }
+                       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));
+                       load_unlock(rpzs, load_rpzsp);
+                       return (result);
+               }
+       }
+
+       fix_triggers(rpzs, &old_totals);
+       fix_triggers(load_rpzs, &new_totals);
+
+       dns_name_format(&load_rpzs->zones[rpz_num]->origin,
+                       namebuf, sizeof(namebuf));
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                     DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL,
+                     "reloading policy zone '%s' changed from"
+                     " %d to %d qname, %d to %d nsdname,"
+                     " %d to %d IP, %d to %d NSIP entries",
+                     namebuf,
+                     old_totals.qname, new_totals.qname,
+                     old_totals.nsdname, new_totals.nsdname,
+                     old_totals.ipv4 + old_totals.ipv6,
+                     new_totals.ipv4 + new_totals.ipv6,
+                     old_totals.nsipv4 + old_totals.nsipv6,
+                     new_totals.nsipv4 + new_totals.nsipv6);
+
+       /*
+        * Exchange the summary databases.
+        */
+       LOCK(&rpzs->search_lock);
+
+       found = rpzs->cidr;
+       rpzs->cidr = load_rpzs->cidr;
+       load_rpzs->cidr = found;
+
+       rbt = rpzs->rbt;
+       rpzs->rbt = load_rpzs->rbt;
+       load_rpzs->rbt = rbt;
+
+       UNLOCK(&rpzs->search_lock);
+       load_unlock(rpzs, load_rpzsp);
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Add an IP address to the radix tree or a name to the summary database.
+ */
+isc_result_t
+dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+           dns_name_t *src_name) {
+       dns_rpz_zone_t *rpz;
+       dns_rpz_type_t rpz_type;
+       isc_result_t result;
+
+       REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+       rpz = rpzs->zones[rpz_num];
+       REQUIRE(rpz != NULL);
+
+       rpz_type = type_from_name(rpz, src_name);
+
+       LOCK(&rpzs->maint_lock);
+       LOCK(&rpzs->search_lock);
+
+       switch (rpz_type) {
+       case DNS_RPZ_TYPE_QNAME:
+       case DNS_RPZ_TYPE_NSDNAME:
+               result = add_name(rpzs, rpz_num, rpz_type, src_name);
+               break;
+       case DNS_RPZ_TYPE_IP:
+       case DNS_RPZ_TYPE_NSIP:
+               result = add_cidr(rpzs, rpz_num, rpz_type, src_name);
+               break;
+       case DNS_RPZ_TYPE_BAD:
+               break;
+       }
+
+       UNLOCK(&rpzs->search_lock);
+       UNLOCK(&rpzs->maint_lock);
+       return (result);
+}
+
+/*
+ * Remove an IP address from the radix tree.
+ */
+static void
+del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+        dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+       isc_result_t result;
+       dns_rpz_cidr_key_t tgt_ip;
+       dns_rpz_prefix_t tgt_prefix;
+       dns_rpz_pair_zbits_t pair;
+       dns_rpz_cidr_node_t *tgt, *parent, *child;
+
+       /*
+        * Do not worry about invalid rpz IP address names.  If we
+        * are here, then something relevant was added and so was
+        * valid.  Invalid names here are usually internal RBTDB nodes.
+        */
+       result = name2ipkey(DNS_RPZ_DEBUG_QUIET, rpzs, rpz_num, rpz_type,
+                           src_name, &tgt_ip, &tgt_prefix, &pair);
+       if (result != ISC_R_SUCCESS)
+               return;
+
+       result = search(rpzs, &tgt_ip, tgt_prefix, &pair, ISC_FALSE, &tgt);
+       if (result != ISC_R_SUCCESS) {
+               INSIST(result == ISC_R_NOTFOUND ||
+                      result == DNS_R_PARTIALMATCH);
+               /*
+                * Do not worry about missing summary RBT nodes that probably
+                * correspond to RBTDB nodes that were implicit RBT nodes
+                * that were later added for (often empty) wildcards
+                * and then to the RBTDB deferred cleanup list.
+                */
+               return;
        }
 
+       /*
+        * Mark the node and its parents to reflect the deleted IP address.
+        * Do not count bits that are already clear for internal RBTDB nodes.
+        */
+       pair.d &= tgt->pair.d;
+       pair.ns &= tgt->pair.ns;
+       tgt->pair.d &= ~pair.d;
+       tgt->pair.ns &= ~pair.ns;
+       set_sum_pair(tgt);
+
+       adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, ISC_FALSE);
+
        /*
         * We might need to delete 2 nodes.
         */
@@ -1071,13 +1681,12 @@ dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) {
                 */
                if ((child = tgt->child[0]) != NULL) {
                        if (tgt->child[1] != NULL)
-                               return;
+                               break;
                } else {
                        child = tgt->child[1];
                }
-               if ((tgt->flags & (DNS_RPZ_CIDR_FG_IP_DATA |
-                                DNS_RPZ_CIDR_FG_NSIP_DATA)) != 0)
-                       return;
+               if (tgt->pair.d + tgt->pair.ns != 0)
+                       break;
 
                /*
                 * Replace the pointer to this node in the parent with
@@ -1085,7 +1694,7 @@ dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) {
                 */
                parent = tgt->parent;
                if (parent == NULL) {
-                       cidr->root = child;
+                       rpzs->cidr = child;
                } else {
                        parent->child[parent->child[1] == tgt] = child;
                }
@@ -1094,26 +1703,143 @@ dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) {
                 */
                if (child != NULL)
                        child->parent = parent;
-               isc_mem_put(cidr->mctx, tgt, sizeof(*tgt));
+               isc_mem_put(rpzs->mctx, tgt, sizeof(*tgt));
 
                tgt = parent;
        } while (tgt != NULL);
 }
 
+static void
+del_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+        dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+       char namebuf[DNS_NAME_FORMATSIZE];
+       dns_fixedname_t trig_namef;
+       dns_name_t *trig_name;
+       dns_rbtnode_t *nmnode;
+       dns_rpz_nm_data_t *nm_data, del_data;
+       isc_result_t result;
+
+       dns_fixedname_init(&trig_namef);
+       trig_name = dns_fixedname_name(&trig_namef);
+       name2data(rpzs, rpz_num, rpz_type, src_name, trig_name, &del_data);
+
+       /*
+        * No need for a summary database of names with only 1 policy zone.
+        */
+       if (rpzs->p.num_zones <= 1) {
+               adj_trigger_cnt(rpzs, rpz_num, rpz_type, NULL, 0, ISC_FALSE);
+               return;
+       }
+
+       nmnode = NULL;
+       result = dns_rbt_findnode(rpzs->rbt, trig_name, NULL, &nmnode, NULL, 0,
+                                 NULL, NULL);
+       if (result != ISC_R_SUCCESS) {
+               /*
+                * Do not worry about missing summary RBT nodes that probably
+                * correspond to RBTDB nodes that were implicit RBT nodes
+                * that were later added for (often empty) wildcards
+                * and then to the RBTDB deferred cleanup list.
+                */
+               if (result == ISC_R_NOTFOUND)
+                       return;
+               dns_name_format(src_name, namebuf, sizeof(namebuf));
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "rpz del_name(%s) node search failed: %s",
+                             namebuf, isc_result_totext(result));
+               return;
+       }
+
+       nm_data = nmnode->data;
+       INSIST(nm_data != NULL);
+
+       /*
+        * Do not count bits that next existed for RBT nodes that would we
+        * would not have found in a summary for a single RBTDB tree.
+        */
+       del_data.pair.d &= nm_data->pair.d;
+       del_data.pair.ns &= nm_data->pair.ns;
+       del_data.wild.d &= nm_data->wild.d;
+       del_data.wild.ns &= nm_data->wild.ns;
+
+       nm_data->pair.d &= ~del_data.pair.d;
+       nm_data->pair.ns &= ~del_data.pair.ns;
+       nm_data->wild.d &= ~del_data.wild.d;
+       nm_data->wild.ns &= ~del_data.wild.ns;
+
+       if (nm_data->pair.d == 0 && nm_data->pair.ns == 0 &&
+           nm_data->wild.d == 0 && nm_data->wild.ns == 0) {
+               result = dns_rbt_deletenode(rpzs->rbt, nmnode, ISC_FALSE);
+               if (result != ISC_R_SUCCESS) {
+                       /*
+                        * 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,
+                                     DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                                     "rpz del_name(%s) node delete failed: %s",
+                                     namebuf, isc_result_totext(result));
+               }
+       }
+
+       adj_trigger_cnt(rpzs, rpz_num, rpz_type, NULL, 0, ISC_FALSE);
+}
+
 /*
- * Caller must hold tree lock.
- * Return  ISC_R_NOTFOUND
- *     or ISC_R_SUCCESS and the found entry's canonical and search names
- *         and its prefix length
+ * Remove an IP address from the radix tree or a name from the summary database.
  */
-isc_result_t
-dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr,
-                 dns_rpz_type_t type, dns_name_t *canon_name,
-                 dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix)
+void
+dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+              dns_name_t *src_name) {
+       dns_rpz_zone_t *rpz;
+       dns_rpz_type_t rpz_type;
+
+       REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+       rpz = rpzs->zones[rpz_num];
+       REQUIRE(rpz != NULL);
+
+       rpz_type = type_from_name(rpz, src_name);
+
+       LOCK(&rpzs->maint_lock);
+       LOCK(&rpzs->search_lock);
+
+       switch (rpz_type) {
+       case DNS_RPZ_TYPE_QNAME:
+       case DNS_RPZ_TYPE_NSDNAME:
+               del_name(rpzs, rpz_num, rpz_type, src_name);
+               break;
+       case DNS_RPZ_TYPE_IP:
+       case DNS_RPZ_TYPE_NSIP:
+               del_cidr(rpzs, rpz_num, rpz_type, src_name);
+               break;
+       case DNS_RPZ_TYPE_BAD:
+               break;
+       }
+
+       UNLOCK(&rpzs->search_lock);
+       UNLOCK(&rpzs->maint_lock);
+}
+
+/*
+ * Search the summary radix tree to get a relative owner name in a
+ * policy zone relevant to a triggering IP address.
+ *     rpz_type and zbits limit the search for IP address netaddr
+ *     return the policy zone's number or DNS_RPZ_INVALID_NUM
+ *     ip_name is the relative owner name found and
+ *     *prefixp is its prefix length.
+ */
+dns_rpz_num_t
+dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+               dns_rpz_zbits_t zbits, const isc_netaddr_t *netaddr,
+               dns_name_t *ip_name, dns_rpz_prefix_t *prefixp)
 {
        dns_rpz_cidr_key_t tgt_ip;
-       isc_result_t result;
+       dns_rpz_pair_zbits_t pair;
        dns_rpz_cidr_node_t *found;
+       isc_result_t result;
+       dns_rpz_num_t rpz_num;
        int i;
 
        /*
@@ -1124,29 +1850,136 @@ dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr,
                tgt_ip.w[1] = 0;
                tgt_ip.w[2] = ADDR_V4MAPPED;
                tgt_ip.w[3] = ntohl(netaddr->type.in.s_addr);
+               if (rpz_type == DNS_RPZ_TYPE_IP)
+                       zbits &= rpzs->have.ipv4;
+               else
+                       zbits &= rpzs->have.nsipv4;
        } else if (netaddr->family == AF_INET6) {
                dns_rpz_cidr_key_t src_ip6;
 
                /*
                 * Given the int aligned struct in_addr member of netaddr->type
                 * one could cast netaddr->type.in6 to dns_rpz_cidr_key_t *,
-                * but there are objections.
+                * but some people object.
                 */
                memcpy(src_ip6.w, &netaddr->type.in6, sizeof(src_ip6.w));
                for (i = 0; i < 4; i++) {
                        tgt_ip.w[i] = ntohl(src_ip6.w[i]);
                }
+               if (rpz_type == DNS_RPZ_TYPE_IP)
+                       zbits &= rpzs->have.ipv6;
+               else
+                       zbits &= rpzs->have.nsipv6;
        } else {
-               return (ISC_R_NOTFOUND);
+               return (DNS_RPZ_INVALID_NUM);
        }
 
-       result = search(cidr, &tgt_ip, 128, type, ISC_FALSE, &found);
-       if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH)
-               return (result);
+       if (zbits == 0)
+               return (DNS_RPZ_INVALID_NUM);
+       make_pair(&pair, zbits, rpz_type);
+
+       LOCK(&rpzs->search_lock);
+       result = search(rpzs, &tgt_ip, 128, &pair, ISC_FALSE, &found);
+       if (result == ISC_R_NOTFOUND) {
+               /*
+                * There are no eligible zones for this IP address.
+                */
+               UNLOCK(&rpzs->search_lock);
+               return (DNS_RPZ_INVALID_NUM);
+       }
+
+       /*
+        * Construct the trigger name for the longest matching trigger
+        * in the first eligible zone with a match.
+        */
+       *prefixp = found->prefix;
+       if (rpz_type == DNS_RPZ_TYPE_IP) {
+               INSIST((found->pair.d & pair.d) != 0);
+               rpz_num = zbit_to_num(found->pair.d & pair.d);
+       } else {
+               INSIST((found->pair.ns & pair.ns) != 0);
+               rpz_num = zbit_to_num(found->pair.ns & pair.ns);
+       }
+       result = ip2name(&found->ip, found->prefix, dns_rootname, ip_name);
+       UNLOCK(&rpzs->search_lock);
+       if (result != ISC_R_SUCCESS) {
+               /*
+                * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+                */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "rpz ip2name() failed: %s",
+                             isc_result_totext(result));
+               return (DNS_RPZ_INVALID_NUM);
+       }
+       return (rpz_num);
+}
+
+/*
+ * Search the summary radix tree for policy zones with triggers matching
+ * a name.
+ */
+dns_rpz_zbits_t
+dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+                 dns_rpz_zbits_t zbits, dns_name_t *trig_name)
+{
+       char namebuf[DNS_NAME_FORMATSIZE];
+       dns_rbtnode_t *nmnode;
+       const dns_rpz_nm_data_t *nm_data;
+       dns_rpz_zbits_t found_zbits;
+       isc_result_t result;
+
+       if (zbits == 0)
+               return (0);
+
+       found_zbits = 0;
+
+       LOCK(&rpzs->search_lock);
+
+       nmnode = NULL;
+       result = dns_rbt_findnode(rpzs->rbt, trig_name, NULL, &nmnode, NULL,
+                                 DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+       switch (result) {
+       case ISC_R_SUCCESS:
+               nm_data = nmnode->data;
+               if (nm_data != NULL) {
+                       if (rpz_type == DNS_RPZ_TYPE_QNAME)
+                               found_zbits = nm_data->pair.d;
+                       else
+                               found_zbits = nm_data->pair.ns;
+               }
+               nmnode = nmnode->parent;
+               /* fall thru */
+       case DNS_R_PARTIALMATCH:
+               while (nmnode != NULL) {
+                       nm_data = nmnode->data;
+                       if (nm_data != NULL) {
+                               if (rpz_type == DNS_RPZ_TYPE_QNAME)
+                                       found_zbits |= nm_data->wild.d;
+                               else
+                                       found_zbits |= nm_data->wild.ns;
+                       }
+                       nmnode = nmnode->parent;
+               }
+               break;
+
+       case ISC_R_NOTFOUND:
+               break;
+
+       default:
+               /*
+                * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+                */
+               dns_name_format(trig_name, namebuf, sizeof(namebuf));
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "dns_rpz_find_name(%s) failed: %s",
+                             namebuf, isc_result_totext(result));
+               break;
+       }
 
-       *prefix = found->bits;
-       return (ip2name(cidr, &found->ip, found->bits, type,
-                       canon_name, search_name));
+       UNLOCK(&rpzs->search_lock);
+       return (zbits & found_zbits);
 }
 
 /*
@@ -1161,10 +1994,10 @@ dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
        isc_result_t result;
 
        result = dns_rdataset_first(rdataset);
-       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       INSIST(result == ISC_R_SUCCESS);
        dns_rdataset_current(rdataset, &rdata);
        result = dns_rdata_tostruct(&rdata, &cname, NULL);
-       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       INSIST(result == ISC_R_SUCCESS);
        dns_rdata_reset(&rdata);
 
        /*
@@ -1191,7 +2024,7 @@ dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
        }
 
        /*
-        * CNAME PASSTHRU.origin means "do not rewrite.
+        * CNAME PASSTHRU. means "do not rewrite.
         */
        if (dns_name_equal(&cname.cname, &rpz->passthru))
                return (DNS_RPZ_POLICY_PASSTHRU);
index bfc7a8716824b30f8407cb52c3da345d0d15aced..d2d475405e14fb04abd1412c47c57e0d262d39c8 100644 (file)
@@ -1295,8 +1295,8 @@ static dns_dbmethods_t sdb_methods = {
        NULL,                   /* resigned */
        NULL,                   /* isdnssec */
        NULL,                   /* getrrsetstats */
-       NULL,                   /* rpz_enabled */
-       NULL,                   /* rpz_findips */
+       NULL,                   /* rpz_attach */
+       NULL,                   /* rpz_ready */
        findnodeext,
        findext,
        NULL,                   /* setcachestats */
index dff7cc5ae477f7d935ff6e0e9666aff7fa4831e1..555605c3199d63d975ab44cf67a7132fd43bb290 100644 (file)
@@ -1261,8 +1261,8 @@ static dns_dbmethods_t sdlzdb_methods = {
        NULL,                   /* resigned */
        NULL,                   /* isdnssec */
        NULL,                   /* getrrsetstats */
-       NULL,                   /* rpz_enabled */
-       NULL,                   /* rpz_findips */
+       NULL,                   /* rpz_attach */
+       NULL,                   /* rpz_ready */
        findnodeext,
        findext,
        NULL,                   /* setcachestats */
index 53e23b74a9dff12701620511064e413ba5a9129f..1eab6d69ca0898834688eb625f4d2a083e8873ef 100644 (file)
@@ -198,9 +198,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
        view->v4_aaaa = dns_aaaa_ok;
        view->v6_aaaa = dns_aaaa_ok;
        view->aaaa_acl = NULL;
-       ISC_LIST_INIT(view->rpz_zones);
-       view->rpz_recursive_only = ISC_TRUE;
-       view->rpz_break_dnssec = ISC_FALSE;
+       view->rpzs = NULL;
        dns_fixedname_init(&view->dlv_fixed);
        view->managed_keys = NULL;
        view->redirect = NULL;
@@ -337,9 +335,9 @@ destroy(dns_view_t *view) {
                        dns_acache_putdb(view->acache, view->cachedb);
                dns_acache_detach(&view->acache);
        }
-       dns_rpz_view_destroy(view);
        dns_rrl_view_destroy(view);
-
+       if (view->rpzs != NULL)
+               dns_rpz_detach_rpzs(&view->rpzs);
        for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
             dlzdb != NULL;
             dlzdb = ISC_LIST_HEAD(view->dlz_searched)) {
@@ -354,7 +352,7 @@ destroy(dns_view_t *view) {
        }
 #else
        INSIST(view->acache == NULL);
-       INSIST(ISC_LIST_EMPTY(view->rpz_zones));
+       INSIST(view->rpzs == NULL);
        INSIST(ISC_LIST_EMPTY(view->dlz_searched));
        INSIST(ISC_LIST_EMPTY(view->dlz_unsearched));
        INSIST(view->rrl == NULL);
index 4d5069fb5ed6127a4598ee167aa52a1557af9f9a..117c319b0836aed9ff54089ea742944a2fa85902 100644 (file)
@@ -131,8 +131,8 @@ dns_db_origin
 dns_db_overmem
 dns_db_printnode
 dns_db_register
-dns_db_rpz_enabled
-dns_db_rpz_findips
+dns_db_rpz_attach
+dns_db_rpz_ready
 dns_db_setcachestats
 dns_db_serialize
 dns_db_subtractrdataset
@@ -647,19 +647,22 @@ dns_result_register
 dns_result_torcode
 dns_result_totext
 dns_rootns_create
+dns_rpz_add
+dns_rpz_attach_rpzs
+dns_rpz_beginload
 dns_rpz_cidr_addip
-dns_rpz_cidr_deleteip
 dns_rpz_cidr_find
-dns_rpz_cidr_free
 dns_rpz_decode_cname
-dns_rpz_enabled
-dns_rpz_needed
-dns_rpz_new_cidr
+dns_rpz_delete
+dns_rpz_delete_node
+dns_rpz_detach_rpzs
+dns_rpz_find_ip
+dns_rpz_find_name
+dns_rpz_new_zones
 dns_rpz_policy2str
-dns_rpz_set_need
+dns_rpz_ready
 dns_rpz_str2policy
 dns_rpz_type2str
-dns_rpz_view_destroy
 dns_rriterator_current
 dns_rriterator_destroy
 dns_rriterator_first
@@ -820,6 +823,7 @@ dns_zone_flush
 dns_zone_forcereload
 dns_zone_forwardupdate
 dns_zone_fulldumptostream
+dns_zone_get_rpz_num
 dns_zone_getadded
 dns_zone_getchecknames
 dns_zone_getclass
@@ -847,6 +851,7 @@ dns_zone_getqueryacl
 dns_zone_getraw
 dns_zone_getqueryaoncl
 dns_zone_getrequeststats
+dns_zone_getrpz_num
 dns_zone_getserial
 dns_zone_getserial2
 dns_zone_getserialupdatemethod
@@ -883,6 +888,8 @@ dns_zone_nscheck
 dns_zone_refresh
 dns_zone_rekey
 dns_zone_replacedb
+dns_zone_rpz_attach
+dns_zone_rpz_enable
 dns_zone_setacache
 dns_zone_setadded
 dns_zone_setalsonotify
index 30590fc1f2a28dfa5fe551c0a2cef90114233bc3..3a23d69488daf88e90717e78ad15f0f2e41253aa 100644 (file)
@@ -349,9 +349,10 @@ struct dns_zone {
        isc_boolean_t           added;
 
        /*%
-        * whether a rpz radix was needed when last loaded
+        * response policy data to be relayed to the database
         */
-       isc_boolean_t           rpz_zone;
+       dns_rpz_zones_t         *rpzs;
+       dns_rpz_num_t           rpz_num;
 
        /*%
         * Serial number update method.
@@ -929,7 +930,8 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
        zone->nodes = 100;
        zone->privatetype = (dns_rdatatype_t)0xffffU;
        zone->added = ISC_FALSE;
-       zone->rpz_zone = ISC_FALSE;
+       zone->rpzs = NULL;
+       zone->rpz_num = DNS_RPZ_INVALID_NUM;
        ISC_LIST_INIT(zone->forwards);
        zone->raw = NULL;
        zone->secure = NULL;
@@ -1041,6 +1043,13 @@ zone_free(dns_zone_t *zone) {
                zone_detachdb(zone);
        if (zone->acache != NULL)
                dns_acache_detach(&zone->acache);
+#ifdef BIND9
+       if (zone->rpzs != NULL) {
+               REQUIRE(zone->rpz_num < zone->rpzs->p.num_zones);
+               dns_rpz_detach_rpzs(&zone->rpzs);
+               zone->rpz_num = DNS_RPZ_INVALID_NUM;
+       }
+#endif
        zone_freedbargs(zone);
        RUNTIME_CHECK(dns_zone_setmasterswithkeys(zone, NULL, NULL, 0)
                      == ISC_R_SUCCESS);
@@ -1529,6 +1538,45 @@ dns_zone_isdynamic(dns_zone_t *zone, isc_boolean_t ignore_freeze) {
 
 }
 
+/*
+ * Set the response policy index and information for a zone.
+ */
+isc_result_t
+dns_zone_rpz_enable(dns_zone_t *zone, dns_rpz_zones_t *rpzs,
+                   dns_rpz_num_t rpz_num)
+{
+       /*
+        * Only RBTDB zones can be used for response policy zones,
+        * because only they have the code to load the create the summary data.
+        * Only zones that are loaded instead of mmap()ed create the
+        * summary data and so can be policy zones.
+        */
+       if (strcmp(zone->db_argv[0], "rbt") != 0 &&
+           strcmp(zone->db_argv[0], "rbt64") != 0)
+               return (ISC_R_NOTIMPLEMENTED);
+
+       /*
+        * This must happen only once or be redundant.
+        */
+       LOCK_ZONE(zone);
+       if (zone->rpzs != NULL) {
+               REQUIRE(zone->rpzs == rpzs && zone->rpz_num == rpz_num);
+       } else {
+               REQUIRE(zone->rpz_num == DNS_RPZ_INVALID_NUM);
+               dns_rpz_attach_rpzs(rpzs, &zone->rpzs);
+               zone->rpz_num = rpz_num;
+       }
+       rpzs->defined |= DNS_RPZ_ZBIT(rpz_num);
+       UNLOCK_ZONE(zone);
+
+       return (ISC_R_SUCCESS);
+}
+
+dns_rpz_num_t
+dns_zone_get_rpz_num(dns_zone_t *zone) {
+       return (zone->rpz_num);
+}
+
 static isc_result_t
 zone_load(dns_zone_t *zone, unsigned int flags) {
        isc_result_t result;
@@ -1606,8 +1654,7 @@ zone_load(dns_zone_t *zone, unsigned int flags) {
                 * "rndc reconfig", we are done.
                 */
                if (!isc_time_isepoch(&zone->loadtime) &&
-                   (flags & DNS_ZONELOADFLAG_NOSTAT) != 0 &&
-                   zone->rpz_zone == dns_rpz_needed()) {
+                   (flags & DNS_ZONELOADFLAG_NOSTAT) != 0) {
                        result = ISC_R_SUCCESS;
                        goto cleanup;
                }
@@ -1616,8 +1663,7 @@ zone_load(dns_zone_t *zone, unsigned int flags) {
                if (result == ISC_R_SUCCESS) {
                        if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
                            !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE) &&
-                           isc_time_compare(&filetime, &zone->loadtime) <= 0 &&
-                           zone->rpz_zone == dns_rpz_needed()) {
+                           isc_time_compare(&filetime, &zone->loadtime) <= 0) {
                                dns_zone_log(zone, ISC_LOG_DEBUG(1),
                                             "skipping load: master file "
                                             "older than last load");
@@ -1625,7 +1671,6 @@ zone_load(dns_zone_t *zone, unsigned int flags) {
                                goto cleanup;
                        }
                        loadtime = filetime;
-                       zone->rpz_zone = dns_rpz_needed();
                }
        }
 
@@ -2067,8 +2112,14 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) {
        isc_result_t tresult;
        unsigned int options;
 
-       options = get_master_options(zone);
+#ifdef BIND9
+       if (zone->rpz_num != DNS_RPZ_INVALID_NUM) {
+               REQUIRE(zone->rpzs != NULL);
+               dns_db_rpz_attach(db, zone->rpzs, zone->rpz_num);
+       }
+#endif
 
+       options = get_master_options(zone);
        if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MANYERRORS))
                options |= DNS_MASTER_MANYERRORS;
 
@@ -4120,6 +4171,11 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
                if (result != ISC_R_SUCCESS)
                        goto cleanup;
        } else {
+#ifdef BIND9
+               result = dns_db_rpz_ready(db);
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
+#endif
                zone_attachdb(zone, db);
                ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
                DNS_ZONE_SETFLAG(zone,
@@ -13019,6 +13075,12 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) {
        REQUIRE(DNS_ZONE_VALID(zone));
        REQUIRE(LOCKED_ZONE(zone));
 
+#ifdef BIND9
+       result = dns_db_rpz_ready(db);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+#endif
+
        result = zone_get_from_db(zone, db, &nscount, &soacount,
                                  NULL, NULL, NULL, NULL, NULL, NULL);
        if (result == ISC_R_SUCCESS) {
index 649ca1286b7f2b1a4c35a4006890d4f69823c570..5611809039e9f40748206fe208db6fb768f34ee9 100644 (file)
@@ -1045,10 +1045,10 @@ static cfg_type_t cfg_type_masterformat = {
  *  response-policy {
  *     zone <string> [ policy (given|disabled|passthru|
  *                                     nxdomain|nodata|cname <domain> ) ]
- *                   [ recursive-only yes|no ]
- *                   [ max-policy-ttl number ] ;
- *  } [ recursive-only yes|no ] [ break-dnssec yes|no ]
- *     [ max-policy-ttl number ] ;
+ *                   [ recursive-only yes|no ] [ max-policy-ttl number ] ;
+ *  } [ recursive-only yes|no ] [ max-policy-ttl number ] ;
+ *      [ break-dnssec yes|no ] [ min-ns-dots number ]
+ *      [ qname-wait-recurse yes|no ]
  */
 
 static void
@@ -1250,6 +1250,8 @@ static cfg_tuplefielddef_t rpz_fields[] = {
        { "recursive-only", &cfg_type_boolean, 0 },
        { "break-dnssec", &cfg_type_boolean, 0 },
        { "max-policy-ttl", &cfg_type_uint32, 0 },
+       { "min-ns-dots", &cfg_type_uint32, 0 },
+       { "qname-wait-recurse", &cfg_type_boolean, 0 },
        { NULL, NULL, 0 }
 };
 static cfg_type_t cfg_type_rpz = {