]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] rpz work
authorEvan Hunt <each@isc.org>
Fri, 12 Jul 2013 21:46:47 +0000 (14:46 -0700)
committerEvan Hunt <each@isc.org>
Fri, 12 Jul 2013 21:46:47 +0000 (14:46 -0700)
3620. [func] Added "rpz-client-ip" policy triggers, enabling
RPZ responses to be configured on the basis of
the client IP address; this can be used, for
example, to blacklist misbehaving recursive
or stub resolvers. [RT #33605]

3619. [bug] Fixed a bug in RPZ with "recursive-only no;"
[RT #33776]

17 files changed:
CHANGES
bin/named/query.c
bin/named/server.c
bin/tests/system/rpz/ns1/root.db
bin/tests/system/rpz/ns3/base.db
bin/tests/system/rpz/ns3/named.conf
bin/tests/system/rpz/ns5/named.args
bin/tests/system/rpz/setup.sh
bin/tests/system/rpz/test1
bin/tests/system/rpz/test2
bin/tests/system/rpz/test5
bin/tests/system/rpz/test6 [new file with mode: 0644]
bin/tests/system/rpz/tests.sh
doc/arm/Bv9ARM-book.xml
lib/dns/include/dns/rpz.h
lib/dns/rpz.c
lib/isccfg/namedconf.c

diff --git a/CHANGES b/CHANGES
index 508038d575668ee71b65d94a049064ae82b0149e..d43c071a59503a374b7dae2294bea3c1c3b4fad2 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,12 @@
+3620.  [func]          Added "rpz-client-ip" policy triggers, enabling
+                       RPZ responses to be configured on the basis of
+                       the client IP address; this can be used, for
+                       example, to blacklist misbehaving recursive
+                       or stub resolvers. [RT #33605]
+
+3619.  [bug]           Fixed a bug in RPZ with "recursive-only no;"
+                       [RT #33776]
+
 3618.  [func]          "rndc reload" now checks modification times of
                        include files as well as master files to determine
                        whether to skip reloading a zone. [RT #33936]
index e31743f458f08b47c72a54d978f9a967b44949ff..3ad589d1674ae2b69158e623c6e64506fcc5fdde 100644 (file)
@@ -917,20 +917,26 @@ rpz_log_fail(ns_client_t *client, int level, dns_name_t *p_name,
 {
        char qnamebuf[DNS_NAME_FORMATSIZE];
        char p_namebuf[DNS_NAME_FORMATSIZE];
+       const char *failed;
 
        if (!isc_log_wouldlog(ns_g_lctx, level))
                return;
 
        /*
-        * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+        * bin/tests/system/rpz/tests.sh looks for "rpz.*failed" for problems.
         */
+       if (level <= DNS_RPZ_DEBUG_LEVEL1)
+               failed = "failed: ";
+       else
+               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",
+                     "rpz %s rewrite %s via %s%s%s%s",
                      dns_rpz_type2str(rpz_type),
-                     qnamebuf, p_namebuf, str, isc_result_totext(result));
+                     qnamebuf, p_namebuf,
+                     str, failed, isc_result_totext(result));
 }
 
 /*
@@ -962,7 +968,7 @@ rpz_getdb(ns_client_t *client, dns_name_t *p_name, dns_rpz_type_t rpz_type,
                return (ISC_R_SUCCESS);
        }
        rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
-                    "query_getzonedb() ", result);
+                    " query_getzonedb()", result);
        return (result);
 }
 
@@ -4011,6 +4017,9 @@ rpz_get_zbits(ns_client_t *client,
        rpzs = client->view->rpzs;
 
        switch (rpz_type) {
+       case DNS_RPZ_TYPE_CLIENT_IP:
+               zbits = rpzs->have.client_ip;
+               break;
        case DNS_RPZ_TYPE_QNAME:
                zbits = rpzs->have.qname;
                break;
@@ -4102,7 +4111,7 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
                result = st->r.r_result;
                if (result == DNS_R_DELEGATION) {
                        rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
-                                    rpz_type, "rpz_rrset_find(1) ", result);
+                                    rpz_type, " rpz_rrset_find(1)", result);
                        st->m.policy = DNS_RPZ_POLICY_ERROR;
                        result = DNS_R_SERVFAIL;
                }
@@ -4125,7 +4134,7 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
                                     &version, &is_zone);
                if (result != ISC_R_SUCCESS) {
                        rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
-                                    rpz_type, "rpz_rrset_find(2) ", result);
+                                    rpz_type, " rpz_rrset_find(2)", result);
                        st->m.policy = DNS_RPZ_POLICY_ERROR;
                        if (zone != NULL)
                                dns_zone_detach(&zone);
@@ -4198,6 +4207,9 @@ rpz_get_p_name(ns_client_t *client, dns_name_t *p_name,
         * from being too long.
         */
        switch (rpz_type) {
+       case DNS_RPZ_TYPE_CLIENT_IP:
+               suffix = &rpz->client_ip;
+               break;
        case DNS_RPZ_TYPE_QNAME:
                suffix = &rpz->origin;
                break;
@@ -4233,7 +4245,7 @@ rpz_get_p_name(ns_client_t *client, dns_name_t *p_name,
                 */
                if (labels-first < 2) {
                        rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, suffix,
-                                    rpz_type, "concatentate() ", result);
+                                    rpz_type, " concatentate()", result);
                        return (ISC_R_FAILURE);
                }
                /*
@@ -4241,7 +4253,7 @@ rpz_get_p_name(ns_client_t *client, dns_name_t *p_name,
                 */
                if (first == 0) {
                        rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, suffix,
-                                    rpz_type, "concatentate() ", result);
+                                    rpz_type, " concatentate()", result);
                }
                ++first;
        }
@@ -4249,7 +4261,7 @@ rpz_get_p_name(ns_client_t *client, dns_name_t *p_name,
 
 /*
  * 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 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.
@@ -4303,7 +4315,7 @@ rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
                                             &rdsiter);
                if (result != ISC_R_SUCCESS) {
                        rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name,
-                                    rpz_type, "allrdatasets() ", result);
+                                    rpz_type, " allrdatasets()", result);
                        return (DNS_R_SERVFAIL);
                }
                for (result = dns_rdatasetiter_first(rdsiter);
@@ -4320,7 +4332,7 @@ rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
                        if (result != ISC_R_NOMORE) {
                                rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
                                             p_name, rpz_type,
-                                            "rdatasetiter ", result);
+                                            " rdatasetiter", result);
                                return (DNS_R_SERVFAIL);
                        }
                        /*
@@ -4505,7 +4517,7 @@ rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
                         *
                         * With more than one applicable policy, prefer
                         * the earliest configured policy,
-                        * QNAME over IP over NSDNAME over NSIP,
+                        * client-IP over 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.
@@ -4523,7 +4535,9 @@ rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
                                break;
 
                        /*
-                        * Stop checking after saving an enabled hit.
+                        * Stop checking after saving an enabled hit in this
+                        * policy zone.  The radix tree in the policy zone
+                        * ensures that we found the longest match.
                         */
                        if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
                                rpz_save_p(st, rpz, rpz_type,
@@ -4593,13 +4607,13 @@ rpz_rewrite_ip_rrset(ns_client_t *client,
        case DNS_R_CNAME:
        case DNS_R_DNAME:
                rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, name, rpz_type,
-                            "NS address rewrite rrset ", result);
+                            " NS address rewrite rrset", result);
                return (ISC_R_SUCCESS);
        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 ",
+                                    rpz_type, " NS address rewrite rrset",
                                     result);
                }
                return (DNS_R_SERVFAIL);
@@ -4802,7 +4816,7 @@ rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
                        /*
                         * With more than one applicable policy, prefer
                         * the earliest configured policy,
-                        * QNAME over IP over NSDNAME over NSIP,
+                        * client-IP over QNAME over IP over NSDNAME over NSIP,
                         * and the smallest name.
                         * We known st->m.rpz->num >= rpz->num  and either
                         * st->m.rpz->num > rpz->num or st->m.type >= rpz_type
@@ -4947,69 +4961,111 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                break;
        case DNS_R_DELEGATION:
        case ISC_R_NOTFOUND:
-               qresult_type = 2;
+               /*
+                * If recursion is on, do only tentative rewriting.
+                * If recursion is off, this the normal and only time we
+                * can rewrite.
+                */
+               if (RECURSIONOK(client))
+                       qresult_type = 2;
+               else
+                       qresult_type = 1;
                break;
        case ISC_R_FAILURE:
        case ISC_R_TIMEDOUT:
        case DNS_R_BROKENCHAIN:
                rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, client->query.qname,
                             DNS_RPZ_TYPE_QNAME,
-                            "stop on qresult in rpz_rewrite() ", qresult);
+                            " stop on qresult in rpz_rewrite()", qresult);
                return (ISC_R_SUCCESS);
        default:
                rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, client->query.qname,
                             DNS_RPZ_TYPE_QNAME,
-                            "stop on unrecognized qresult in rpz_rewrite() ",
+                            " stop on unrecognized qresult in rpz_rewrite()",
                             qresult);
                return (ISC_R_SUCCESS);
        }
 
        rdataset = NULL;
-       if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
-               /*
-                * 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.
-                */
+
+       if ((st->state & (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) !=
+           (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) {
+               isc_netaddr_t netaddr;
+               dns_rpz_zbits_t allowed;
+
                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.
+                        * Get bits for the policy zones that do not need
+                        * to wait for the results of recursion.
                         */
-                       zbits = client->view->rpzs->have.qname_skip_recurse;
-                       if (zbits == 0)
+                       allowed = client->view->rpzs->have.qname_skip_recurse;
+                       if (allowed == 0)
                                return (ISC_R_SUCCESS);
                } else {
-                       zbits = DNS_RPZ_ALL_ZBITS;
+                       allowed = 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.
+                * Check once for triggers for the client IP address.
+                */
+               if ((st->state & DNS_RPZ_DONE_CLIENT_IP) == 0) {
+                       zbits = rpz_get_zbits(client, dns_rdatatype_none,
+                                             DNS_RPZ_TYPE_CLIENT_IP);
+                       zbits &= allowed;
+                       if (zbits != 0) {
+                               isc_netaddr_fromsockaddr(&netaddr,
+                                                       &client->peeraddr);
+                               result = rpz_rewrite_ip(client, &netaddr, qtype,
+                                                       DNS_RPZ_TYPE_CLIENT_IP,
+                                                       zbits, &rdataset);
+                               if (result != ISC_R_SUCCESS)
+                                       goto cleanup;
+                       }
+               }
+
+               /*
+                * 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
+                */
+               if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
+                       result = rpz_rewrite_name(client, client->query.qname,
+                                                 qtype, DNS_RPZ_TYPE_QNAME,
+                                                 allowed, &rdataset);
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+
+                       /*
+                        * Check IPv4 addresses in A RRs next.
+                        * Reset to the start of the NS names.
+                        */
+                       st->r.label = dns_name_countlabels(client->query.qname);
+                       st->state &= ~(DNS_RPZ_DONE_QNAME_IP |
+                                      DNS_RPZ_DONE_IPv4);
+
+               }
+
+               /*
+                * Quit if this was an attempt to find a qname or
+                * client-IP trigger before recursion.
+                * We will be back if no pre-recursion triggers hit.
+                * For example, consider 2 policy zones, both with qname and
+                * IP address triggers.  If the qname misses the 1st zone,
+                * then we cannot know whether a hit for the qname in the
+                * 2nd zone matters until after recursing to get the A RRs and
+                * testing them in the first zone.
+                * Do not bother saving the work from this attempt,
+                * because recusion is so slow.
                 */
                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.
+                * DNS_RPZ_DONE_QNAME but not DNS_RPZ_DONE_CLIENT_IP
+                * is reset at the end of dealing with each CNAME.
                 */
-               st->state &= ~(DNS_RPZ_DONE_QNAME_IP | DNS_RPZ_DONE_IPv4);
-               st->state |= DNS_RPZ_DONE_QNAME;
+               st->state |= (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME);
        }
 
        /*
@@ -5022,8 +5078,8 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
        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,
+                                              client->query.qname, qtype,
+                                              DNS_RPZ_TYPE_IP,
                                               &rdataset, resuming);
                if (result != ISC_R_SUCCESS)
                        goto cleanup;
@@ -5098,12 +5154,12 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                        case ISC_R_FAILURE:
                                rpz_rewrite_ns_skip(client, nsname, result,
                                                DNS_RPZ_DEBUG_LEVEL3,
-                                               "NS db_find() ");
+                                               " NS db_find()");
                                continue;
                        default:
                                rpz_rewrite_ns_skip(client, nsname, result,
                                                DNS_RPZ_INFO_LEVEL,
-                                               "unrecognized NS db_find() ");
+                                               " unrecognized NS db_find()");
                                continue;
                        }
                }
@@ -5120,7 +5176,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
                        if (result != ISC_R_SUCCESS) {
                                rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
                                             nsname, DNS_RPZ_TYPE_NSIP,
-                                            "rdata_tostruct() ", result);
+                                            " rdata_tostruct()", result);
                                st->m.policy = DNS_RPZ_POLICY_ERROR;
                                goto cleanup;
                        }
@@ -6413,12 +6469,17 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        rpz_st->state |= DNS_RPZ_REWRITTEN;
                if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS &&
                    rpz_st->m.policy != DNS_RPZ_POLICY_PASSTHRU &&
+                   (rpz_st->m.policy != DNS_RPZ_POLICY_TCP_ONLY ||
+                    (client->attributes & NS_CLIENTATTR_TCP) == 0) &&
                    rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
-                       if (rpz_st->m.type == DNS_RPZ_TYPE_QNAME) {
-                               result = dns_name_copy(client->query.qname,
-                                                      fname, NULL);
-                               RUNTIME_CHECK(result == ISC_R_SUCCESS);
-                       }
+                       /* We got a hit and are going to answer with our
+                        * fiction. Ensure that we answer with the name
+                        * we looked up even if we were stopped short
+                        * in recursion or for a deferral.
+                        */
+                       rresult = dns_name_copy(client->query.qname,
+                                               fname, NULL);
+                       RUNTIME_CHECK(rresult == ISC_R_SUCCESS);
                        rpz_clean(&zone, &db, &node, NULL);
                        if (rpz_st->m.rdataset != NULL) {
                                query_putrdataset(client, &rdataset);
@@ -6438,6 +6499,23 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        rpz_st->m.zone = NULL;
 
                        switch (rpz_st->m.policy) {
+                       case DNS_RPZ_POLICY_TCP_ONLY:
+                               client->message->flags |= DNS_MESSAGEFLAG_TC;
+                               if (result == DNS_R_NXDOMAIN ||
+                                   result == DNS_R_NCACHENXDOMAIN)
+                                       client->message->rcode =
+                                                   dns_rcode_nxdomain;
+                               else
+                                       result = ISC_R_SUCCESS;
+                               rpz_log_rewrite(client, ISC_FALSE,
+                                               rpz_st->m.policy,
+                                               rpz_st->m.type, zone,
+                                               rpz_st->p_name);
+                               goto cleanup;
+                       case DNS_RPZ_POLICY_DROP:
+                               result = ISC_R_SUCCESS;
+                               QUERY_ERROR(DNS_R_DROP);
+                               break;
                        case DNS_RPZ_POLICY_NXDOMAIN:
                                result = DNS_R_NXDOMAIN;
                                break;
@@ -6450,8 +6528,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                    result != DNS_R_CNAME) {
                                        /*
                                         * We will add all of the rdatasets of
-                                        * the node by iterating, setting the
-                                        * TTL then.
+                                        * the node by iterating later,
+                                        * and set the TTL then.
                                         */
                                        if (dns_rdataset_isassociated(rdataset))
                                            dns_rdataset_disassociate(rdataset);
index 7e02fa5703a2cd0cd5f1bc52fe782d79822a107b..5aac59b2988492fae334678de9e1f2f88e39b828 100644 (file)
@@ -1640,10 +1640,13 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
                return (result);
        }
        dns_name_init(&new->origin, NULL);
+       dns_name_init(&new->client_ip, NULL);
        dns_name_init(&new->ip, NULL);
        dns_name_init(&new->nsdname, NULL);
        dns_name_init(&new->nsip, NULL);
        dns_name_init(&new->passthru, NULL);
+       dns_name_init(&new->drop, NULL);
+       dns_name_init(&new->tcp_only, NULL);
        dns_name_init(&new->cname, NULL);
        new->num = view->rpzs->p.num_zones++;
        view->rpzs->zones[new->num] = new;
@@ -1685,6 +1688,11 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
        if (*old_rpz_okp && !dns_name_equal(&old->origin, &new->origin))
                *old_rpz_okp = ISC_FALSE;
 
+       result = configure_rpz_name2(view, rpz_obj, &new->client_ip,
+                                    DNS_RPZ_CLIENT_IP_ZONE, &new->origin);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
        result = configure_rpz_name2(view, rpz_obj, &new->ip,
                                     DNS_RPZ_IP_ZONE, &new->origin);
        if (result != ISC_R_SUCCESS)
@@ -1701,7 +1709,17 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
                return (result);
 
        result = configure_rpz_name(view, rpz_obj, &new->passthru,
-                                   DNS_RPZ_PASSTHRU_ZONE, "zone");
+                                   DNS_RPZ_PASSTHRU_NAME, "name");
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       result = configure_rpz_name(view, rpz_obj, &new->drop,
+                                   DNS_RPZ_DROP_NAME, "name");
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       result = configure_rpz_name(view, rpz_obj, &new->tcp_only,
+                                   DNS_RPZ_TCP_ONLY_NAME, "name");
        if (result != ISC_R_SUCCESS)
                return (result);
 
index 32c14dba23915724325cdb97198affaa6cc04227..24daa505ca6160f1c5fdcd848ebb77152c6e1aec 100644 (file)
@@ -38,3 +38,6 @@ ns.tld4.      A       10.53.0.4
 ; performance test
 tld5.          NS      ns.tld5.
 ns.tld5.       A       10.53.0.5
+
+; generate SERVFAIL
+servfail       NS      ns.tld2.
index 475257a6cf39fca21ebdec13017e84baeffaea41..5b0d261b05267680d414c2681fb9e6054ae54f8e 100644 (file)
 ;   Its contents are also changed with nsupdate
 
 
-$TTL   120
+$TTL   300
 @      SOA     blx.  hostmaster.ns.blx. ( 1 3600 1200 604800 60 )
-       NS      ns
-ns     A       10.53.0.3
+       NS      ns.tld3.
 
-; Poke the radix tree a little.
-128.1111.2222.3333.4444.5555.6666.7777.8888.rpz-ip     CNAME   .
-128.1111.2222.3333.4444.5555.6666.zz.rpz-ip            CNAME   .
-128.1111.2222.3333.4444.5555.zz.8888.rpz-ip            CNAME   .
-128.1111.2222.3333.4444.zz.8888.rpz-ip                 CNAME   .
-128.zz.3333.4444.0.0.8888.rpz-ip                       CNAME   .
-128.zz.3333.4444.0.7777.8888.rpz-ip                    CNAME   .
-128.zz.3333.4444.0.8777.8888.rpz-ip                    CNAME   .
-127.zz.3333.4444.0.8777.8888.rpz-ip                    CNAME   .
-
-
-; regression testing for some old crashes
-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 b491354566f7a12774dccfff90ac7a4c75da5019..3d05297b97cb760c3ced04b2190e934e073ddc18 100644 (file)
@@ -46,8 +46,11 @@ 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
+           zone "bl-drop"      policy drop;
+           zone "bl-tcp-only"  policy tcp-only;
+       }
+       min-ns-dots 0
+       qname-wait-recurse yes
        ;
 };
 
@@ -84,6 +87,10 @@ zone "bl-wildcname." {type master; file "bl-wildcname.db";
                                allow-update {any;};};
 zone "bl-garden."      {type master; file "bl-garden.db";
                                allow-update {any;};};
+zone "bl-drop."                {type master; file "bl-drop.db";
+                               allow-update {any;};};
+zone "bl-tcp-only."    {type master; file "bl-tcp-only.db";
+                               allow-update {any;};};
 
 zone "crash1.tld2"     {type master; file "crash1";};
 zone "crash2.tld3."    {type master; file "crash2";};
index acf35aef08e45d1d7fce0aef572c852d5d736d1d..882d88c589fc433c14c47271437a04cf3a08075c 100644 (file)
@@ -1,3 +1,3 @@
 # run the performace test close to real life
 
--c named.conf -g
+-c named.conf -gd3
index 19d391735e1c016add8339ef4a5d43a7b6bd0b0e..58ec57e9a3fc3e13cb79dd77ccec714f4dbd2de1 100644 (file)
@@ -26,11 +26,13 @@ QPERF=`sh qperf.sh`
 
 sh clean.sh
 
-# set up test policy zones.  bl-2 is used to check competing zones.
-#      bl-{given,disabled,passthru,no-data,nxdomain,cname,wildcard,garden}
-#      are used to check policy overrides in named.conf.
-#      NO-OP is an obsolete synonym for PASSHTRU
-for NM in '' -2 -given -disabled -passthru -no-op -nodata -nxdomain -cname -wildcname -garden; do
+# set up test policy zones.
+#   bl is the main test zone
+#   bl-2 is used to check competing zones.
+#   bl-{given,disabled,passthru,no-data,nxdomain,cname,wildcard,garden,
+#          drop,tcp-only} are used to check policy overrides in named.conf.
+#   NO-OP is an obsolete synonym for PASSHTRU
+for NM in '' -2 -given -disabled -passthru -no-op -nodata -nxdomain -cname -wildcname -garden -drop -tcp-only; do
     sed -e "/SOA/s/blx/bl$NM/g" ns3/base.db >ns3/bl$NM.db
 done
 
@@ -48,7 +50,7 @@ signzone () {
 signzone ns2 tld2s. base-tld2s.db tld2s.db
 
 
-# Performance checks.
+# Performance and a few other checks.
 cat <<EOF >ns5/rpz-switch
 response-policy {
        zone "bl0"; zone "bl1"; zone "bl2"; zone "bl3"; zone "bl4";
@@ -56,13 +58,14 @@ response-policy {
        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;
+    max-policy-ttl 90
+    break-dnssec yes
+    qname-wait-recurse no
+    ;
 EOF
 
 cat <<EOF >ns5/example.db
-\$TTL  120
+\$TTL  300
 @      SOA     .  hostmaster.ns.example.tld5. ( 1 3600 1200 604800 60 )
        NS      ns
        NS      ns1
@@ -71,15 +74,16 @@ ns1 A       10.53.0.5
 EOF
 
 cat <<EOF >ns5/bl.db
-\$TTL  120
+\$TTL  300
 @              SOA     .  hostmaster.ns.blperf. ( 1 3600 1200 604800 60 )
-               NS      ns
-ns             A       10.53.0.5
+               NS      ns.tld5.
 
-; used only in failure for "recursive-only no" in #8 test5
-a3-5.tld2      CNAME   *.
+; for "qname-wait-recurse no" in #35 test1
+x.servfail     A       35.35.35.35
+; for "recursive-only no" in #8 test5
+a3-5.tld2      CNAME   .
 ; for "break-dnssec" in #9 & #10 test5
-a3-5.tld2s     CNAME   *.
+a3-5.tld2s     CNAME   .
 ; for "max-policy-ttl 90" in #17 test5
 a3-17.tld2     500 A   17.17.17.17
 
@@ -88,8 +92,7 @@ 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
+    # Do not build the full zones if we will not use them.
     $PERL -e 'for ($val = 1; $val <= 65535; ++$val) {
        printf("host-%05d\tA    192.168.%d.%d\n", $val, $val/256, $val%256);
        }' >>ns5/example.db
index 68dd59fa4b9773d1979c244d72402ebfb1eb5fbe..a4d33046533d8a81e552ad20ad5580310acab3be 100644 (file)
@@ -92,4 +92,10 @@ update       add c1.crash2.tld3.bl.  300 CNAME .
 ; DO=1 without signatures, DO=0 with signatures are rewritten
 ;      26 - 27
 update add  a0-1.tld2s.bl.     300 CNAME .
+;      32
+update add  a3-8.tld2.bl.      300 CNAME rpz-drop.
+;      33
+update add  a3-9.tld2.bl.      300 CNAME rpz-tcp-only.
+;      34 qname-wait-recurse yes
+update add  x.servfail.bl.     300 A   127.0.0.34
 send
index 68425b1a4225592d8ee6e7ab04b8a4d4349d66b3..11c7308f0cfc36d99511ffc0a26641e2986e6fba 100644 (file)
@@ -72,3 +72,8 @@ update add 32.16.1.16.172.rpz-ip.bl   300 CNAME .
 send
 update add c2.crash2.tld3.bl-2         300 A   127.0.0.16
 send
+
+; client-IP address trigger
+;      17
+update add 32.1.0.53.10.rpz-client-ip.bl 300 A 127.0.0.17
+send
index 42dbac8ba422597f597ce062b393ddd0111c0da8..959f547f2e5acc8b39887b5784af94e5d03c562f 100644 (file)
@@ -35,10 +35,8 @@ send
 ;      4
 update add  a3-4.tld2.bl-disabled.  300 A 127.0.0.4
 send
-;      5 - 8
+;      5 - 7
 update add  a3-5.tld2.bl-nodata.    300 A 127.0.0.5
-;      9 - 10
-update add  a3-5.tld2s.bl-nodata.   300 A 127.0.0.9
 send
 ;      11
 update add  a3-6.tld2.bl-nxdomain.  300 A 127.0.0.11
@@ -57,3 +55,9 @@ send
 ;      16
 update add  a3-16.tld2.bl.         300 A 127.0.0.16
 send
+;      18
+update add  a3-18.tld2.bl-drop.            300 A 127.0.0.18
+send
+;      19
+update add  a3-19.tld2.bl-tcp-only. 300 A 127.0.0.19
+send
diff --git a/bin/tests/system/rpz/test6 b/bin/tests/system/rpz/test6
new file mode 100644 (file)
index 0000000..8a7e112
--- /dev/null
@@ -0,0 +1,40 @@
+; 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'
+
+server 10.53.0.3 5300
+
+; Poke the radix tree a little.
+update add  128.1111.2222.3333.4444.5555.6666.7777.8888.rpz-ip.bl. 300 CNAME .
+update add  128.1111.2222.3333.4444.5555.6666.zz.rpz-ip.bl.    300 CNAME   .
+update add  128.1111.2222.3333.4444.5555.zz.8888.rpz-ip.bl.    300 CNAME   .
+update add  128.1111.2222.3333.4444.zz.8888.rpz-ip.bl.         300 CNAME   .
+update add  128.zz.3333.4444.0.0.8888.rpz-ip.bl.               300 CNAME   .
+update add  128.zz.3333.4444.0.7777.8888.rpz-ip.bl.            300 CNAME   .
+update add  128.zz.3333.4444.0.8777.8888.rpz-ip.bl.            300 CNAME   .
+update add  127.zz.3333.4444.0.8777.8888.rpz-ip.bl.            300 CNAME   .
+;
+;
+; regression testing for some old crashes
+update add  redirect.bl.       300 A           127.0.0.1
+update add  *.redirect.bl.     300 A           127.0.0.1
+update add  *.credirect.bl.    300 CNAME       google.com.
+;
+send
index 7671bb2f96180919a3b49fd2e143ed5bea079d9a..cf8390c94a3bc874e97e24bfdc19be2b96688e46 100644 (file)
@@ -22,15 +22,14 @@ SYSTEMTESTTOP=..
 
 ns=10.53.0
 ns1=$ns.1              # root, defining the others
-ns2=$ns.2              # authoritative server whose answers are rewritten
-ns3=$ns.3              # resolver that does the rewriting
+ns2=$ns.2              # authoritative server whose records are rewritten
+ns3=$ns.3              # main rewriting resolver
 ns4=$ns.4              # another authoritative server that is rewritten
-ns5=$ns.5              # resolver to check performance and give un-rewritten
-                       #       responses for comparisons
+ns5=$ns.5              # another rewriting resolver
 
 HAVE_CORE=
 SAVE_RESULTS=
-NS3_STATS=47
+
 
 USAGE="$0: [-x]"
 while getopts "x" c; do
@@ -58,11 +57,16 @@ comment () {
 RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
 
 digcmd () {
-    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 || \
-       digcmd_args="+noauth $digcmd_args"
+    if test "$1" = TCP; then
+       shift
+    fi
+    # Default to +noauth and @$ns3
+    # Also default to -bX where X is the @value so that OS X will choose
+    #      the right IP source address.
+    digcmd_args=`echo "+noadd +time=1 +tries=1 -p 5300 $*" |   \
+           sed -e "/@/!s/.*/& @$ns3/"                          \
+               -e '/-b/!s/@\([^ ]*\)/@\1 -b\1/'                \
+               -e '/+n?o?auth/!s/.*/+noauth &/'`
     #echo I:dig $digcmd_args 1>&2
     $DIG $digcmd_args
 }
@@ -88,10 +92,13 @@ setret () {
 # (re)load the reponse policy zones with the rules in the file $TEST_FILE
 load_db () {
     if test -n "$TEST_FILE"; then
-       $NSUPDATE -v $TEST_FILE || {
+       if $NSUPDATE -v $TEST_FILE; then :
+           $RNDCCMD $ns3 sync
+       else
            echo "I:failed to update policy zone with $TEST_FILE"
+           $RNDCCMD $ns3 sync
            exit 1
-       }
+       fi
     fi
 }
 
@@ -134,15 +141,20 @@ 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"
+    HOST=$1
+    LABEL="$2"
+    NSDIR="$3"
+    EXPECTED="$4"
+    $RNDCCMD $HOST stats
+    NEW_CNT=0`sed -n -e 's/[    ]*\([0-9]*\).response policy.*/\1/p'  \
+                   $NSDIR/named.stats | tail -1`
+    eval "OLD_CNT=0\$${NSDIR}_CNT"
+    GOT=`expr $NEW_CNT - $OLD_CNT`
+    if test "$GOT" -ne "$EXPECTED"; then
+       setret "I:wrong $LABEL $NSDIR statistics of $GOT instead of $EXPECTED"
     fi
+    eval "${NSDIR}_CNT=$NEW_CNT"
 }
 
 # $1=message  $2=optional test file name
@@ -179,6 +191,12 @@ clean_result () {
 ckresult () {
     #ckalive "$1" "I:server crashed by 'dig $1'" || return 1
     if $PERL $SYSTEMTESTTOP/digcomp.pl $DIGNM $2 >/dev/null; then
+       NEED_TCP=`echo "$1" | sed -n -e 's/[Tt][Cc][Pp].*/TCP/p'`
+       RESULT_TCP=`sed -n -e 's/.*Truncated, retrying in TCP.*/TCP/p' $DIGNM`
+       if test "$NEED_TCP" != "$RESULT_TCP"; then
+           setret "I:'dig $1' wrong; no or unexpected truncation in $DIGNM"
+           return 1
+       fi
        clean_result ${DIGNM}*
        return 0
     fi
@@ -235,12 +253,14 @@ addr () {
     clean_result ${DIGNM}*
 }
 
-# check that a response is not rewritten
-# $1=target domain  $2=optional query type
+# Check that a response is not rewritten
+#   Use $ns1 instead of the authority for most test domains, $ns2 to prevent
+#   spurious differences for `dig +norecurse`
+# $1=optional "TCP"  remaining args for dig
 nochange () {
     make_dignm
     digcmd $* >$DIGNM
-    digcmd $* @$ns5 >${DIGNM}_OK
+    digcmd $* @$ns1 >${DIGNM}_OK
     ckresult "$*" ${DIGNM}_OK && clean_result ${DIGNM}_OK
 }
 
@@ -252,6 +272,20 @@ here () {
     ckresult "$*" ${DIGNM}_OK
 }
 
+# check dropped response
+DROPPED='^;; connection timed out; no servers could be reached'
+drop () {
+    make_dignm
+    digcmd $* >$DIGNM
+    if grep "$DROPPED" $DIGNM >/dev/null; then
+       clean_result ${DIGNM}*
+       return 0
+    fi
+    setret "I:'dig $1' wrong; response in $DIGNM"
+    return 1
+}
+
+
 # make prototype files to check against rewritten results
 digcmd nonexistent @$ns2 >proto.nxdomain
 digcmd txt-only.tld2 @$ns2 >proto.nodata
@@ -291,7 +325,15 @@ 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            +nodnssec   # 31
+drop a3-8.tld2                         # 32 drop
+nochange tcp a3-9.tld2                 # 33 tcp-only
+here x.servfail <<'EOF'                        # 34 qname-wait-recurse yes
+    ;; status: SERVFAIL, x
+EOF
+addr 35.35.35.35 "x.servfail @$ns5"    # 35 qname-wait-recurse no
 end_group
+ckstats $ns3 test1 ns3 22
+ckstats $ns5 test1 ns5 1
 
 start_group "IP rewrites" test2
 nodata a3-1.tld2                       # 1 NODATA
@@ -310,7 +352,9 @@ 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
+addr 127.0.0.17 "a4-4.tld2 -b $ns1"    # 17 client-IP address trigger
 end_group
+ckstats $ns3 test2 ns3 10
 
 # check that IP addresses for previous group were deleted from the radix tree
 start_group "radix tree deletions"
@@ -326,6 +370,7 @@ nochange a3-1.tld2 -tAAAA
 nochange a4-1-aaaa.tld2 -tAAAA
 nochange a5-1-2.tld2
 end_group
+ckstats $ns3 'radix tree deletions' ns3 0
 
 if ./rpz nsdname; then
     # these tests assume "min-ns-dots 0"
@@ -343,7 +388,7 @@ 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`
+    ckstats $ns3 test3 ns3 7
 else
     echo "I:NSDNAME not checked; named configured with --disable-rpz-nsdname"
 fi
@@ -365,7 +410,7 @@ if ./rpz nsip; then
     a3-1.tld2.     x   IN      TXT   "NSIP walled garden"
 EOF
     end_group
-    NS3_STATS=`expr $NS3_STATS + 4`
+    ckstats $ns3 test4 ns3 4
 else
     echo "I:NSIP not checked; named configured with --disable-rpz-nsip"
 fi
@@ -377,12 +422,12 @@ addr 127.0.0.1 a3-1.tld2          # 1 bl-given
 nochange a3-2.tld2                     # 2 bl-passthru
 nochange a3-3.tld2                     # 3 bl-no-op    obsolete for passthru
 nochange a3-4.tld2                     # 4 bl-disabled
-nodata a3-5.tld2                       # 5 bl-nodata
-nodata a3-5.tld2    +norecurse         # 6 bl-nodata       recursive-only no
-nodata a3-5.tld2                       # 7 bl-nodata
-nodata a3-5.tld2    +norecurse @$ns5   # 8 bl-nodata       recursive-only no
-nodata a3-5.tld2s              @$ns5   # 9 bl-nodata
-nodata a3-5.tld2s   +dnssec    @$ns5   # 10 bl-nodata      break-dnssec
+nodata a3-5.tld2                       # 5 bl-nodata   zone recursive-only no
+nodata a3-5.tld2    +norecurse         # 6 bl-nodata   zone recursive-only no
+nodata a3-5.tld2                       # 7 bl-nodata           not needed
+nxdomain a3-5.tld2  +norecurse @$ns5   # 8 bl-nodata   global recursive-only no
+nxdomain a3-5.tld2s            @$ns5   # 9 bl-nodata   global break-dnssec
+nxdomain a3-5.tld2s +dnssec    @$ns5   # 10 bl-nodata  global break-dnssec
 nxdomain a3-6.tld2                     # 11 bl-nxdomain
 here a3-7.tld2 -tany <<'EOF'
     ;; status: NOERROR, x
@@ -394,10 +439,15 @@ addr 59.59.59.59 a3-9.sub9.tld2           # 14 bl_wildcname
 addr 12.12.12.12 a3-15.tld2            # 15 bl-garden  via CNAME to a12.tld2
 addr 127.0.0.16 a3-16.tld2         100 # 16 bl             max-policy-ttl 100
 addr 17.17.17.17 "a3-17.tld2 @$ns5" 90 # 17 ns5 bl         max-policy-ttl 90
+drop a3-18.tld2                                # 18 bl-drop
+nxdomain TCP a3-19.tld2                        # 19 bl-tcp-only
 end_group
+ckstats $ns3 test5 ns3 12
+ckstats $ns5 test5 ns5 4
+
 
 # check that miscellaneous bugs are still absent
-start_group "crashes"
+start_group "crashes" test6
 for Q in RRSIG SIG ANY 'ANY +dnssec'; do
     nocrash a3-1.tld2 -t$Q
     nocrash a3-2.tld2 -t$Q
@@ -411,6 +461,8 @@ done
 # resolving foo.
 # nxdomain 32.3.2.1.127.rpz-ip
 end_group
+ckstats $ns3 bugs ns3 8
+
 
 
 # superficial test for major performance bugs
@@ -423,6 +475,7 @@ if test -n "$QPERF"; then
        $QPERF -c -1 -l30 -d ns5/requests -s $ns5 -p 5300 >/dev/null
        comment "before real test $1"
        PFILE="ns5/$2.perf"
+       $RNDCCMD $ns5 notrace
        $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 \
@@ -437,17 +490,17 @@ if test -n "$QPERF"; then
     }
 
     # get qps with rpz
-    perf 'with rpz' rpz 'NOERROR:2900 NXDOMAIN:100 '
+    perf 'with RPZ' rpz 'NOERROR:2900 NXDOMAIN:100 '
     RPZ=`trim rpz`
 
     # turn off rpz and measure qps again
-    echo "# rpz off" >ns5/rpz-switch
+    echo "# RPZ off" >ns5/rpz-switch
     RNDCCMD_OUT=`$RNDCCMD $ns5 reload`
-    perf 'without rpz' norpz 'NOERROR:3000 '
+    perf 'without RPZ' norpz 'NOERROR:3000 '
     NORPZ=`trim norpz`
 
     PERCENT=`expr \( "$RPZ" \* 100 + \( $NORPZ / 2 \) \) / $NORPZ`
-    echo "I:$RPZ qps with rpz is $PERCENT% of $NORPZ qps without rpz"
+    echo "I:$RPZ qps with RPZ is $PERCENT% of $NORPZ qps without RPZ"
 
     MIN_PERCENT=30
     if test "$PERCENT" -lt $MIN_PERCENT; then
@@ -458,15 +511,13 @@ if test -n "$QPERF"; then
        setret "I:$RPZ qps with RPZ or $PERCENT% of $NORPZ qps without RPZ is too high"
     fi
 
-    ckstats $ns5 ns5 203
+    ckstats $ns5 performance ns5 200
 
 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
index 106da528b8dec76b9a6751361ae135dccceb0f6b..0c29021802b8d79d2753070f9ef6d6fac32cb175 100644 (file)
@@ -4950,7 +4950,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
        <optional> min-table-size <replaceable>number</replaceable> ; </optional>
       } ; </optional>
     <optional> response-policy { <replaceable>zone_name</replaceable>
-       <optional> policy given | disabled | passthru | nxdomain | nodata | cname <replaceable>domain</replaceable> </optional>
+       <optional> policy given | disabled | passthru | drop | 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> min-ns-dots <replaceable>number</replaceable> </optional>
@@ -9288,70 +9288,115 @@ deny-answer-aliases { "example.net"; };
          </para>
 
          <para>
-           Four policy triggers are encoded in RPZ records, QNAME, IP, NSIP,
-           and NSDNAME.
-           QNAME RPZ records triggered by query names of requests and targets
-           of CNAME records resolved to generate the response.
-           The owner name of a QNAME RPZ record is the query name relativized
-           to the RPZ.
-         </para>
+           Five policy triggers can be encoded in RPZ records.
+           <variablelist>
+             <varlistentry>
+               <term><command>RPZ-CLIENT-IP</command></term>
+               <listitem>
+                 <para>
+                   IP records are triggered by the IP address of the
+                   DNS client.
+                   Client IP address triggers are encoded in records that have
+                   owner names that are subdomains of
+                   <command>rpz-client-ip</command> relativized to the
+                   policy zone origin name
+                   and encode an address or address block.
+                   IPv4 addresses are represented as
+                   <userinput>prefixlength.B4.B3.B2.B1.rpz-ip</userinput>.
+                   The IPv4 prefix length must be between 1 and 32.
+                   All four bytes, B4, B3, B2, and B1, must be present.
+                   B4 is the decimal value of the least significant byte of the
+                   IPv4 address as in IN-ADDR.ARPA.
+                 </para>
 
-         <para>
-           The second kind of RPZ trigger is an IP address in an A and AAAA
-           record in the ANSWER section of a response.
-           IP address triggers are encoded in records that have owner names
-           that are subdomains of <userinput>rpz-ip</userinput> relativized
-           to the RPZ origin name and encode an IP address or address block.
-           IPv4 trigger addresses are represented as
-           <userinput>prefixlength.B4.B3.B2.B1.rpz-ip</userinput>.
-           The prefix length must be between 1 and 32.
-           All four bytes, B4, B3, B2, and B1, must be present.
-           B4 is the decimal value of the least significant byte of the
-           IPv4 address as in IN-ADDR.ARPA.
-           IPv6 addresses are encoded in a format similar to the standard
-           IPv6 text representation,
-           <userinput>prefixlength.W8.W7.W6.W5.W4.W3.W2.W1.rpz-ip</userinput>.
-           Each of W8,...,W1 is a one to four digit hexadecimal number
-           representing 16 bits of the IPv6 address as in the standard text
-           representation of IPv6 addresses, but reversed as in IN-ADDR.ARPA.
-           All 8 words must be present except when consecutive
-           zero words are replaced with <userinput>.zz.</userinput>
-           analogous to double colons (::) in standard IPv6 text encodings.
-           The prefix length must be between 1 and 128.
-         </para>
+                 <para>
+                   IPv6 addresses are encoded in a format similar
+                   to the standard IPv6 text representation,
+                   <userinput>prefixlength.W8.W7.W6.W5.W4.W3.W2.W1.rpz-ip</userinput>.
+                   Each of W8,...,W1 is a one to four digit hexadecimal number
+                   representing 16 bits of the IPv6 address as in the standard 
+                   text representation of IPv6 addresses,
+                   but reversed as in IN-ADDR.ARPA.
+                   All 8 words must be present except when one set of consecutive
+                   zero words is replaced with <userinput>.zz.</userinput>
+                   analogous to double colons (::) in standard IPv6 text
+                   encodings.
+                   The IPv6 prefix length must be between 64 and 128.
+                 </para>
+               </listitem>
+             </varlistentry>
 
-         <para>
-           NSDNAME triggers match names of authoritative servers
-           for the query name, a parent of the query name, a CNAME for
-           query name, or a parent of a CNAME.
-           They are encoded as subdomains of
-           <userinput>rpz-nsdomain</userinput> relativized
-           to the RPZ origin name.
-           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>
+             <varlistentry>
+               <term><command>QNAME</command></term>
+               <listitem>
+                 <para>
+                   QNAME policy records are triggered by query names of
+                   requests and targets of CNAME records resolved to generate
+                   the response.
+                   The owner name of a QNAME policy record is
+                   the query name relativized to the policy zone.
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>RPZ-IP</command></term>
+               <listitem>
+                 <para>
+                   IP triggers are IP addresses in an
+                   A or AAAA record in the ANSWER section of a response.
+                   They are encoded like client-IP triggers except as
+                   subdomains of <command>rpz-ip</command>.
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>RPZ-NSDNAME</command></term>
+               <listitem>
+                 <para>
+                   NSDNAME triggers match names of authoritative servers
+                   for the query name, a parent of the query name, a CNAME for
+                   query name, or a parent of a CNAME.
+                   They are encoded as subdomains of
+                   <command>rpz-nsdname</command> relativized
+                   to the RPZ origin name.
+                   NSIP triggers match IP addresses in A and
+                   AAAA RRsets for domains that can be checked against NSDNAME
+                   policy records.
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>RPZ-NSIP</command></term>
+               <listitem>
+                 <para>
+                   NSIP triggers are encoded like IP triggers except as
+                   subdomains of <command>rpz-nsip</command>.
+                   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>
+               </listitem>
+             </varlistentry>
+           </variablelist>
 
          <para>
-           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
+           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 are rewritten according to at most one
            policy record, a single record encoding an action (other than
            <command>DISABLED</command> actions) must be chosen.
-           Triggers or the records that encode them are chosen in
-           the following order:
+           Triggers or the records that encode them are chosen for the
+           rewriting in the following order:
            <itemizedlist>
              <listitem>Choose the triggered record in the zone that appears
-               first in the response-policy option.
+               first in the <command>response-policy</command> option.
              </listitem>
-             <listitem>Prefer QNAME to IP to NSDNAME to NSIP triggers
-               in a single zone.
+             <listitem>Prefer CLIENT-IP to QNAME to IP to NSDNAME to NSIP
+               triggers in a single zone.
              </listitem>
              <listitem>Among NSDNAME triggers, prefer the
                trigger that matches the smallest name under the DNSSEC ordering.
@@ -9375,73 +9420,156 @@ deny-answer-aliases { "example.net"; };
          </para>
 
          <para>
-           RPZ record sets are sets of any types of DNS record except
-           DNAME or DNSSEC that encode actions or responses to queries.
-           <itemizedlist>
-             <listitem>The <command>NXDOMAIN</command> response is encoded
-               by a CNAME whose target is the root domain (.)
-             </listitem>
-             <listitem>A CNAME whose target is the wildcard top-level
-               domain (*.) specifies the <command>NODATA</command> action,
-               which rewrites the response to NODATA or ANCOUNT=1.
-             </listitem>
-             <listitem>The <command>Local Data</command> action is
-               represented by a set ordinary DNS records that are used
-               to answer queries.  Queries for record types not the
-               set are answered with NODATA.
-
-               A special form of local data is a CNAME whose target is a
-               wildcard such as *.example.com.
-               It is used as if were an ordinary CNAME after the astrisk (*)
-               has been replaced with the query name.
-               The purpose for this special form is query logging in the
-               walled garden's authority DNS server.
-             </listitem>
-             <listitem>The <command>PASSTHRU</command> policy is specified
-               by a CNAME whose target is <command>rpz-passthru.</command>
-               It causes the response to not be rewritten
-               and is most often used to "poke holes" in policies for
-               CIDR blocks.
-               (A CNAME whose target is the variable part of its owner name
-               is an obsolete specification of the PASSTHRU policy.)
-             </listitem>
-           </itemizedlist>
+           RPZ record sets are any types of DNS record except
+           DNAME or DNSSEC that encode actions or responses to
+           individual queries.
+           Any of the policies can be used with any of the triggers.
+           For example, while the <command>TCP-only</command> policy is
+           commonly used with <command>client-IP</command> triggers,
+           it cn be used with any type of trigger to force the use of
+           TCP for responses with owner names in a zone.
+           <variablelist>
+             <varlistentry>
+               <term><command>PASSTHRU</command></term>
+               <listitem>
+                 <para>
+                   The whitelist policy is specified
+                   by a CNAME whose target is <command>rpz-passthru</command>.
+                   It causes the response to not be rewritten
+                   and is most often used to "poke holes" in policies for
+                   CIDR blocks.
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>DROP</command></term>
+               <listitem>
+                 <para>
+                   The blacklist policy is specified
+                   by a CNAME whose target is <command>rpz-drop</command>.
+                   It causes the response to be discarded.
+                   Nothing is sent to the DNS client.
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>TCP-Only</command></term>
+               <listitem>
+                 <para>
+                   The "slip" policy is specified
+                   by a CNAME whose target is <command>rpz-tcp-only</command>.
+                   It changes UDP responses to short, truncated DNS responses
+                   that require the DNS client to try again with TCP.
+                   It is used to mitigate distributed DNS reflection attacks.
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>NXDOMAIN</command></term>
+               <listitem>
+                 <para>
+                   The domain undefined response is encoded
+                   by a CNAME whose target is the root domain (.)
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>NODATA</command></term>
+               <listitem>
+                 <para>
+                   The empty set of resource records is specified by
+                   CNAME whose target is the wildcard top-level
+                   domain (*.).
+                   It rewrites the response to NODATA or ANCOUNT=1.
+                 </listitem>
+               </para>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>Local Data</command></term>
+               <listitem>
+                 <para>
+                   A set of ordinary DNS records can be used to answer queries.
+                   Queries for record types not the set are answered with
+                   NODATA.
+                 </para>
+
+                 <para>
+                   A special form of local data is a CNAME whose target is a
+                   wildcard such as *.example.com.
+                   It is used as if were an ordinary CNAME after the astrisk (*)
+                   has been replaced with the query name.
+                   The purpose for this special form is query logging in the
+                   walled garden's authority DNS server.
+                 </para>
+               </listitem>
+             </varlistentry>
+           </variablelist>
          </para>
 
          <para>
-           The actions specified in a policy zone can be overridden with a
-           <command>policy</command> clause in the
+           All of the actions specified in all of the individual records
+           in a policy zone
+           can be overridden with a <command>policy</command> clause in the
            <command>response-policy</command> option.
            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."
-             </listitem>
-             <listitem><command>DISABLED</command> causes policy records to do
-               nothing but log what they might have done.
-               The response to the DNS query will be written according to
-               any triggered policy records that are not disabled.
-               Disabled policy zones should appear first,
-               because they will often not be logged
-               if a higher precedence trigger is found first.
-             </listitem>
-             <listitem><command>PASSTHRU</command> causes all policy records
-               to act as if they were CNAME records with targets the variable
-               part of their owner name.  They protect the response from
-               being changed.
-             </listitem>
-             <listitem><command>NXDOMAIN</command> causes all RPZ records
-               to specify NXDOMAIN policies.
-             </listitem>
-             <listitem><command>NODATA</command> overrides with the
-               NODATA policy
-             </listitem>
-             <listitem><command>CNAME domain</command> causes all RPZ
-               policy records to act as if they were "cname domain" records.
-             </listitem>
-           </itemizedlist>
+           <variablelist>
+             <varlistentry>
+               <term><command>GIVEN</command></term>
+               <listitem>
+                 <para>The placeholder policy says "do not override but
+                   perform the action specified in the zone."
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>DISABLED</command></term>
+               <listitem>
+                 <para>
+                   The testing override policy causes policy zone records to do
+                   nothing but log what they would have done if the
+                   policy zone were not disabled.
+                   The response to the DNS query will be written (or not)
+                   according to any triggered policy records that are not
+                   disabled.
+                   Disabled policy zones should appear first,
+                   because they will often not be logged
+                   if a higher precedence trigger is found first.
+                 </para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
+               <term><command>PASSTHRU</command></term>,
+               <term><command>DROP</command></term>,
+               <term><command>TCP-Only</command></term>,
+               <term><command>NXDOMAIN</command></term>,
+               and
+               <term><command>NODATA</command></term>
+               <listitem>
+                 <para>
+                   override with the corresponding per-record policy.
+                 </listitem>
+               </para>
+             </varlistentry>
+
+             <varlistentry>
+                 <term><command>CNAME domain</command></term>
+                 <listitem>
+                   <para>
+                     causes all RPZ policy records to act as if they were
+                     "cname domain" records.
+                   </para>
+                 </listitem>
+               </varlistentry>
+           </variablelist>
          </para>
 
          <para>
@@ -9469,25 +9597,31 @@ deny-answer-aliases { "example.net"; };
          </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
+            No DNS records are needed for a QNAME or Client-IP trigger.
+           The name or IP address itself is sufficient,
+           so in principle the query name need not be recursively resolved.
+           However, not resolving the requested
+            name can leak 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
+            significant time.
+           The <command>qname-wait-recurse no</command> option
+            overrides that default behavior when recursion cannot
+            change a non-error response.
+           The option does not affect QNAME or client-IP 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.
+           The option can cause appear to rewrite error responses
+           such as SERVFAIL when no recursion is done to discover problems
+           at the authoritative server.
          </para>
 
          <para>
@@ -9515,26 +9649,38 @@ deny-answer-aliases { "example.net"; };
 
 ; QNAME policy records.  There are no periods (.) after the owner names.
 nxdomain.domain.com     CNAME   .               ; NXDOMAIN policy
+*.nxdomain.domain.com   CNAME   .               ; NXDOMAIN policy
 nodata.domain.com       CNAME   *.              ; NODATA policy
+*.nodata.domain.com     CNAME   *.              ; NODATA policy
 bad.domain.com          A       10.0.0.1        ; redirect to a walled garden
                         AAAA    2001:2::1
+bzone.domain.com        CNAME   garden.example.com.
 
 ; do not rewrite (PASSTHRU) OK.DOMAIN.COM
 ok.domain.com           CNAME   rpz-passthru.
 
-bzone.domain.com        CNAME   garden.example.com.
-
 ; redirect x.bzone.domain.com to x.bzone.domain.com.garden.example.com
 *.bzone.domain.com      CNAME   *.garden.example.com.
 
 
-; IP policy records that rewrite all answers for 127/8 except 127.0.0.1
+; IP policy records that rewrite all responses containing A records in 127/8
+;       except 127.0.0.1
 8.0.0.0.127.rpz-ip      CNAME   .
 32.1.0.0.127.rpz-ip     CNAME   rpz-passthru.
 
 ; NSDNAME and NSIP policy records
 ns.domain.com.rpz-nsdname   CNAME   .
 48.zz.2.2001.rpz-nsip       CNAME   .
+
+; blacklist and whitelist some DNS clients
+112.zz.2001.rpz-client-ip    CNAME   rpz-drop.
+8.0.0.0.127.rpz-client-ip    CNAME   rpz-drop.
+
+; force some DNS clients and responses in the example.com zone to TCP
+16.0.0.1.10.rpz-client-ip   CNAME   rpz-tcp-only.
+example.com                 CNAME   rpz-tcp-only.
+*.example.com               CNAME   rpz-tcp-only.
+
 </programlisting>
           <para>
             RPZ can affect server performance.
index 10c9dc572dc6d0e791b2afe08b296d5374d291ef..28bffaa7fa352b7a1aea233fe897353938237bb5 100644 (file)
 ISC_LANG_BEGINDECLS
 
 #define DNS_RPZ_PREFIX         "rpz-"
+/*
+ * Sub-zones of various trigger types.
+ */
+#define DNS_RPZ_CLIENT_IP_ZONE DNS_RPZ_PREFIX"client-ip"
 #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"
+/*
+ * Special policies.
+ */
+#define DNS_RPZ_PASSTHRU_NAME  DNS_RPZ_PREFIX"passthru"
+#define DNS_RPZ_DROP_NAME      DNS_RPZ_PREFIX"drop"
+#define DNS_RPZ_TCP_ONLY_NAME  DNS_RPZ_PREFIX"tcp-only"
+
 
 typedef isc_uint8_t            dns_rpz_prefix_t;
 
 typedef enum {
        DNS_RPZ_TYPE_BAD,
+       DNS_RPZ_TYPE_CLIENT_IP,
        DNS_RPZ_TYPE_QNAME,
        DNS_RPZ_TYPE_IP,
        DNS_RPZ_TYPE_NSDNAME,
@@ -46,17 +57,19 @@ typedef enum {
 } dns_rpz_type_t;
 
 /*
- * Require DNS_RPZ_POLICY_PASSTHRU < DNS_RPZ_POLICY_NXDOMAIN <
- * DNS_RPZ_POLICY_NODATA < DNS_RPZ_POLICY_CNAME to choose among competing
- * policies.
+ * Require DNS_RPZ_POLICY_PASSTHRU < DNS_RPZ_POLICY_DROP
+ * < DNS_RPZ_POLICY_TCP_ONLY DNS_RPZ_POLICY_NXDOMAIN < DNS_RPZ_POLICY_NODATA
+ * < DNS_RPZ_POLICY_CNAME to choose among competing policies.
  */
 typedef enum {
        DNS_RPZ_POLICY_GIVEN = 0,       /* 'given': what policy record says */
        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 */
-       DNS_RPZ_POLICY_CNAME = 5,       /* 'cname x': answer with x's rrsets */
+       DNS_RPZ_POLICY_DROP = 3,        /* 'drop': do not respond */
+       DNS_RPZ_POLICY_TCP_ONLY = 4,    /* 'tcp-only': answer UDP with TC=1 */
+       DNS_RPZ_POLICY_NXDOMAIN = 5,    /* 'nxdomain': answer with NXDOMAIN */
+       DNS_RPZ_POLICY_NODATA = 6,      /* 'nodata': answer with ANCOUNT=0 */
+       DNS_RPZ_POLICY_CNAME = 7,       /* 'cname x': answer with x's rrsets */
        DNS_RPZ_POLICY_RECORD,
        DNS_RPZ_POLICY_WILDCNAME,
        DNS_RPZ_POLICY_MISS,
@@ -89,10 +102,12 @@ typedef isc_uint32_t           dns_rpz_zbits_t;
                                                0 : (1<<((n)+1))) -1))
 
 /*
- * A single response policy zone.
+ * The number of triggers of each type in a response policy zone.
  */
 typedef struct dns_rpz_triggers dns_rpz_triggers_t;
 struct dns_rpz_triggers {
+       int             client_ipv4;
+       int             client_ipv6;
        int             qname;
        int             ipv4;
        int             ipv6;
@@ -100,19 +115,24 @@ struct dns_rpz_triggers {
        int             nsipv4;
        int             nsipv6;
 };
+/*
+ * A single response policy zone.
+ */
 typedef struct dns_rpz_zone dns_rpz_zone_t;
 struct dns_rpz_zone {
        isc_refcount_t  refs;
        dns_rpz_num_t   num;            /* ordinal in list of policy zones */
        dns_name_t      origin;         /* Policy zone name */
+       dns_name_t      client_ip;      /* DNS_RPZ_CLIENT_IP_ZONE.origin. */
        dns_name_t      ip;             /* DNS_RPZ_IP_ZONE.origin. */
        dns_name_t      nsdname;        /* DNS_RPZ_NSDNAME_ZONE.origin */
        dns_name_t      nsip;           /* DNS_RPZ_NSIP_ZONE.origin. */
-       dns_name_t      passthru;       /* DNS_RPZ_PASSTHRU_ZONE. */
+       dns_name_t      passthru;       /* DNS_RPZ_PASSTHRU_NAME. */
+       dns_name_t      drop;           /* DNS_RPZ_DROP_NAME. */
+       dns_name_t      tcp_only;       /* DNS_RPZ_TCP_ONLY_NAME. */
        dns_name_t      cname;          /* override value for ..._CNAME */
        dns_ttl_t       max_policy_ttl;
        dns_rpz_policy_t policy;        /* DNS_RPZ_POLICY_GIVEN or override */
-       dns_rpz_triggers_t  triggers;
 };
 
 /*
@@ -133,6 +153,7 @@ struct dns_rpz_zones {
                dns_rpz_num_t       num_zones;
        } p;
        dns_rpz_zone_t          *zones[DNS_RPZ_MAX_ZONES];
+       dns_rpz_triggers_t      triggers[DNS_RPZ_MAX_ZONES];
 
        dns_rpz_zbits_t         defined;
 
@@ -151,6 +172,9 @@ struct dns_rpz_zones {
         */
        dns_rpz_zbits_t         load_begun;
        struct {
+               dns_rpz_zbits_t     client_ipv4;
+               dns_rpz_zbits_t     client_ipv6;
+               dns_rpz_zbits_t     client_ip;
                dns_rpz_zbits_t     qname;
                dns_rpz_zbits_t     ipv4;
                dns_rpz_zbits_t     ipv6;
@@ -161,6 +185,7 @@ struct dns_rpz_zones {
                dns_rpz_zbits_t     nsip;
                dns_rpz_zbits_t     qname_skip_recurse;
        } have;
+       dns_rpz_triggers_t      total_triggers;
 
        isc_mem_t               *mctx;
        isc_refcount_t          refs;
@@ -184,11 +209,12 @@ struct dns_rpz_zones {
 typedef struct {
        unsigned int            state;
 # define DNS_RPZ_REWRITTEN     0x0001
-# 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_RECURSING     0x0020
+# define DNS_RPZ_DONE_CLIENT_IP        0x0002  /* client IP address checked */
+# define DNS_RPZ_DONE_QNAME    0x0004  /* qname checked */
+# define DNS_RPZ_DONE_QNAME_IP 0x0008  /* IP addresses of qname checked */
+# define DNS_RPZ_DONE_NSDNAME  0x0010  /* NS name missed; checking addresses */
+# define DNS_RPZ_DONE_IPv4     0x0020
+# define DNS_RPZ_RECURSING     0x0040
        /*
         * Best match so far.
         */
index 9a5b33672436ed1b0b3af7f99b5fb91d335d2392..37157f68755d856c3753dd38975fe406249c2d7b 100644 (file)
@@ -111,13 +111,14 @@ typedef struct {
                                     - 1 - ((n) % 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.
+ * A triplet of arrays of bits flagging the existence of
+ * client-IP, IP, and NSIP 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_addr_zbits dns_rpz_addr_zbits_t;
+struct dns_rpz_addr_zbits {
+       dns_rpz_zbits_t         client_ip;
+       dns_rpz_zbits_t         ip;
+       dns_rpz_zbits_t         nsip;
 };
 
 /*
@@ -128,8 +129,8 @@ struct dns_rpz_cidr_node {
        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;
+       dns_rpz_addr_zbits_t    set;
+       dns_rpz_addr_zbits_t    sum;
 };
 
 /*
@@ -137,10 +138,20 @@ struct dns_rpz_cidr_node {
  * 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.
  */
+/*
+ * A pair of arrays of bits flagging the existence of
+ * QNAME and NSDNAME policy triggers.
+ */
+typedef struct dns_rpz_nm_zbits dns_rpz_nm_zbits_t;
+struct dns_rpz_nm_zbits {
+       dns_rpz_zbits_t         qname;
+       dns_rpz_zbits_t         ns;
+};
+
 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;
+       dns_rpz_nm_zbits_t      set;
+       dns_rpz_nm_zbits_t      wild;
 };
 
 #if 0
@@ -166,6 +177,8 @@ catch_name(const dns_name_t *src_name, const char *tgt, const char *str) {
 const char *
 dns_rpz_type2str(dns_rpz_type_t type) {
        switch (type) {
+       case DNS_RPZ_TYPE_CLIENT_IP:
+               return ("CLIENT-IP");
        case DNS_RPZ_TYPE_QNAME:
                return ("QNAME");
        case DNS_RPZ_TYPE_IP:
@@ -183,25 +196,28 @@ dns_rpz_type2str(dns_rpz_type_t type) {
 
 dns_rpz_policy_t
 dns_rpz_str2policy(const char *str) {
+       static struct {
+               const char *str;
+               dns_rpz_policy_t policy;
+       } tbl[] = {
+               {"given",       DNS_RPZ_POLICY_GIVEN},
+               {"disabled",    DNS_RPZ_POLICY_DISABLED},
+               {"passthru",    DNS_RPZ_POLICY_PASSTHRU},
+               {"drop",        DNS_RPZ_POLICY_DROP},
+               {"tcp-only",    DNS_RPZ_POLICY_TCP_ONLY},
+               {"nxdomain",    DNS_RPZ_POLICY_NXDOMAIN},
+               {"nodata",      DNS_RPZ_POLICY_NODATA},
+               {"cname",       DNS_RPZ_POLICY_CNAME},
+               {"no-op",       DNS_RPZ_POLICY_PASSTHRU},   /* old passthru */
+       };
+       unsigned int n;
+
        if (str == NULL)
                return (DNS_RPZ_POLICY_ERROR);
-       if (!strcasecmp(str, "given"))
-               return (DNS_RPZ_POLICY_GIVEN);
-       if (!strcasecmp(str, "disabled"))
-               return (DNS_RPZ_POLICY_DISABLED);
-       if (!strcasecmp(str, "passthru"))
-               return (DNS_RPZ_POLICY_PASSTHRU);
-       if (!strcasecmp(str, "nxdomain"))
-               return (DNS_RPZ_POLICY_NXDOMAIN);
-       if (!strcasecmp(str, "nodata"))
-               return (DNS_RPZ_POLICY_NODATA);
-       if (!strcasecmp(str, "cname"))
-               return (DNS_RPZ_POLICY_CNAME);
-       /*
-        * Obsolete
-        */
-       if (!strcasecmp(str, "no-op"))
-               return (DNS_RPZ_POLICY_PASSTHRU);
+       for (n = 0; n < sizeof(tbl)/sizeof(tbl[0]); ++n) {
+               if (!strcasecmp(tbl[n].str, str))
+                       return (tbl[n].policy);
+       }
        return (DNS_RPZ_POLICY_ERROR);
 }
 
@@ -213,6 +229,12 @@ dns_rpz_policy2str(dns_rpz_policy_t policy) {
        case DNS_RPZ_POLICY_PASSTHRU:
                str = "PASSTHRU";
                break;
+       case DNS_RPZ_POLICY_DROP:
+               str = "DROP";
+               break;
+       case DNS_RPZ_POLICY_TCP_ONLY:
+               str = "TCP-ONLY";
+               break;
        case DNS_RPZ_POLICY_NXDOMAIN:
                str = "NXDOMAIN";
                break;
@@ -267,20 +289,47 @@ zbit_to_num(dns_rpz_zbits_t zbit) {
        return (rpz_num);
 }
 
-static inline void
-make_pair(dns_rpz_pair_zbits_t *pair, dns_rpz_zbits_t zbits,
-         dns_rpz_type_t type)
+/*
+ * Make a set of bit masks given one or more bits and their type.
+ */
+static void
+make_addr_set(dns_rpz_addr_zbits_t *tgt_set, dns_rpz_zbits_t zbits,
+             dns_rpz_type_t type)
 {
        switch (type) {
-       case DNS_RPZ_TYPE_QNAME:
+       case DNS_RPZ_TYPE_CLIENT_IP:
+               tgt_set->client_ip = zbits;
+               tgt_set->ip = 0;
+               tgt_set->nsip = 0;
+               break;
        case DNS_RPZ_TYPE_IP:
-               pair->d = zbits;
-               pair->ns = 0;
+               tgt_set->client_ip = 0;
+               tgt_set->ip = zbits;
+               tgt_set->nsip = 0;
                break;
-       case DNS_RPZ_TYPE_NSDNAME:
        case DNS_RPZ_TYPE_NSIP:
-               pair->d = 0;
-               pair->ns = zbits;
+               tgt_set->client_ip = 0;
+               tgt_set->ip = 0;
+               tgt_set->nsip = zbits;
+               break;
+       default:
+               INSIST(0);
+               break;
+       }
+}
+
+static void
+make_nm_set(dns_rpz_nm_zbits_t *tgt_set,
+           dns_rpz_num_t rpz_num, dns_rpz_type_t type)
+{
+       switch (type) {
+       case DNS_RPZ_TYPE_QNAME:
+               tgt_set->qname = DNS_RPZ_ZBIT(rpz_num);
+               tgt_set->ns = 0;
+               break;
+       case DNS_RPZ_TYPE_NSDNAME:
+               tgt_set->qname = 0;
+               tgt_set->ns = DNS_RPZ_ZBIT(rpz_num);
                break;
        default:
                INSIST(0);
@@ -289,30 +338,33 @@ make_pair(dns_rpz_pair_zbits_t *pair, dns_rpz_zbits_t zbits,
 }
 
 /*
- * Mark a node and all of its parents as having IP or NSIP data
+ * Mark a node and all of its parents as having client-IP, IP, or NSIP data
  */
 static void
 set_sum_pair(dns_rpz_cidr_node_t *cnode) {
        dns_rpz_cidr_node_t *child;
-       dns_rpz_pair_zbits_t sum;
+       dns_rpz_addr_zbits_t sum;
 
        do {
-               sum = cnode->pair;
+               sum = cnode->set;
 
                child = cnode->child[0];
                if (child != NULL) {
-                       sum.d |= child->sum.d;
-                       sum.ns |= child->sum.ns;
+                       sum.client_ip |= child->sum.client_ip;
+                       sum.ip |= child->sum.ip;
+                       sum.nsip |= child->sum.nsip;
                }
 
                child = cnode->child[1];
                if (child != NULL) {
-                       sum.d |= child->sum.d;
-                       sum.ns |= child->sum.ns;
+                       sum.client_ip |= child->sum.client_ip;
+                       sum.ip |= child->sum.ip;
+                       sum.nsip |= child->sum.nsip;
                }
 
-               if (cnode->sum.d == sum.d &&
-                   cnode->sum.ns == sum.ns)
+               if (cnode->sum.client_ip == sum.client_ip &&
+                   cnode->sum.ip == sum.ip &&
+                   cnode->sum.nsip == sum.nsip)
                        break;
                cnode->sum = sum;
                cnode = cnode->parent;
@@ -342,6 +394,7 @@ fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
        }
        rpzs->have.qname_skip_recurse = zbits;
 
+       rpzs->have.client_ip = rpzs->have.client_ipv4 | rpzs->have.client_ipv6;
        rpzs->have.ip = rpzs->have.ipv4 | rpzs->have.ipv6;
        rpzs->have.nsip = rpzs->have.nsipv4 | rpzs->have.nsipv6;
 }
@@ -352,37 +405,45 @@ adj_trigger_cnt(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
                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;
 
-       rpz = rpzs->zones[rpz_num];
        switch (rpz_type) {
+       case DNS_RPZ_TYPE_CLIENT_IP:
+               REQUIRE(tgt_ip != NULL);
+               if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+                       cnt = &rpzs->triggers[rpz_num].client_ipv4;
+                       have = &rpzs->have.client_ipv4;
+               } else {
+                       cnt = &rpzs->triggers[rpz_num].client_ipv6;
+                       have = &rpzs->have.client_ipv6;
+               }
+               break;
        case DNS_RPZ_TYPE_QNAME:
-               cnt = &rpz->triggers.qname;
+               cnt = &rpzs->triggers[rpz_num].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;
+                       cnt = &rpzs->triggers[rpz_num].ipv4;
                        have = &rpzs->have.ipv4;
                } else {
-                       cnt = &rpz->triggers.ipv6;
+                       cnt = &rpzs->triggers[rpz_num].ipv6;
                        have = &rpzs->have.ipv6;
                }
                break;
        case DNS_RPZ_TYPE_NSDNAME:
-               cnt = &rpz->triggers.nsdname;
+               cnt = &rpzs->triggers[rpz_num].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;
+                       cnt = &rpzs->triggers[rpz_num].nsipv4;
                        have = &rpzs->have.nsipv4;
                } else {
-                       cnt = &rpz->triggers.nsipv6;
+                       cnt = &rpzs->triggers[rpz_num].nsipv6;
                        have = &rpzs->have.nsipv6;
                }
                break;
@@ -445,8 +506,8 @@ badname(int level, dns_name_t *name, const char *str1, const char *str2) {
        /*
         * bin/tests/system/rpz/tests.sh looks for "invalid rpz".
         */
-       if (level < DNS_RPZ_DEBUG_QUIET
-           && isc_log_wouldlog(dns_lctx, level)) {
+       if (level < DNS_RPZ_DEBUG_QUIET &&
+           isc_log_wouldlog(dns_lctx, level)) {
                dns_name_format(name, namebuf, sizeof(namebuf));
                isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
                              DNS_LOGMODULE_RBTDB, level,
@@ -495,9 +556,9 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
                        return (ISC_R_FAILURE);
                i = 0;
                while (i < DNS_RPZ_CIDR_WORDS * 2) {
-                       if (w[i] != 0 || zeros
-                           || i >= DNS_RPZ_CIDR_WORDS * 2 - 1
-                           || w[i+1] != 0) {
+                       if (w[i] != 0 || zeros ||
+                           i >= DNS_RPZ_CIDR_WORDS * 2 - 1 ||
+                           w[i+1] != 0) {
                                INSIST((size_t)len <= sizeof(str));
                                n = snprintf(&str[len], sizeof(str) - len,
                                             ".%x", w[i++]);
@@ -536,10 +597,9 @@ type_from_name(dns_rpz_zone_t *rpz, dns_name_t *name) {
        if (dns_name_issubdomain(name, &rpz->ip))
                return (DNS_RPZ_TYPE_IP);
 
-       /*
-        * Require `./configure --enable-rpz-nsip` and nsdname
-        * until consistency problems are resolved.
-        */
+       if (dns_name_issubdomain(name, &rpz->client_ip))
+               return (DNS_RPZ_TYPE_CLIENT_IP);
+
 #ifdef ENABLE_RPZ_NSIP
        if (dns_name_issubdomain(name, &rpz->nsip))
                return (DNS_RPZ_TYPE_NSIP);
@@ -563,7 +623,7 @@ 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)
+          dns_rpz_addr_zbits_t *new_set)
 {
        dns_rpz_zone_t *rpz;
        char ip_str[DNS_NAME_FORMATSIZE];
@@ -582,7 +642,7 @@ name2ipkey(int log_level,
        rpz = rpzs->zones[rpz_num];
        REQUIRE(rpz != NULL);
 
-       make_pair(new_pair, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+       make_addr_set(new_set, DNS_RPZ_ZBIT(rpz_num), rpz_type);
 
        ip_labels = dns_name_countlabels(src_name);
        if (rpz_type == DNS_RPZ_TYPE_QNAME)
@@ -728,7 +788,6 @@ 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;
@@ -745,12 +804,12 @@ name2data(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
         */
        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);
+               memset(&new_data->set, 0, sizeof(new_data->set));
+               make_nm_set(&new_data->wild, rpz_num, rpz_type);
        } else {
                prefix_len = 0;
-               make_pair(&new_data->pair, DNS_RPZ_ZBIT(rpz_num), rpz_type);
-               new_data->wild = zero;
+               make_nm_set(&new_data->set, rpz_num, rpz_type);
+               memset(&new_data->wild, 0, sizeof(new_data->wild));
        }
 
        dns_name_init(&tmp_name, tmp_name_offsets);
@@ -850,16 +909,16 @@ trim_zbits(dns_rpz_zbits_t zbits, dns_rpz_zbits_t found) {
 static isc_result_t
 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,
+       const dns_rpz_addr_zbits_t *tgt_set, 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;
+       dns_rpz_addr_zbits_t set;
        int cur_num, child_num;
        dns_rpz_prefix_t dbit;
        isc_result_t find_result;
 
-       pair = *tgt_pair;
+       set = *tgt_set;
        find_result = ISC_R_NOTFOUND;
        *found = NULL;
        cur = rpzs->cidr;
@@ -882,15 +941,17 @@ search(dns_rpz_zones_t *rpzs,
                        else
                                parent->child[cur_num] = child;
                        child->parent = parent;
-                       child->pair.d |= tgt_pair->d;
-                       child->pair.ns |= tgt_pair->ns;
+                       child->set.client_ip |= tgt_set->client_ip;
+                       child->set.ip |= tgt_set->ip;
+                       child->set.nsip |= tgt_set->nsip;
                        set_sum_pair(child);
                        *found = cur;
                        return (ISC_R_SUCCESS);
                }
 
-               if ((cur->sum.d & pair.d) == 0 &&
-                   (cur->sum.ns & pair.ns) == 0) {
+               if ((cur->sum.client_ip & set.client_ip) == 0 &&
+                   (cur->sum.ip & set.ip) == 0 &&
+                   (cur->sum.nsip & set.nsip) == 0) {
                        /*
                         * This node has no relevant data
                         * and is in none of the target trees.
@@ -913,8 +974,9 @@ search(dns_rpz_zones_t *rpzs,
                                /*
                                 * The node's key matches the target exactly.
                                 */
-                               if ((cur->pair.d & pair.d) != 0 ||
-                                   (cur->pair.ns & pair.ns) != 0) {
+                               if ((cur->set.client_ip & set.client_ip) != 0 ||
+                                   (cur->set.ip & set.ip) != 0 ||
+                                   (cur->set.nsip & set.nsip) != 0) {
                                        /*
                                         * It is the answer if it has data.
                                         */
@@ -929,8 +991,9 @@ search(dns_rpz_zones_t *rpzs,
                                         * The node lacked relevant data,
                                         * but will have it now.
                                         */
-                                       cur->pair.d |= tgt_pair->d;
-                                       cur->pair.ns |= tgt_pair->ns;
+                                       cur->set.client_ip |= tgt_set->client_ip;
+                                       cur->set.ip |= tgt_set->ip;
+                                       cur->set.nsip |= tgt_set->nsip;
                                        set_sum_pair(cur);
                                        *found = cur;
                                        find_result = ISC_R_SUCCESS;
@@ -957,15 +1020,16 @@ search(dns_rpz_zones_t *rpzs,
                        child_num = DNS_RPZ_IP_BIT(&cur->ip, tgt_prefix+1);
                        new_parent->child[child_num] = cur;
                        cur->parent = new_parent;
-                       new_parent->pair = *tgt_pair;
+                       new_parent->set = *tgt_set;
                        set_sum_pair(new_parent);
                        *found = new_parent;
                        return (ISC_R_SUCCESS);
                }
 
                if (dbit == cur->prefix) {
-                       if ((cur->pair.d & pair.d) != 0 ||
-                           (cur->pair.ns & pair.ns) != 0) {
+                       if ((cur->set.client_ip & set.client_ip) != 0 ||
+                           (cur->set.ip & set.ip) != 0 ||
+                           (cur->set.nsip & set.nsip) != 0) {
                                /*
                                 * We have a partial match between of all of the
                                 * current node but only part of the target.
@@ -974,8 +1038,12 @@ search(dns_rpz_zones_t *rpzs,
                                 */
                                find_result = DNS_R_PARTIALMATCH;
                                *found = cur;
-                               pair.d = trim_zbits(pair.d, cur->pair.d);
-                               pair.ns = trim_zbits(pair.ns, cur->pair.ns);
+                               set.client_ip = trim_zbits(set.ip,
+                                                       cur->set.client_ip);
+                               set.ip = trim_zbits(set.ip,
+                                                   cur->set.ip);
+                               set.nsip = trim_zbits(set.nsip,
+                                                     cur->set.nsip);
                        }
                        parent = cur;
                        cur_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
@@ -1011,7 +1079,7 @@ search(dns_rpz_zones_t *rpzs,
                new_parent->child[1-child_num] = cur;
                cur->parent = new_parent;
                sibling->parent = new_parent;
-               sibling->pair = *tgt_pair;
+               sibling->set = *tgt_set;
                set_sum_pair(sibling);
                *found = sibling;
                return (ISC_R_SUCCESS);
@@ -1027,19 +1095,19 @@ add_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
 {
        dns_rpz_cidr_key_t tgt_ip;
        dns_rpz_prefix_t tgt_prefix;
-       dns_rpz_pair_zbits_t pair;
+       dns_rpz_addr_zbits_t set;
        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);
+                           src_name, &tgt_ip, &tgt_prefix, &set);
        /*
         * Log complaints about bad owner names but let the zone load.
         */
        if (result != ISC_R_SUCCESS)
                return (ISC_R_SUCCESS);
 
-       result = search(rpzs, &tgt_ip, tgt_prefix, &pair, ISC_TRUE, &found);
+       result = search(rpzs, &tgt_ip, tgt_prefix, &set, ISC_TRUE, &found);
        if (result != ISC_R_SUCCESS) {
                char namebuf[DNS_NAME_FORMATSIZE];
 
@@ -1088,9 +1156,9 @@ add_nm(dns_rpz_zones_t *rpzs, dns_name_t *trig_name,
        /*
         * Do not count bits that are already present
         */
-       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 ||
+       if ((nm_data->set.qname & new_data->set.qname) != 0 ||
+           (nm_data->set.ns & new_data->set.ns) != 0 ||
+           (nm_data->wild.qname & new_data->wild.qname) != 0 ||
            (nm_data->wild.ns & new_data->wild.ns) != 0) {
                char namebuf[DNS_NAME_FORMATSIZE];
 
@@ -1104,9 +1172,9 @@ add_nm(dns_rpz_zones_t *rpzs, dns_name_t *trig_name,
                return (ISC_R_EXISTS);
        }
 
-       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->set.qname |= new_data->set.qname;
+       nm_data->set.ns |= new_data->set.ns;
+       nm_data->wild.qname |= new_data->wild.qname;
        nm_data->wild.ns |= new_data->wild.ns;
        return (ISC_R_SUCCESS);
 }
@@ -1242,6 +1310,8 @@ rpz_detach(dns_rpz_zone_t **rpzp, dns_rpz_zones_t *rpzs) {
 
        if (dns_name_dynamic(&rpz->origin))
                dns_name_free(&rpz->origin, rpzs->mctx);
+       if (dns_name_dynamic(&rpz->client_ip))
+               dns_name_free(&rpz->client_ip, rpzs->mctx);
        if (dns_name_dynamic(&rpz->ip))
                dns_name_free(&rpz->ip, rpzs->mctx);
        if (dns_name_dynamic(&rpz->nsdname))
@@ -1250,6 +1320,10 @@ rpz_detach(dns_rpz_zone_t **rpzp, dns_rpz_zones_t *rpzs) {
                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->drop))
+               dns_name_free(&rpz->drop, rpzs->mctx);
+       if (dns_name_dynamic(&rpz->tcp_only))
+               dns_name_free(&rpz->tcp_only, rpzs->mctx);
        if (dns_name_dynamic(&rpz->cname))
                dns_name_free(&rpz->cname, rpzs->mctx);
 
@@ -1339,6 +1413,9 @@ dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
        LOCK(&rpzs->maint_lock);
        LOCK(&rpzs->search_lock);
        if ((rpzs->load_begun & tgt) == 0) {
+               /*
+                * There is no existing version of the target zone.
+                */
                rpzs->load_begun |= tgt;
                dns_rpz_attach_rpzs(rpzs, load_rpzsp);
                UNLOCK(&rpzs->search_lock);
@@ -1353,6 +1430,11 @@ dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
                        return (result);
                load_rpzs = *load_rpzsp;
                load_rpzs->p.num_zones = rpzs->p.num_zones;
+               load_rpzs->total_triggers = rpzs->total_triggers;
+               memcpy(load_rpzs->triggers, rpzs->triggers,
+                      sizeof(load_rpzs->triggers));
+               memset(&load_rpzs->triggers[rpz_num], 0,
+                      sizeof(load_rpzs->triggers[rpz_num]));
                load_rpzs->zones[rpz_num] = rpz;
                isc_refcount_increment(&rpz->refs, NULL);
        }
@@ -1361,33 +1443,53 @@ dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
 }
 
 static void
-fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_triggers_t *totals) {
-       dns_rpz_num_t rpz_num;
+fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+       dns_rpz_num_t n;
        const dns_rpz_zone_t *rpz;
+       dns_rpz_triggers_t old_totals;
        dns_rpz_zbits_t zbit;
+       char namebuf[DNS_NAME_FORMATSIZE];
 
-#      define SET_TRIG(type)                                           \
-       if (rpz == NULL || rpz->triggers.type == 0) {                   \
+#      define SET_TRIG(n, zbit, type)                                  \
+       if (rpzs->triggers[n].type == 0) {                              \
                rpzs->have.type &= ~zbit;                               \
        } else {                                                        \
-               totals->type += rpz->triggers.type;                     \
+               rpzs->total_triggers.type += rpzs->triggers[n].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);
+       memcpy(&old_totals, &rpzs->total_triggers, sizeof(old_totals));
+       memset(&rpzs->total_triggers, 0, sizeof(rpzs->total_triggers));
+       for (n = 0; n < rpzs->p.num_zones; ++n) {
+               rpz = rpzs->zones[n];
+               zbit = DNS_RPZ_ZBIT(n);
+               SET_TRIG(n, zbit, client_ipv4);
+               SET_TRIG(n, zbit, client_ipv6);
+               SET_TRIG(n, zbit, qname);
+               SET_TRIG(n, zbit, ipv4);
+               SET_TRIG(n, zbit, ipv6);
+               SET_TRIG(n, zbit, nsdname);
+               SET_TRIG(n, zbit, nsipv4);
+               SET_TRIG(n, zbit, nsipv6);
        }
 
        fix_qname_skip_recurse(rpzs);
 
+       dns_name_format(&rpzs->zones[rpz_num]->origin,
+                       namebuf, sizeof(namebuf));
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                     DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL,
+                     "(re)loading policy zone '%s' changed from"
+                     " %d to %d qname, %d to %d nsdname,"
+                     " %d to %d IP, %d to %d NSIP entries",
+                     namebuf,
+                     old_totals.qname, rpzs->total_triggers.qname,
+                     old_totals.nsdname, rpzs->total_triggers.nsdname,
+                     old_totals.ipv4 + old_totals.ipv6,
+                     rpzs->total_triggers.ipv4 + rpzs->total_triggers.ipv6,
+                     old_totals.nsipv4 + old_totals.nsipv6,
+                     rpzs->total_triggers.nsipv4 + rpzs->total_triggers.nsipv6);
+
 #      undef SET_TRIG
 }
 
@@ -1399,16 +1501,15 @@ 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_rpz_zbits_t new_bit;
+       dns_rpz_addr_zbits_t new_ip;
        dns_rbt_t *rbt;
        dns_rbtnodechain_t chain;
        dns_rbtnode_t *nmnode;
        dns_rpz_nm_data_t *nm_data, new_data;
-       dns_rpz_triggers_t old_totals, new_totals;
        dns_fixedname_t labelf, originf, namef;
        dns_name_t *label, *origin, *name;
        isc_result_t result;
@@ -1423,22 +1524,9 @@ dns_rpz_ready(dns_rpz_zones_t *rpzs,
                 * This is a successful initial zone loading,
                 * perhaps for a new instance of a view.
                 */
-               fix_triggers(rpzs, &new_totals);
+               fix_triggers(rpzs, rpz_num);
                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);
        }
 
@@ -1446,23 +1534,25 @@ dns_rpz_ready(dns_rpz_zones_t *rpzs,
        LOCK(&load_rpzs->search_lock);
 
        /*
-        * Copy the other policy zones to the new summary databases
-        * unless there is only one policy zone.
+        * Unless there is only one policy zone, copy the other policy zones
+        * from the old policy structure to the new summary databases.
         */
        if (rpzs->p.num_zones > 1) {
+               new_bit = ~DNS_RPZ_ZBIT(rpz_num);
+
                /*
                 * Copy to the radix tree.
                 */
-               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) {
+                       new_ip.ip = cnode->set.ip & new_bit;
+                       new_ip.client_ip = cnode->set.client_ip & new_bit;
+                       new_ip.nsip = cnode->set.nsip & new_bit;
+                       if (new_ip.client_ip != 0 ||
+                           new_ip.ip != 0 ||
+                           new_ip.nsip != 0) {
                                result = search(load_rpzs,
                                                &cnode->ip, cnode->prefix,
-                                               &new_pair, ISC_TRUE,
-                                               &found);
+                                               &new_ip, ISC_TRUE, &found);
                                if (result == ISC_R_NOMEMORY)
                                        goto unlock_and_detach;
                                INSIST(result == ISC_R_SUCCESS);
@@ -1475,7 +1565,7 @@ dns_rpz_ready(dns_rpz_zones_t *rpzs,
                                continue;
                        /*
                         * Go up until we find a branch to the right where
-                        * we previously took the branck to the left.
+                        * we previously took the branch to the left.
                         */
                        for (;;) {
                                parent_cnode = cnode->parent;
@@ -1491,7 +1581,7 @@ dns_rpz_ready(dns_rpz_zones_t *rpzs,
                }
 
                /*
-                * Copy to the summary rbt.
+                * Copy to the summary RBT.
                 */
                dns_fixedname_init(&namef);
                name = dns_fixedname_name(&namef);
@@ -1507,17 +1597,15 @@ dns_rpz_ready(dns_rpz_zones_t *rpzs,
                        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.set.qname = (nm_data->set.qname &
+                                                     new_bit);
+                               new_data.set.ns = nm_data->set.ns & new_bit;
+                               new_data.wild.qname = (nm_data->wild.qname &
+                                                      new_bit);
+                               new_data.wild.ns = nm_data->wild.ns & new_bit;
+                               if (new_data.set.qname != 0 ||
+                                   new_data.set.ns != 0 ||
+                                   new_data.wild.qname != 0 ||
                                    new_data.wild.ns != 0) {
                                        result = dns_name_concatenate(label,
                                                        origin, name, NULL);
@@ -1539,23 +1627,7 @@ dns_rpz_ready(dns_rpz_zones_t *rpzs,
                }
        }
 
-       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);
+       fix_triggers(load_rpzs, rpz_num);
 
        /*
         * Exchange the summary databases.
@@ -1570,7 +1642,10 @@ dns_rpz_ready(dns_rpz_zones_t *rpzs,
        rpzs->rbt = load_rpzs->rbt;
        load_rpzs->rbt = rbt;
 
+       rpzs->total_triggers = load_rpzs->total_triggers;
+
        UNLOCK(&rpzs->search_lock);
+
        result = ISC_R_SUCCESS;
 
  unlock_and_detach:
@@ -1605,6 +1680,7 @@ dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *src_name)
        case DNS_RPZ_TYPE_NSDNAME:
                result = add_name(rpzs, rpz_num, rpz_type, src_name);
                break;
+       case DNS_RPZ_TYPE_CLIENT_IP:
        case DNS_RPZ_TYPE_IP:
        case DNS_RPZ_TYPE_NSIP:
                result = add_cidr(rpzs, rpz_num, rpz_type, src_name);
@@ -1628,7 +1704,7 @@ del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
        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_addr_zbits_t tgt_set;
        dns_rpz_cidr_node_t *tgt, *parent, *child;
 
        /*
@@ -1637,11 +1713,11 @@ del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
         * 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);
+                           src_name, &tgt_ip, &tgt_prefix, &tgt_set);
        if (result != ISC_R_SUCCESS)
                return;
 
-       result = search(rpzs, &tgt_ip, tgt_prefix, &pair, ISC_FALSE, &tgt);
+       result = search(rpzs, &tgt_ip, tgt_prefix, &tgt_set, ISC_FALSE, &tgt);
        if (result != ISC_R_SUCCESS) {
                INSIST(result == ISC_R_NOTFOUND ||
                       result == DNS_R_PARTIALMATCH);
@@ -1658,10 +1734,12 @@ del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
         * 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;
+       tgt_set.client_ip &= tgt->set.client_ip;
+       tgt_set.ip &= tgt->set.ip;
+       tgt_set.nsip &= tgt->set.nsip;
+       tgt->set.client_ip &= ~tgt_set.client_ip;
+       tgt->set.ip &= ~tgt_set.ip;
+       tgt->set.nsip &= ~tgt_set.nsip;
        set_sum_pair(tgt);
 
        adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, ISC_FALSE);
@@ -1680,7 +1758,9 @@ del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
                } else {
                        child = tgt->child[1];
                }
-               if (tgt->pair.d + tgt->pair.ns != 0)
+               if (tgt->set.client_ip != 0 ||
+                   tgt->set.ip != 0 ||
+                   tgt->set.nsip != 0)
                        break;
 
                /*
@@ -1754,18 +1834,18 @@ del_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
         * 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.set.qname &= nm_data->set.qname;
+       del_data.set.ns &= nm_data->set.ns;
+       del_data.wild.qname &= nm_data->wild.qname;
        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->set.qname &= ~del_data.set.qname;
+       nm_data->set.ns &= ~del_data.set.ns;
+       nm_data->wild.qname &= ~del_data.wild.qname;
        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) {
+       if (nm_data->set.qname == 0 && nm_data->set.ns == 0 &&
+           nm_data->wild.qname == 0 && nm_data->wild.ns == 0) {
                result = dns_rbt_deletenode(rpzs->rbt, nmnode, ISC_FALSE);
                if (result != ISC_R_SUCCESS) {
                        /*
@@ -1805,6 +1885,7 @@ dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
        case DNS_RPZ_TYPE_NSDNAME:
                del_name(rpzs, rpz_num, rpz_type, src_name);
                break;
+       case DNS_RPZ_TYPE_CLIENT_IP:
        case DNS_RPZ_TYPE_IP:
        case DNS_RPZ_TYPE_NSIP:
                del_cidr(rpzs, rpz_num, rpz_type, src_name);
@@ -1831,7 +1912,7 @@ dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
                dns_name_t *ip_name, dns_rpz_prefix_t *prefixp)
 {
        dns_rpz_cidr_key_t tgt_ip;
-       dns_rpz_pair_zbits_t pair;
+       dns_rpz_addr_zbits_t tgt_set;
        dns_rpz_cidr_node_t *found;
        isc_result_t result;
        dns_rpz_num_t rpz_num;
@@ -1845,10 +1926,20 @@ dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
                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)
+               switch (rpz_type) {
+               case DNS_RPZ_TYPE_CLIENT_IP:
+                       zbits &= rpzs->have.client_ipv4;
+                       break;
+               case DNS_RPZ_TYPE_IP:
                        zbits &= rpzs->have.ipv4;
-               else
+                       break;
+               case DNS_RPZ_TYPE_NSIP:
                        zbits &= rpzs->have.nsipv4;
+                       break;
+               default:
+                       INSIST(0);
+                       break;
+               }
        } else if (netaddr->family == AF_INET6) {
                dns_rpz_cidr_key_t src_ip6;
 
@@ -1861,20 +1952,30 @@ dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
                for (i = 0; i < 4; i++) {
                        tgt_ip.w[i] = ntohl(src_ip6.w[i]);
                }
-               if (rpz_type == DNS_RPZ_TYPE_IP)
+               switch (rpz_type) {
+               case DNS_RPZ_TYPE_CLIENT_IP:
+                       zbits &= rpzs->have.client_ipv6;
+                       break;
+               case DNS_RPZ_TYPE_IP:
                        zbits &= rpzs->have.ipv6;
-               else
+                       break;
+               case DNS_RPZ_TYPE_NSIP:
                        zbits &= rpzs->have.nsipv6;
+                       break;
+               default:
+                       INSIST(0);
+                       break;
+               }
        } else {
                return (DNS_RPZ_INVALID_NUM);
        }
 
        if (zbits == 0)
                return (DNS_RPZ_INVALID_NUM);
-       make_pair(&pair, zbits, rpz_type);
+       make_addr_set(&tgt_set, zbits, rpz_type);
 
        LOCK(&rpzs->search_lock);
-       result = search(rpzs, &tgt_ip, 128, &pair, ISC_FALSE, &found);
+       result = search(rpzs, &tgt_ip, 128, &tgt_set, ISC_FALSE, &found);
        if (result == ISC_R_NOTFOUND) {
                /*
                 * There are no eligible zones for this IP address.
@@ -1888,12 +1989,19 @@ dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
         * 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);
+       switch (rpz_type) {
+       case DNS_RPZ_TYPE_CLIENT_IP:
+               rpz_num = zbit_to_num(found->set.client_ip & tgt_set.client_ip);
+               break;
+       case DNS_RPZ_TYPE_IP:
+               rpz_num = zbit_to_num(found->set.ip & tgt_set.ip);
+               break;
+       case DNS_RPZ_TYPE_NSIP:
+               rpz_num = zbit_to_num(found->set.nsip & tgt_set.nsip);
+               break;
+       default:
+               INSIST(0);
+               break;
        }
        result = ip2name(&found->ip, found->prefix, dns_rootname, ip_name);
        UNLOCK(&rpzs->search_lock);
@@ -1939,9 +2047,9 @@ dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
                nm_data = nmnode->data;
                if (nm_data != NULL) {
                        if (rpz_type == DNS_RPZ_TYPE_QNAME)
-                               found_zbits = nm_data->pair.d;
+                               found_zbits = nm_data->set.qname;
                        else
-                               found_zbits = nm_data->pair.ns;
+                               found_zbits = nm_data->set.ns;
                }
                nmnode = nmnode->parent;
                /* fall thru */
@@ -1950,7 +2058,7 @@ dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
                        nm_data = nmnode->data;
                        if (nm_data != NULL) {
                                if (rpz_type == DNS_RPZ_TYPE_QNAME)
-                                       found_zbits |= nm_data->wild.d;
+                                       found_zbits |= nm_data->wild.qname;
                                else
                                        found_zbits |= nm_data->wild.ns;
                        }
@@ -2019,7 +2127,19 @@ dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
        }
 
        /*
-        * CNAME PASSTHRU. means "do not rewrite.
+        * CNAME rpz-tcp-only. means "send truncated UDP responses."
+        */
+       if (dns_name_equal(&cname.cname, &rpz->tcp_only))
+               return (DNS_RPZ_POLICY_TCP_ONLY);
+
+       /*
+        * CNAME rpz-drop. means "do not respond."
+        */
+       if (dns_name_equal(&cname.cname, &rpz->drop))
+               return (DNS_RPZ_POLICY_DROP);
+
+       /*
+        * CNAME rpz-passthru. means "do not rewrite."
         */
        if (dns_name_equal(&cname.cname, &rpz->passthru))
                return (DNS_RPZ_POLICY_PASSTHRU);
index b2c2946ca5c3d8b2b5cd2ca9131b5f6a1e7df0d4..ff25fc89486b527071fec1972df5f3bffa8548b1 100644 (file)
@@ -1107,7 +1107,7 @@ static cfg_type_t cfg_type_masterformat = {
 
 /*%
  *  response-policy {
- *     zone <string> [ policy (given|disabled|passthru|
+ *     zone <string> [ policy (given|disabled|passthru|drop|tcp-only|
  *                                     nxdomain|nodata|cname <domain> ) ]
  *                   [ recursive-only yes|no ] [ max-policy-ttl number ] ;
  *  } [ recursive-only yes|no ] [ max-policy-ttl number ] ;
@@ -1137,7 +1137,7 @@ doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) {
 
 /*
  * Parse
- *     given|disabled|passthru|nxdomain|nodata|cname <domain>
+ *     given|disabled|passthru|drop|tcp-only|nxdomain|nodata|cname <domain>
  */
 static isc_result_t
 cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type,
@@ -1268,9 +1268,12 @@ static cfg_type_t cfg_type_rpz_zone = {
        doc_keyvalue, &cfg_rep_string,
        &zone_kw
 };
+/*
+ * "no-op" is an obsolete equivalent of "passthru".
+ */
 static const char *rpz_policies[] = {
-       "given", "disabled", "passthru", "no-op", "nxdomain", "nodata",
-       "cname", NULL
+       "given", "disabled", "passthru", "no-op", "drop", "tcp-only",
+       "nxdomain", "nodata", "cname", NULL
 };
 static cfg_type_t cfg_type_rpz_policy_name = {
        "policy name", cfg_parse_enum, cfg_print_ustring,