]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] DNS Response Policy Service API
authorEvan Hunt <each@isc.org>
Mon, 11 Sep 2017 18:53:42 +0000 (11:53 -0700)
committerEvan Hunt <each@isc.org>
Mon, 11 Sep 2017 18:57:43 +0000 (11:57 -0700)
4713. [func] Added support for the DNS Response Policy Service
(DNSRPS) API, which allows named to use an external
response policy daemon when built with
"configure --enable-dnsrps".  Thanks to Vernon
Schryver and Farsight Security. [RT #43376]

53 files changed:
CHANGES
bin/named/server.c
bin/tests/system/Makefile.in
bin/tests/system/feature-test.c
bin/tests/system/rpz/.gitignore [new file with mode: 0644]
bin/tests/system/rpz/Makefile.in [new file with mode: 0644]
bin/tests/system/rpz/ckdnsrps.sh [new file with mode: 0644]
bin/tests/system/rpz/clean.sh
bin/tests/system/rpz/dnsrps.c [new file with mode: 0644]
bin/tests/system/rpz/dnsrpzd-license.conf [new file with mode: 0644]
bin/tests/system/rpz/dnsrpzd.conf [new file with mode: 0644]
bin/tests/system/rpz/fastrpz.license [new file with mode: 0644]
bin/tests/system/rpz/ns2/named.conf
bin/tests/system/rpz/ns3/base.db
bin/tests/system/rpz/ns3/named.conf
bin/tests/system/rpz/ns5/named.conf
bin/tests/system/rpz/ns5/tld5.db
bin/tests/system/rpz/ns6/named.conf
bin/tests/system/rpz/ns7/named.conf
bin/tests/system/rpz/setup.sh
bin/tests/system/rpz/test3
bin/tests/system/rpz/test4
bin/tests/system/rpz/tests.sh
bin/tests/system/rpzrecurse/clean.sh
bin/tests/system/rpzrecurse/ns1/named.conf
bin/tests/system/rpzrecurse/ns2/named.clientip.conf
bin/tests/system/rpzrecurse/ns2/named.clientip2.conf
bin/tests/system/rpzrecurse/ns2/named.conf.header
bin/tests/system/rpzrecurse/ns2/named.log.conf
bin/tests/system/rpzrecurse/ns2/named.wildcard1.conf
bin/tests/system/rpzrecurse/ns2/named.wildcard2.conf
bin/tests/system/rpzrecurse/ns2/named.wildcard3.conf
bin/tests/system/rpzrecurse/ns3/named1.conf
bin/tests/system/rpzrecurse/ns3/named2.conf
bin/tests/system/rpzrecurse/ns4/named.conf
bin/tests/system/rpzrecurse/prereq.sh
bin/tests/system/rpzrecurse/setup.sh
bin/tests/system/rpzrecurse/testgen.pl
bin/tests/system/rpzrecurse/tests.sh
config.h.in
configure
configure.in
doc/arm/Bv9ARM-book.xml
doc/arm/notes.xml
lib/dns/Makefile.in
lib/dns/dnsrps.c [new file with mode: 0644]
lib/dns/include/dns/Makefile.in
lib/dns/include/dns/dnsrps.h [new file with mode: 0644]
lib/dns/include/dns/librpz.h [new file with mode: 0644]
lib/dns/include/dns/rpz.h
lib/dns/rpz.c
lib/isccfg/namedconf.c
lib/ns/query.c

diff --git a/CHANGES b/CHANGES
index 92cb845c5f09a26a58b8b67f388cab983f608812..79cc3fa4184ec4fa8550e4512f9e292f50ea7f14 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,9 @@
+4713.  [func]          Added support for the DNS Response Policy Service
+                       (DNSRPS) API, which allows named to use an external
+                       response policy daemon when built with
+                       "configure --enable-dnsrps".  Thanks to Vernon
+                       Schryver and Farsight Security. [RT #43376]
+
 4712.  [bug]           "dig +domain" and "dig +search" didn't retain the
                        search domain when retrying with TCP. [RT #45547]
 
index bbd9ee726bd1ed6909ce9ca6acbec2c463ac4c88..323e7f526cf65286f120fb94e42f088029e54625 100644 (file)
@@ -59,6 +59,7 @@
 #include <dns/db.h>
 #include <dns/dispatch.h>
 #include <dns/dlz.h>
+#include <dns/dnsrps.h>
 #include <dns/dns64.h>
 #include <dns/dyndb.h>
 #include <dns/events.h>
@@ -1862,6 +1863,230 @@ cleanup:
        return (result);
 }
 
+typedef struct conf_dnsrps_ctx conf_dnsrps_ctx_t;
+struct conf_dnsrps_ctx {
+       isc_result_t    result;
+       char            *cstr;
+       size_t          cstr_size;
+       isc_mem_t       *mctx;
+};
+
+/*
+ * Add to the DNSRPS configuration string.
+ */
+static isc_boolean_t
+conf_dnsrps_sadd(conf_dnsrps_ctx_t *ctx, const char *p, ...) {
+       size_t new_len, cur_len, new_cstr_size;
+       char *new_cstr;
+       va_list args;
+
+       if (ctx->cstr == NULL) {
+               ctx->cstr = isc_mem_get(ctx->mctx, 256);
+               if (ctx->cstr == NULL) {
+                       ctx->result = ISC_R_NOMEMORY;
+                       return (ISC_FALSE);
+               }
+               ctx->cstr[0] = '\0';
+               ctx->cstr_size = 256;
+       }
+
+       cur_len = strlen(ctx->cstr);
+       va_start(args, p);
+       new_len = vsnprintf(ctx->cstr + cur_len, ctx->cstr_size - cur_len,
+                           p, args) + 1;
+       va_end(args);
+
+       if (cur_len + new_len <= ctx->cstr_size)
+               return (ISC_TRUE);
+
+       new_cstr_size = ((cur_len + new_len)/256 + 1) * 256;
+       new_cstr = isc_mem_get(ctx->mctx, new_cstr_size);
+       if (new_cstr == NULL) {
+               ctx->result = ISC_R_NOMEMORY;
+               return (ISC_FALSE);
+       }
+
+       memmove(new_cstr, ctx->cstr, cur_len);
+       isc_mem_put(ctx->mctx, ctx->cstr, ctx->cstr_size);
+       ctx->cstr_size = new_cstr_size;
+       ctx->cstr = new_cstr;
+
+       /* cannot use args twice after a single va_start()on some systems */
+       va_start(args, p);
+       vsnprintf(ctx->cstr + cur_len, ctx->cstr_size - cur_len, p, args);
+       va_end(args);
+       return (ISC_TRUE);
+}
+
+/*
+ * Get an DNSRPS configuration value using the global and view options
+ * for the default.  Return ISC_FALSE upon failure.
+ */
+static isc_boolean_t
+conf_dnsrps_get(const cfg_obj_t **sub_obj,
+               const cfg_obj_t **maps ,const cfg_obj_t *obj,
+               const char *name, conf_dnsrps_ctx_t *ctx)
+{
+       if (ctx != NULL && ctx->result != ISC_R_SUCCESS) {
+               *sub_obj = NULL;
+               return (ISC_FALSE);
+       }
+
+       *sub_obj = cfg_tuple_get(obj, name);
+       if (cfg_obj_isvoid(*sub_obj)) {
+               *sub_obj = NULL;
+               if (maps != NULL &&
+                   ISC_R_SUCCESS != named_config_get(maps, name, sub_obj))
+                       *sub_obj = NULL;
+       }
+       return (ISC_TRUE);
+}
+
+/*
+ * Handle a DNSRPS boolean configuration value with the global and view
+ * options providing the default.
+ */
+static void
+conf_dnsrps_yes_no(const cfg_obj_t *obj, const char* name,
+                  conf_dnsrps_ctx_t *ctx)
+{
+       const cfg_obj_t *sub_obj;
+
+       if (!conf_dnsrps_get(&sub_obj, NULL, obj, name, ctx))
+               return;
+       if (sub_obj == NULL)
+               return;
+       if (ctx == NULL) {
+               cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+                           "\"%s\" without \"dnsrps-enable yes\"",
+                           name);
+               return;
+       }
+
+       conf_dnsrps_sadd(ctx, " %s %s", name,
+                        cfg_obj_asboolean(sub_obj) ? "yes" : "no");
+}
+
+static void
+conf_dnsrps_num(const cfg_obj_t *obj, const char *name,
+               conf_dnsrps_ctx_t *ctx)
+{
+       const cfg_obj_t *sub_obj;
+
+       if (!conf_dnsrps_get(&sub_obj, NULL, obj, name, ctx))
+               return;
+       if (sub_obj == NULL)
+               return;
+       if (ctx == NULL) {
+               cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+                           "\"%s\" without \"dnsrps-enable yes\"",
+                           name);
+               return;
+       }
+
+       conf_dnsrps_sadd(ctx, " %s %d", name, cfg_obj_asuint32(sub_obj));
+}
+
+/*
+ * Convert the parsed RPZ configuration statement to a string for
+ * dns_rpz_new_zones().
+ */
+static isc_result_t
+conf_dnsrps(dns_view_t *view, const cfg_obj_t **maps,
+           isc_boolean_t nsip_enabled, isc_boolean_t nsdname_enabled,
+           dns_rpz_zbits_t *nsip_on, dns_rpz_zbits_t *nsdname_on,
+           char **rps_cstr, size_t *rps_cstr_size,
+           const cfg_obj_t *rpz_obj, const cfg_listelt_t *zone_element)
+{
+       conf_dnsrps_ctx_t ctx;
+       const cfg_obj_t *zone_obj, *obj;
+       dns_rpz_num_t rpz_num;
+       isc_boolean_t on;
+       const char *s;
+
+       memset(&ctx, 0, sizeof(ctx));
+       ctx.result = ISC_R_SUCCESS;
+       ctx.mctx = view->mctx;
+
+       for (rpz_num = 0;
+            zone_element != NULL && ctx.result == ISC_R_SUCCESS;
+            ++rpz_num) {
+               zone_obj = cfg_listelt_value(zone_element);
+
+               s = cfg_obj_asstring(cfg_tuple_get(zone_obj, "zone name"));
+               conf_dnsrps_sadd(&ctx, "zone \"%s\"", s);
+
+               obj = cfg_tuple_get(zone_obj, "policy");
+               if (!cfg_obj_isvoid(obj)) {
+                       s = cfg_obj_asstring(cfg_tuple_get(obj, "policy name"));
+                       conf_dnsrps_sadd(&ctx, " policy %s", s);
+                       if (strcasecmp(s, "cname") == 0) {
+                               s = cfg_obj_asstring(cfg_tuple_get(obj,
+                                                                  "cname"));
+                               conf_dnsrps_sadd(&ctx, " %s", s);
+                       }
+               }
+
+               conf_dnsrps_yes_no(zone_obj, "recursive-only", &ctx);
+               conf_dnsrps_yes_no(zone_obj, "log", &ctx);
+               conf_dnsrps_num(zone_obj, "max-policy-ttl", &ctx);
+               obj = cfg_tuple_get(rpz_obj, "nsip-enable");
+               if (!cfg_obj_isvoid(obj)) {
+                       if (cfg_obj_asboolean(obj))
+                               *nsip_on |= DNS_RPZ_ZBIT(rpz_num);
+                       else
+                               *nsip_on &= ~DNS_RPZ_ZBIT(rpz_num);
+               }
+               on = ((*nsip_on & DNS_RPZ_ZBIT(rpz_num)) != 0);
+               if (nsip_enabled != on)
+                       conf_dnsrps_sadd(&ctx, on ? " nsip-enable yes " :
+                                         " nsip-enable no ");
+               obj = cfg_tuple_get(rpz_obj, "nsdname-enable");
+               if (!cfg_obj_isvoid(obj)) {
+                       if (cfg_obj_asboolean(obj))
+                               *nsdname_on |= DNS_RPZ_ZBIT(rpz_num);
+                       else
+                               *nsdname_on &= ~DNS_RPZ_ZBIT(rpz_num);
+               }
+               on = ((*nsdname_on & DNS_RPZ_ZBIT(rpz_num)) != 0);
+               if (nsdname_enabled != on)
+                       conf_dnsrps_sadd(&ctx, on
+                                                ? " nsdname-enable yes "
+                                                : " nsdname-enable no ");
+               conf_dnsrps_sadd(&ctx, ";\n");
+               zone_element = cfg_list_next(zone_element);
+       }
+
+       conf_dnsrps_yes_no(rpz_obj, "recursive-only",  &ctx);
+       conf_dnsrps_num(rpz_obj, "max-policy-ttl",  &ctx);
+       conf_dnsrps_num(rpz_obj, "min-ns-dots",  &ctx);
+       conf_dnsrps_yes_no(rpz_obj, "qname-wait-recurse",  &ctx);
+       conf_dnsrps_yes_no(rpz_obj, "break-dnssec",  &ctx);
+       if (!nsip_enabled)
+               conf_dnsrps_sadd(&ctx, " nsip-enable no ");
+       if (!nsdname_enabled)
+               conf_dnsrps_sadd(&ctx,  " nsdname-enable no ");
+
+       /*
+        * Get the general dnsrpzd parameters from the response-policy
+        * statement in the view and the general options.
+        */
+       if (conf_dnsrps_get(&obj, maps, rpz_obj, "dnsrps-options", &ctx) &&
+           obj != NULL)
+               conf_dnsrps_sadd(&ctx, " %s\n", cfg_obj_asstring(obj));
+
+       if (ctx.result == ISC_R_SUCCESS) {
+               *rps_cstr = ctx.cstr;
+               *rps_cstr_size = ctx.cstr_size;
+       } else {
+               if (ctx.cstr != NULL)
+                       isc_mem_put(ctx.mctx, ctx.cstr, ctx.cstr_size);
+               *rps_cstr = NULL;
+               *rps_cstr_size = 0;
+       }
+       return (ctx.result);
+}
+
 static isc_result_t
 configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
                   const char *str, const char *msg)
@@ -1960,13 +2185,19 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
                            "invalid zone name '%s'", str);
                return (DNS_R_EMPTYLABEL);
        }
-       for (rpz_num = 0; rpz_num < view->rpzs->p.num_zones-1; ++rpz_num) {
-               if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin,
-                                  &zone->origin)) {
-                       cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
-                                   "duplicate '%s'", str);
-                       result = DNS_R_DUPLICATE;
-                       return (result);
+       if (!view->rpzs->p.dnsrps_enabled) {
+               for (rpz_num = 0;
+                    rpz_num < view->rpzs->p.num_zones - 1;
+                    ++rpz_num)
+               {
+                       if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin,
+                                          &zone->origin)) {
+                               cfg_obj_log(rpz_obj, named_g_lctx,
+                                           DNS_RPZ_ERROR_LEVEL,
+                                           "duplicate '%s'", str);
+                               result = DNS_R_DUPLICATE;
+                               return (result);
+                       }
                }
        }
        if (*old_rpz_okp && !dns_name_equal(&old->origin, &zone->origin))
@@ -2030,12 +2261,17 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
 }
 
 static isc_result_t
-configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
-             isc_boolean_t *old_rpz_okp)
+configure_rpz(dns_view_t *view, const cfg_obj_t **maps,
+             const cfg_obj_t *rpz_obj, isc_boolean_t *old_rpz_okp)
 {
+       isc_boolean_t dnsrps_enabled;
        const cfg_listelt_t *zone_element;
+       char *rps_cstr;
+       size_t rps_cstr_size;
        const cfg_obj_t *sub_obj;
        isc_boolean_t recursive_only_def;
+       isc_boolean_t nsip_enabled, nsdname_enabled;
+       dns_rpz_zbits_t nsip_on, nsdname_on;
        dns_ttl_t ttl_def;
        isc_uint32_t minupdateint_def;
        dns_rpz_zones_t *zones;
@@ -2051,13 +2287,80 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
        if (zone_element == NULL)
                return (ISC_R_SUCCESS);
 
-       result = dns_rpz_new_zones(&view->rpzs, view->mctx,
+#ifdef ENABLE_RPZ_NSIP
+       nsip_enabled = ISC_TRUE;
+       nsdname_enabled = ISC_TRUE;
+#else
+       nsip_enabled = ISC_FALSE;
+       nsdname_enabled = ISC_FALSE;
+#endif
+       sub_obj = cfg_tuple_get(rpz_obj, "nsip-enable");
+       if (!cfg_obj_isvoid(sub_obj))
+               nsip_enabled = cfg_obj_asboolean(sub_obj);
+       nsip_on = nsip_enabled ? DNS_RPZ_ALL_ZBITS : 0;
+
+       sub_obj = cfg_tuple_get(rpz_obj, "nsdname-enable");
+       if (!cfg_obj_isvoid(sub_obj))
+               nsdname_enabled = cfg_obj_asboolean(sub_obj);
+       nsdname_on = nsdname_enabled ? DNS_RPZ_ALL_ZBITS : 0;
+
+       /*
+        * "dnsrps-enable yes|no" can be either a global or response-policy
+        * clause.
+        */
+       dnsrps_enabled = ISC_FALSE;
+       rps_cstr = NULL;
+       rps_cstr_size = 0;
+       sub_obj = NULL;
+       (void)named_config_get(maps, "dnsrps-enable", &sub_obj);
+       if (sub_obj != NULL) {
+               dnsrps_enabled = cfg_obj_asboolean(sub_obj);
+       }
+       sub_obj = cfg_tuple_get(rpz_obj, "dnsrps-enable");
+       if (!cfg_obj_isvoid(sub_obj)) {
+               dnsrps_enabled = cfg_obj_asboolean(sub_obj);
+       }
+#ifdef USE_DNSRPS
+       if (dnsrps_enabled && librpz == NULL) {
+               cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                           "\"dnsrps-enable yes\" but %s",
+                           librpz_lib_open_emsg.c);
+               return (ISC_R_FAILURE);
+       }
+#else
+       if (dnsrps_enabled) {
+               cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                           "\"dnsrps-enable yes\" but"
+                           " with `./configure --enable-dnsrps`");
+               return (ISC_R_FAILURE);
+       }
+#endif
+
+       if (dnsrps_enabled) {
+               /*
+                * Generate the DNS Response Policy Service
+                * configuration string.
+                */
+               result = conf_dnsrps(view, maps,
+                                     nsip_enabled, nsdname_enabled,
+                                     &nsip_on, &nsdname_on,
+                                     &rps_cstr, &rps_cstr_size,
+                                     rpz_obj, zone_element);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       }
+
+       result = dns_rpz_new_zones(&view->rpzs, rps_cstr,
+                                  rps_cstr_size, view->mctx,
                                   named_g_taskmgr, named_g_timermgr);
        if (result != ISC_R_SUCCESS)
                return (result);
 
        zones = view->rpzs;
 
+       zones->p.nsip_on = nsip_on;
+       zones->p.nsdname_on = nsdname_on;
+
        sub_obj = cfg_tuple_get(rpz_obj, "recursive-only");
        if (!cfg_obj_isvoid(sub_obj) &&
            !cfg_obj_asboolean(sub_obj))
@@ -2141,15 +2444,33 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
         * zones are unchanged, then use the same policy data.
         * Data for individual zones that must be reloaded will be merged.
         */
-       if (old != NULL && memcmp(&old->p, &zones->p, sizeof(zones->p)) != 0)
+       if (*old_rpz_okp &&
+           old != NULL && memcmp(&old->p, &zones->p, sizeof(zones->p)) != 0)
+       {
+               *old_rpz_okp = ISC_FALSE;
+       }
+
+       if (*old_rpz_okp &&
+           (old == NULL ||
+            old->rps_cstr == NULL) != (zones->rps_cstr == NULL))
+       {
                *old_rpz_okp = ISC_FALSE;
+       }
+
+       if (*old_rpz_okp &&
+           (zones->rps_cstr != NULL &&
+            strcmp(old->rps_cstr, zones->rps_cstr) != 0))
+       {
+               *old_rpz_okp = ISC_FALSE;
+       }
+
        if (*old_rpz_okp) {
                dns_rpz_detach_rpzs(&view->rpzs);
                dns_rpz_attach_rpzs(pview->rpzs, &view->rpzs);
        } else if (old != NULL && pview != NULL) {
-               pview->rpzs->rpz_ver += 1;
+               ++pview->rpzs->rpz_ver;
                view->rpzs->rpz_ver = pview->rpzs->rpz_ver;
-               cfg_obj_log(rpz_obj, named_g_lctx, ISC_LOG_DEBUG(1),
+               cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_DEBUG_LEVEL1,
                            "updated RPZ policy: version %d",
                            view->rpzs->rpz_ver);
        }
@@ -3429,7 +3750,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        obj = NULL;
        if (view->rdclass == dns_rdataclass_in && need_hints &&
            named_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
-               CHECK(configure_rpz(view, obj, &old_rpz_ok));
+               CHECK(configure_rpz(view, maps, obj, &old_rpz_ok));
        }
 
        obj = NULL;
@@ -3460,6 +3781,32 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
                                     ISC_FALSE));
        }
 
+       /*
+        * Check that a master or slave zone was found for each
+        * zone named in the response policy statement
+        * unless we are using RPZ service interface.
+        */
+       if (view->rpzs != NULL && !view->rpzs->p.dnsrps_enabled) {
+               dns_rpz_num_t n;
+
+               for (n = 0; n < view->rpzs->p.num_zones; ++n) {
+                       if ((view->rpzs->defined & DNS_RPZ_ZBIT(n)) == 0) {
+                               char namebuf[DNS_NAME_FORMATSIZE];
+
+                               dns_name_format(&view->rpzs->zones[n]->origin,
+                                               namebuf, sizeof(namebuf));
+                               isc_log_write(named_g_lctx,
+                                             NAMED_LOGCATEGORY_GENERAL,
+                                             NAMED_LOGMODULE_SERVER,
+                                             DNS_RPZ_ERROR_LEVEL, "rpz '%s'"
+                                             " is not a master or slave zone",
+                                             namebuf);
+                               result = ISC_R_NOTFOUND;
+                               goto cleanup;
+                       }
+               }
+       }
+
        /*
         * If we're allowing added zones, then load zone configuration
         * from the newzone file for zones that were added during previous
@@ -5567,10 +5914,14 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
        }
 
        /*
-        * Note whether this is a response policy zone and which one if so.
+        * Note whether this is a response policy zone and which one if so,
+        * unless we are using RPZ service interface.  In that case, the
+        * BIND zone database has nothing to do with rpz and so we don't care.
         */
        for (rpz_num = 0; ; ++rpz_num) {
-               if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones) {
+               if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones ||
+                   view->rpzs->p.dnsrps_enabled)
+               {
                        rpz_num = DNS_RPZ_INVALID_NUM;
                        break;
                }
@@ -8315,6 +8666,22 @@ load_configuration(const char *filename, named_server_t *server,
 
        (void) named_server_loadnta(server);
 
+#ifdef USE_DNSRPS
+       /*
+        * Start and connect to the DNS Response Policy Service
+        * daemon, dnsrpzd, for each view that uses DNSRPS.
+        */
+       for (view = ISC_LIST_HEAD(server->viewlist);
+            view != NULL;
+            view = ISC_LIST_NEXT(view, link)) {
+               result = dns_dnsrps_connect(view->rpzs);
+               if (result != ISC_R_SUCCESS) {
+                       view = NULL;
+                       goto cleanup;
+               }
+       }
+#endif
+
        result = ISC_R_SUCCESS;
 
  cleanup:
@@ -8757,6 +9124,12 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) {
        server->mctx = mctx;
        server->task = NULL;
        server->zonemgr = NULL;
+
+#ifdef USE_DNSRPS
+       CHECKFATAL(dns_dnsrps_server_create(),
+                  "initializing RPZ service interface");
+#endif
+
        /* Initialize server data structures. */
        server->interfacemgr = NULL;
        ISC_LIST_INIT(server->viewlist);
@@ -8913,6 +9286,10 @@ named_server_destroy(named_server_t **serverp) {
                dns_dt_detach(&server->dtenv);
 #endif /* HAVE_DNSTAP */
 
+#ifdef USE_DNSRPS
+       dns_dnsrps_server_destroy();
+#endif
+
        named_controls_destroy(&server->controls);
 
        isc_stats_detach(&server->zonestats);
index 6370c67ba3bf66a83094d796017ae5f72a951001..e7fedd3a53d283191fca6e3ac95b29d3ecb18a61 100644 (file)
@@ -12,7 +12,7 @@ VERSION=@BIND9_VERSION@
 
 @BIND9_MAKE_INCLUDES@
 
-SUBDIRS =      dlzexternal dyndb pipelined rndc rsabigexponent tkey
+SUBDIRS =      dlzexternal dyndb pipelined rndc rpz rsabigexponent tkey
 
 CINCLUDES =    ${ISC_INCLUDES} ${DNS_INCLUDES}
 
index bf1928a0710c41f5f84b9f6e806862ba244dd4e1..2b831972b0475f40cc781d169f2140ce07777fb8 100644 (file)
@@ -35,6 +35,7 @@ usage() {
        fprintf(stderr, "usage: feature-test <arg>\n");
        fprintf(stderr, "args:\n");
        fprintf(stderr, "       --edns-version\n");
+       fprintf(stderr, "       --enable-dnsrps\n");
        fprintf(stderr, "       --enable-filter-aaaa\n");
        fprintf(stderr, "       --gethostname\n");
        fprintf(stderr, "       --gssapi\n");
@@ -54,6 +55,14 @@ main(int argc, char **argv) {
                return (1);
        }
 
+       if (strcmp(argv[1], "--enable-dnsrps") == 0) {
+#ifdef USE_DNSRPS
+               return (0);
+#else
+               return (1);
+#endif
+       }
+
        if (strcmp(argv[1], "--enable-filter-aaaa") == 0) {
 #ifdef ALLOW_FILTER_AAAA
                return (0);
diff --git a/bin/tests/system/rpz/.gitignore b/bin/tests/system/rpz/.gitignore
new file mode 100644 (file)
index 0000000..0457088
--- /dev/null
@@ -0,0 +1,2 @@
+alt-dnsrpzd-license.conf
+dnsrps
diff --git a/bin/tests/system/rpz/Makefile.in b/bin/tests/system/rpz/Makefile.in
new file mode 100644 (file)
index 0000000..abee032
--- /dev/null
@@ -0,0 +1,45 @@
+# Copyright (C) 2011-2013, 2015, 2016  Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+srcdir =       @srcdir@
+VPATH =                @srcdir@
+top_srcdir =   @top_srcdir@
+
+VERSION=@BIND9_VERSION@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES =     ${ISC_INCLUDES} ${DNS_INCLUDES}
+
+CDEFINES =
+CWARNINGS =
+
+DNSLIBS =
+ISCLIBS =      ../../../../lib/isc/libisc.@A@
+
+DNSDEPLIBS =
+ISCDEPLIBS =    ../../../../lib/isc/libisc.@A@
+
+DEPLIBS =      ${ISCDEPLIBS}
+
+LIBS =         ${ISCLIBS} @LIBS@
+
+TARGETS =      dnsrps@EXEEXT@
+
+DNSRPSOBJS =   dnsrps.@O@
+
+SRCS =         dnsrps.c
+
+@BIND9_MAKE_RULES@
+
+all: dnsrps@EXEEXT@
+
+dnsrps@EXEEXT@: ${DNSRPSOBJS} ${DEPLIBS}
+       ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${DNSRPSOBJS} ${LIBS}
+
+clean distclean::
+       rm -f ${TARGETS}
+
diff --git a/bin/tests/system/rpz/ckdnsrps.sh b/bin/tests/system/rpz/ckdnsrps.sh
new file mode 100644 (file)
index 0000000..fa239ee
--- /dev/null
@@ -0,0 +1,161 @@
+#! /bin/sh
+#
+# Copyright (C) 2017  Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+set -e
+
+# Say on stdout whether to test DNSRPS
+#      and create dnsrps.conf and dnsrps-slave.conf
+# Note that dnsrps.conf and dnsrps-slave.conf are included in named.conf
+#      and differ from dnsrpz.conf which is used by dnsrpzd.
+
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+DNSRPS_CMD=../rpz/dnsrps
+
+AS_NS=
+TEST_DNSRPS=
+MCONF=dnsrps.conf
+SCONF=dnsrps-slave.conf
+USAGE="$0: [-xAD] [-M dnsrps.conf] [-S dnsrps-slave.conf]"
+while getopts "xADM:S:" c; do
+    case $c in
+       x) set -x; DEBUG=-x;;
+       A) AS_NS=yes;;
+       D) TEST_DNSRPS=yes;;
+       M) MCONF="$OPTARG";;
+       S) SCONF="$OPTARG";;
+       *) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if [ "$#" -ne 0 ]; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+# erase any existing conf files
+cat /dev/null > $MCONF
+cat /dev/null > $SCONF
+
+add_conf () {
+    echo "$*" >>$MCONF
+    echo "$*" >>$SCONF
+}
+
+if ! $FEATURETEST --enable-dnsrps; then
+    if [ -n "$TEST_DNSRPS" ]; then
+        add_conf "## DNSRPS disabled at compile time"
+    fi
+    add_conf "#skip"
+    exit 0
+fi
+
+if [ -z "$TEST_DNSRPS" ]; then
+    add_conf "## DNSRPS testing is disabled"
+    add_conf '#skip'
+    exit 0
+fi
+
+if [ ! -x $DNSRPS_CMD ]; then
+    add_conf "## make $DNSRPS_CMD to test DNSRPS"
+    add_conf '#skip'
+    exit 0
+fi
+
+if $DNSRPS_CMD -a >/dev/null; then :
+else
+    add_conf "## DNSRPS provider library is not available"
+    add_conf '#skip'
+    exit 0
+fi
+
+CMN="  dnsrps-options { dnsrpzd-conf ../dnsrpzd.conf
+                        dnsrpzd-sock ../dnsrpzd.sock
+                        dnsrpzd-rpzf ../dnsrpzd.rpzf
+                        dnsrpzd-args '-dddd -L stdout'
+                        log-level 3"
+
+MASTER="$CMN"
+if [ -n "$AS_NS" ]; then
+    MASTER="$MASTER
+                       qname-as-ns yes
+                       ip-as-ns yes"
+fi
+
+# write dnsrps setttings for master resolver
+cat <<EOF >>$MCONF
+$MASTER };
+EOF
+
+# write dnsrps setttings for resolvers that should not start dnsrpzd
+cat <<EOF >>$SCONF
+$CMN
+                       dnsrpzd '' };   # do not start dnsrpzd
+EOF
+
+
+# DNSRPS is available.
+# The test should fail if the license is bad.
+add_conf "dnsrps-enable yes;"
+
+# Use alt-dnsrpzd-license.conf if it exists
+CUR_L=dnsrpzd-license-cur.conf
+ALT_L=alt-dnsrpzd-license.conf
+# try ../rpz/alt-dnsrpzd-license.conf if alt-dnsrpzd-license.conf does not exist
+[ -s $ALT_L ] || ALT_L=../rpz/alt-dnsrpzd-license.conf
+if [ -s $ALT_L ]; then
+    SRC_L=$ALT_L
+    USE_ALT=
+else
+    SRC_L=../rpz/dnsrpzd-license.conf
+    USE_ALT="## consider installing alt-dnsrpzd-license.conf"
+fi
+cp $SRC_L $CUR_L
+
+# parse $CUR_L for the license zone name, master IP addresses, and optional
+#   transfer-source IP addresses
+eval `sed -n -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'\
+    -e 's/.*zone *\([-a-z0-9]*.license.fastrpz.com\).*/NAME=\1/p'      \
+    -e 's/.*farsight_fastrpz_license *\([0-9.]*\);.*/IPV4=\1/p'                \
+    -e 's/.*farsight_fastrpz_license *\([0-9a-f:]*\);.*/IPV6=\1/p'     \
+    -e 's/.*transfer-source *\([0-9.]*\);.*/TS4=-b\1/p'                        \
+    -e 's/.*transfer-source *\([0-9a-f:]*\);.*/TS6=-b\1/p'             \
+    -e 's/.*transfer-source-v6 *\([0-9a-f:]*\);.*/TS6=-b\1/p'          \
+       $CUR_L`
+if [ -z "$NAME" ]; then
+    add_conf "## no DNSRPS tests; no license domain name in $SRC_L"
+    add_conf '#fail'
+    exit 0
+fi
+if [ -z "$IPV4" ]; then
+    IPV4=license1.fastrpz.com
+    TS4=
+fi
+if [ -z "$IPV6" ]; then
+    IPV6=license1.fastrpz.com
+    TS6=
+fi
+
+# This TSIG key is common and NOT a secret
+KEY='hmac-sha256:farsight_fastrpz_license:f405d02b4c8af54855fcebc1'
+
+# Try IPv4 and then IPv6 to deal with IPv6 tunnel and connectivity problems
+if `$DIG -4 -t axfr -y$KEY $TS4 $NAME @$IPV4                           \
+           | grep -i "^$NAME.*TXT" >/dev/null`; then
+    exit 0
+fi
+if `$DIG -6 -t axfr -y$KEY $TS6 $NAME @$IPV6                           \
+           | grep -i "^$NAME.*TXT" >/dev/null`; then
+    exit 0
+fi
+
+add_conf "## DNSRPS lacks a valid license via $SRC_L"
+[ -z "$USE_ALT" ] || add_conf "$USE_ALT"
+add_conf '#fail'
index ec469037bb03946f652684db58b31d0d758b1d53..ffe1c4fd9005d5aba97d167b5e7977d4d00942b9 100644 (file)
@@ -10,7 +10,9 @@ rm -f proto.* dsset-* trusted.conf dig.out* nsupdate.tmp ns*/*tmp
 rm -f ns*/*.key ns*/*.private ns2/tld2s.db ns2/bl.tld2.db
 rm -f ns3/bl*.db ns*/*switch ns*/empty.db ns*/empty.db.jnl
 rm -f ns5/requests ns5/example.db ns5/bl.db ns5/*.perf
-rm -f */named.memstats */named.run */named.stats */session.key
-rm -f */*.jnl */*.core */*.pid
+rm -f */named.memstats */*.run */named.stats */session.key
+rm -f */*.log */*.jnl */*core */*.pid
 rm -f */policy2.db
 rm -f ns*/named.lock
+rm -f dnsrps*.conf
+rm -f dnsrpzd-license-cur.conf dnsrpzd.rpzf dnsrpzd.sock dnsrpzd.pid
diff --git a/bin/tests/system/rpz/dnsrps.c b/bin/tests/system/rpz/dnsrps.c
new file mode 100644 (file)
index 0000000..1abd9d8
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2011-2013, 2016  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * -a          exit(0) if dnsrps is available or dlopen() msg if not
+ * -p          print the path to dnsrpzd configured in dnsrps so that
+ *                 dnsrpzd can be run by a setup.sh script.
+ *                 Exit(1) if dnsrps is not available
+ * -n domain   print the serial number of a domain to check if a new
+ *                 version of a policy zone has been transferred to dnsrpzd.
+ *                 Exit(1) if dnsrps is not available
+ * -w sec.ond  wait for seconds, because `sleep 0.1` is not portable
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <isc/boolean.h>
+#include <isc/util.h>
+
+#ifdef USE_DNSRPS
+#define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN
+#include <dns/librpz.h>
+
+librpz_t *librpz;
+#else
+typedef struct {char c[120];} librpz_emsg_t;
+#endif
+
+
+static isc_boolean_t link_dnsrps(librpz_emsg_t *emsg);
+
+
+#define USAGE "usage: [-ap] [-n domain] [-w sec.onds]\n"
+
+int
+main(int argc, char **argv) {
+#ifdef USE_DNSRPS
+       char cstr[sizeof("zone ")+1024+10];
+       librpz_clist_t *clist;
+       librpz_client_t *client;
+       librpz_rsp_t *rsp;
+       uint32_t serial;
+#endif
+       double seconds;
+       librpz_emsg_t emsg;
+       char *p;
+       int i;
+
+       while ((i = getopt(argc, argv, "apn:w:")) != -1) {
+               switch (i) {
+               case 'a':
+                       if (!link_dnsrps(&emsg)) {
+                               printf("I:%s\n", emsg.c);
+                               return (1);
+                       }
+                       return (0);
+
+               case 'p':
+                       if (!link_dnsrps(&emsg)) {
+                               fprintf(stderr, "## %s\n", emsg.c);
+                               return (1);
+                       }
+#ifdef USE_DNSRPS
+                       printf("%s\n", librpz->dnsrpzd_path);
+                       return (0);
+#else
+                       INSIST(0);
+#endif
+
+               case 'n':
+                       if (!link_dnsrps(&emsg)) {
+                               fprintf(stderr, "## %s\n", emsg.c);
+                               return (1);
+                       }
+#ifdef USE_DNSRPS
+                       /*
+                        * Get the serial number of a policy zone from
+                        * a running dnsrpzd daemon.
+                        */
+                       clist = librpz->clist_create(&emsg, NULL, NULL,
+                                                    NULL, NULL, NULL);
+                       if (clist == NULL) {
+                               fprintf(stderr, "## %s: %s\n", optarg, emsg.c);
+                               return (1);
+                       }
+                       snprintf(cstr, sizeof(cstr),
+                                "zone %s; dnsrpzd \"\";"
+                                " dnsrpzd-sock dnsrpzd.sock;"
+                                " dnsrpzd-rpzf dnsrpzd.rpzf",
+                                optarg);
+                       client = librpz->client_create(&emsg, clist,
+                                                      cstr, true);
+                       if (client == NULL) {
+                               fprintf(stderr, "## %s\n", emsg.c);
+                               return (1);
+                       }
+
+                       rsp = NULL;
+                       if (!librpz->rsp_create(&emsg, &rsp, NULL,
+                                               client, true, false) ||
+                           rsp == NULL) {
+                               fprintf(stderr, "## %s\n", emsg.c);
+                               librpz->client_detach(&client);
+                               return (1);
+                       }
+
+                       if (!librpz->soa_serial(&emsg, &serial, optarg, rsp)) {
+                               fprintf(stderr, "## %s\n", emsg.c);
+                               librpz->client_detach(&client);
+                               return (1);
+                       }
+                       librpz->rsp_detach(&rsp);
+                       librpz->client_detach(&client);
+                       printf("%d\n", serial);
+                       return (0);
+#else
+                       INSIST(0);
+#endif
+
+               case 'w':
+                       seconds = strtod(optarg, &p);
+                       if (seconds <= 0 || *p != '\0') {
+                               fputs(USAGE, stderr);
+                               return (1);
+                       }
+                       usleep((int)(seconds*1000.0*1000.0));
+                       return (0);
+
+               default:
+                       fputs(USAGE, stderr);
+                       return (1);
+               }
+       }
+       fputs(USAGE, stderr);
+       return (1);
+}
+
+
+static isc_boolean_t
+link_dnsrps(librpz_emsg_t *emsg) {
+#ifdef USE_DNSRPS
+       librpz = librpz_lib_open(emsg, NULL, DNSRPS_LIBRPZ_PATH);
+       if (librpz == NULL)
+               return (ISC_FALSE);
+
+       return (ISC_TRUE);
+#else
+       snprintf(emsg->c, sizeof(emsg->c), "DNSRPS not configured");
+       return (ISC_FALSE);
+#endif
+}
diff --git a/bin/tests/system/rpz/dnsrpzd-license.conf b/bin/tests/system/rpz/dnsrpzd-license.conf
new file mode 100644 (file)
index 0000000..745c7c9
--- /dev/null
@@ -0,0 +1,10 @@
+zone isc.license.fastrpz.com {
+    masters port 53 {
+        KEY farsight_fastrpz_license 104.244.14.176;
+        KEY farsight_fastrpz_license 2620:11c:f008::176;
+    };
+};
+
+key farsight_fastrpz_license {
+    algorithm hmac-sha256; secret "f405d02b4c8af54855fcebc1";
+};
diff --git a/bin/tests/system/rpz/dnsrpzd.conf b/bin/tests/system/rpz/dnsrpzd.conf
new file mode 100644 (file)
index 0000000..de73945
--- /dev/null
@@ -0,0 +1,49 @@
+# dnsrpzd configuration.
+
+pid-file ../dnsrpzd.pid
+
+include ../dnsrpzd-license-cur.conf
+
+# configure NOTIFY and zone transfers
+port 5301;
+listen-on port 5301 { 10.53.0.3; };
+allow-notify { 10.53.0.0/24; };
+
+zone "bl0"             {type master; file "../ns5/bl.db"; };
+zone "bl1"             {type master; file "../ns5/bl.db"; };
+zone "bl2"             {type master; file "../ns5/bl.db"; };
+zone "bl3"             {type master; file "../ns5/bl.db"; };
+zone "bl4"             {type master; file "../ns5/bl.db"; };
+zone "bl5"             {type master; file "../ns5/bl.db"; };
+zone "bl6"             {type master; file "../ns5/bl.db"; };
+zone "bl7"             {type master; file "../ns5/bl.db"; };
+zone "bl8"             {type master; file "../ns5/bl.db"; };
+zone "bl9"             {type master; file "../ns5/bl.db"; };
+zone "bl10"            {type master; file "../ns5/bl.db"; };
+zone "bl11"            {type master; file "../ns5/bl.db"; };
+zone "bl12"            {type master; file "../ns5/bl.db"; };
+zone "bl13"            {type master; file "../ns5/bl.db"; };
+zone "bl14"            {type master; file "../ns5/bl.db"; };
+zone "bl15"            {type master; file "../ns5/bl.db"; };
+zone "bl16"            {type master; file "../ns5/bl.db"; };
+zone "bl17"            {type master; file "../ns5/bl.db"; };
+zone "bl18"            {type master; file "../ns5/bl.db"; };
+zone "bl19"            {type master; file "../ns5/bl.db"; };
+
+zone "bl"              {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-2"            {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-given"                {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-passthru"     {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-no-op"                {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-disabled"     {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-nodata"       {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-nxdomain"     {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-cname"                {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-wildcname"    {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-garden"       {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-drop"         {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-tcp-only"     {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl.tld2"         {type slave; masters port 5300 { 10.53.0.3; }; };
+
+zone "policy1"         {type slave; masters port 5300 { 10.53.0.6; }; };
+zone "policy2"         {type slave; masters port 5300 { 10.53.0.7; }; };
diff --git a/bin/tests/system/rpz/fastrpz.license b/bin/tests/system/rpz/fastrpz.license
new file mode 100644 (file)
index 0000000..634c553
--- /dev/null
@@ -0,0 +1,3 @@
+NAME='isc.license.fastrpz.com'
+KEY='hmac-sha256:farsight_fastrpz_license:f405d02b4c8af54855fcebc1'
+LSERVER=license1.fastrpz.com
index 1cb99b315c5b26a19ed3ab6b6ed983e8e361692a..d30551df4af9b86eb3b10361bfa019cffce42308 100644 (file)
@@ -43,4 +43,5 @@ zone "subsub.sub3.tld2."    {type master; file "tld2.db";};
 
 zone "tld2s."              {type master; file "tld2s.db";};
 
-zone "bl.tld2."                    {type master; file "bl.tld2.db"; notify yes; notify-delay 1;};
+zone "bl.tld2."                    {type master; file "bl.tld2.db";
+                               notify yes; notify-delay 0;};
index 30425bb38301ddaff517af2f6116a5fab8c1727f..3adb543fb286f00bd41694a8b31cde0e538aaa6f 100644 (file)
@@ -15,3 +15,5 @@ $TTL  300
 
 ; regression testing for some old crashes
 example.com     NS     example.org.
+
+domain.com     cname   foobar.com
index 5bb696b7c17fe9f9c1d8afa984dfd05ed73e5511..866d5feb66201da66bf5a3e255159d57e0329bcd 100644 (file)
@@ -21,7 +21,7 @@ options {
        session-keyfile "session.key";
        listen-on { 10.53.0.3; };
        listen-on-v6 { none; };
-       notify no;
+       notify yes;
        minimal-responses no;
 
        response-policy {
@@ -43,9 +43,17 @@ options {
        min-ns-dots 0
        qname-wait-recurse yes
        min-update-interval 0
+       nsdname-enable yes
+       nsip-enable yes
        ;
+
+       include "../dnsrps.conf";
+       also-notify { 10.53.0.3 port 5301; };
+       notify-delay 0;
 };
 
+logging { category rpz { default_debug; }; };
+
 key rndc_key {
        secret "1234abcd8765";
        algorithm hmac-sha256;
@@ -87,5 +95,5 @@ zone "bl-tcp-only."   {type master; file "bl-tcp-only.db";
 zone "bl.tld2."                {type slave; file "bl.tld2.db"; masters {10.53.0.2;};
                                request-ixfr no; masterfile-format text;};
 
-zone "crash1.tld2"     {type master; file "crash1";};
-zone "crash2.tld3."    {type master; file "crash2";};
+zone "crash1.tld2"     {type master; file "crash1"; notify no;};
+zone "crash2.tld3."    {type master; file "crash2"; notify no;};
index dd8cfa8ae13249cf68b23c19a4d9f8f4e2168348..22e56cb0eda751d7baed9aae27d55693833e4dd4 100644 (file)
@@ -22,12 +22,14 @@ options {
        listen-on { 10.53.0.5; };
        listen-on-v6 { none; };
        ixfr-from-differences yes;
-       notify-delay 1;
+       notify-delay 0;
        notify yes;
        minimal-responses no;
 
        # turn rpz on or off
        include "rpz-switch";
+
+       include "../dnsrps-slave.conf";
 };
 
 key rndc_key {
index 00a5e9b00a8e0c0f9c40ca71f7fe25ea9a56cd3e..432b3e6de0b80792fcd0db28ce8bf02c35d146e8 100644 (file)
@@ -25,3 +25,5 @@ example.tld5. NS      ns
                NS      ns1
 ns             A       10.53.0.5
 ns1            A       10.53.0.5
+
+as-ns  TXT     "rewritten with ip-as-ns and qname-as-ns"
index e7015e7f1faf5970999162df6c48e1674f6aeb77..064b3cdbaeb580321d65c936d25b77e569bd3791 100644 (file)
@@ -20,9 +20,17 @@ options {
        forwarders { 10.53.0.3; };
        minimal-responses no;
 
-       response-policy { zone "policy1" min-update-interval 0; };
+       response-policy {
+           zone "policy1" min-update-interval 0;
+       } qname-wait-recurse yes
+       nsip-enable yes
+       nsdname-enable yes;
+
+       include "../dnsrps-slave.conf";
 };
 
+logging { category rpz { default_debug; }; };
+
 key rndc_key {
        secret "1234abcd8765";
        algorithm hmac-sha256;
@@ -38,5 +46,7 @@ zone "policy1" {
        type slave;
        masters { 10.53.0.5; };
        file "empty.db";
+       also-notify { 10.53.0.3 port 5301; };
+       notify-delay 0;
        allow-transfer  { any; };
 };
index 54627736b8e6692e6de26b9f19349179e38c42c3..379eff1dec51c81af93b34c093d477280da57190 100644 (file)
@@ -18,11 +18,18 @@ options {
        listen-on-v6 { none; };
        minimal-responses no;
 
-       response-policy { zone "policy2"; }
-               qname-wait-recurse no
-               min-update-interval 0;
+       response-policy {
+           zone "policy2";
+       } qname-wait-recurse no
+       nsip-enable yes
+       nsdname-enable yes
+        min-update-interval 0;
+
+       include "../dnsrps-slave.conf";
 };
 
+logging { category rpz { default_debug; }; };
+
 key rndc_key {
        secret "1234abcd8765";
        algorithm hmac-sha256;
@@ -38,6 +45,8 @@ zone "policy2" {
        type slave;
        masters { 10.53.0.5; };
        file "policy2.db";
+       also-notify { 10.53.0.3 port 5301; };
+       notify-delay 0;
        allow-transfer  { any; };
        request-ixfr no; // force axfr on rndc reload
 };
index 5a87d15a4207e2ef2cfb50d4307aefc079aaa02a..2cadd168a1410c336d96b116ef6ccad70aee7735 100644 (file)
@@ -6,6 +6,8 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# touch dnsrps-off to not test with DNSRPS
+
 set -e
 
 SYSTEMTESTTOP=..
@@ -13,7 +15,28 @@ SYSTEMTESTTOP=..
 
 QPERF=`$SHELL qperf.sh`
 
-$SHELL clean.sh
+USAGE="$0: [-Dx]"
+DEBUG=
+while getopts "Dx" c; do
+    case $c in
+       x) set -x; DEBUG=-x;;
+        D) TEST_DNSRPS="-D";;
+       *) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+$SHELL clean.sh $DEBUG
+
+# decide whether to test DNSRPS
+# Note that dnsrps.conf and dnsrps-slave.conf are included in named.conf
+# and differ from dnsrpz.conf which is used by dnsrpzd.
+$SHELL ../rpz/ckdnsrps.sh -A $TEST_DNSRPS $DEBUG
+test -z "`grep 'dnsrps-enable yes' dnsrps.conf`" && TEST_DNSRPS=
 
 # set up test policy zones.
 #   bl is the main test zone
@@ -48,9 +71,11 @@ 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
+    qname-wait-recurse no
+    nsip-enable yes
+    nsdname-enable yes
     max-policy-ttl 90
     break-dnssec yes
-    qname-wait-recurse no
     ;
 EOF
 
@@ -110,3 +135,11 @@ $PERL -e 'for ($cnt = $val = 1; $cnt <= 3000; ++$cnt) {
 cp ns2/bl.tld2.db.in ns2/bl.tld2.db
 cp ns5/empty.db.in ns5/empty.db
 cp ns5/empty.db.in ns5/policy2.db
+
+# Run dnsrpzd to get the license and prime the static policy zones
+if test -n "$TEST_DNSRPS"; then
+   DNSRPZD="`../rpz/dnsrps -p`"
+   cd ns3
+   "$DNSRPZ" -D../dnsrpzd.rpzf -S../dnsrpzd.sock -C../dnsrpzd.conf \
+             -w 0 -dddd -L stdout >./dnsrpzd.run 2>&1
+fi
index 6d87d7a17320c5ae1ff02574132063bdf1197f6d..0cdc7524b9504d659fb267033fc831363259ca50 100644 (file)
@@ -36,4 +36,7 @@ update add  a4-1.tld2.bl.             300 A 12.12.12.12
 ; prefer policy for largest NS name
 update add  ns.sub3.tld2.rpz-nsdname.bl.       300 A   127.0.0.1
 update add  ns.subsub.sub3.tld2.rpz-nsdname.bl. 300 A  127.0.0.2
+
+; ip-as-qname rewrites all of tld5
+update add  ns.tld5.bl.                        300 A 12.12.12.12
 send
index 4129be626ca852be6dc233439cdf2a868fd945fa..beca0d3bf0eba0060aca9f5a89be973c96be4153 100644 (file)
@@ -25,4 +25,7 @@ update add  24.0.0.168.192.rpz-ip.bl  300 CNAME 24.0.0.168.192.
 ;
 ; prefer NSIP policy to NSDNAME policy
 update add  ns.tld2.rpz-nsdname.bl.    300 CNAME 10.0.0.1
+
+; ip-as-ns rewrites all of tld5
+update add  32.5.0.53.10.rpz-ip.bl.    300 A 12.12.12.12
 send
index 9112b3230a6630884888ccaf5a8140faa2f34aab..38e5a6a55b7a7fd328b16977eb4d368e0a484b90 100644 (file)
@@ -1,14 +1,16 @@
+#!/bin/sh
+#
 # Copyright (C) 2011-2017  Internet Systems Consortium, Inc. ("ISC")
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-# $Id$
-
-
 # test response policy zones (RPZ)
 
+# touch dnsrps-off to not test with DNSRPS
+# touch dnsrps-only to not test with classic RPZ
+
 SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
@@ -22,13 +24,20 @@ ns6=$ns.6           # a forwarding server
 ns7=$ns.7              # another rewriting resolver
 
 HAVE_CORE=
-SAVE_RESULTS=
 
+status=0
+
+DEBUG=
+SAVE_RESULTS=
+DNSRPS_TEST_MODE=       # "" to test with and then without DNSRPS
+ARGS=
 
-USAGE="$0: [-x]"
-while getopts "x" c; do
+USAGE="$0: [-xS] [-D {1,2}]"
+while getopts "xSD:" c; do
     case $c in
-       x) set -x;;
+       x) set -x; DEBUG=-x; ARGS="$ARGS -x";;
+       S) SAVE_RESULTS=-S; ARGS="$ARGS -S";;
+       D) DNSRPS_TEST_MODE="$OPTARG";;  # with or without DNSRPZ
        *) echo "$USAGE" 1>&2; exit 1;;
     esac
 done
@@ -48,8 +57,51 @@ comment () {
     fi
 }
 
+DNSRPSCMD=./dnsrps
 RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
 
+# Run the tests twice, first without DNSRPS and then with if it is available
+if [ -z "$DNSRPS_TEST_MODE" ]; then
+    if [ -e dnsrps-only ]; then
+        echo "I:'dnsrps-only' found: skipping native RPZ sub-test"
+    else
+        echo "I:running native RPZ sub-test"
+       $SHELL ./$0 -D1 $ARGS || status=1
+    fi
+
+    if [ -e dnsrps-off ]; then
+       echo "I:'dnsrps-off' found: skipping DNSRPS sub-test"
+    else
+       echo "I:attempting to configure servers with DNSRPS..."
+       $PERL $SYSTEMTESTTOP/stop.pl .
+       $SHELL ./setup.sh -D $DEBUG
+       sed -n 's/^## /I:/p' dnsrps.conf
+       if grep '^#fail' dnsrps.conf >/dev/null; then
+           echo "I:exit status: 1"
+           exit 1
+       fi
+       if test -z "`grep '^#skip' dnsrps.conf`"; then
+           echo "I:running DNSRPS sub-test"
+           $PERL $SYSTEMTESTTOP/start.pl --noclean --restart .
+           $SHELL ./$0 $ARGS -D2 || status=1
+        else
+            echo "I:DNSRPS sub-test skipped"
+       fi
+    fi
+
+    echo "I:exit status: $status"
+    exit $status
+fi
+
+if test -x $DNSRPSCMD; then
+    # speed up the many delays for dnsrpzd by waiting only 0.1 seconds
+    WAIT_CMD="$DNSRPSCMD -w 0.1"
+    TEN_SECS=100
+else
+    WAIT_CMD="sleep 1"
+    TEN_SECS=10
+fi
+
 digcmd () {
     if test "$1" = TCP; then
        shift
@@ -57,9 +109,9 @@ digcmd () {
     # 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=2 +tries=1 -p 5300 $*" |   \
-           sed -e "/@/!s/.*/& @$ns3/"                          \
-               -e '/-b/!s/@\([^ ]*\)/@\1 -b\1/'                \
+    digcmd_args=`echo "+nocookie +noadd +time=2 +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
@@ -69,6 +121,7 @@ digcmd () {
 GROUP_NM=
 TEST_NUM=0
 make_dignm () {
+    TEST_NUM=`expr $TEST_NUM : '\([0-9]*\).*'`     # trim '+' characters
     TEST_NUM=`expr $TEST_NUM + 1`
     DIGNM=dig.out$GROUP_NM-$TEST_NUM
     while test -f $DIGNM; do
@@ -83,6 +136,72 @@ setret () {
     echo "$*"
 }
 
+# set $SN to the SOA serial number of a zone
+# $1=domain  $2=DNS server and client IP address
+get_sn() {
+    SOA=`$DIG -p 5300 +short +norecurse soa "$1" "@$2" "-b$2"`
+    SN=`expr "$SOA" : '[^ ]* [^ ]* \([^ ]*\) .*'`
+    test "$SN" != "" && return
+    echo "I:no serial number from \`dig -p 5300 soa $1 @$2\` in \"$SOA\""
+    exit 1
+}
+
+get_sn_fast () {
+    RSN=`$DNSRPSCMD -n "$1"`
+    #echo "dnsrps serial for $1 is $RSN"
+    if test -z "$RSN"; then
+       echo "I:dnsrps failed to get SOA serial number for $1"
+       exit 1
+    fi
+}
+
+# check that dnsrpzd has loaded its zones
+# $1=domain  $2=DNS server IP address
+FZONES=`sed -n -e 's/^zone "\(.*\)".*\(10.53.0..\).*/Z=\1;M=\2/p' dnsrpzd.conf`
+dnsrps_loaded() {
+    test "$DNSRPS_TEST_MODE" = 2 || return
+    n=0
+    for V in $FZONES; do
+       eval "$V"
+       get_sn $Z $M
+       while true; do
+           get_sn_fast "$Z"
+           if test "$SN" -eq "0$RSN"; then
+               #echo "$Z @$M serial=$SN"
+               break
+           fi
+           n=`expr $n + 1`
+           if test "$n" -gt $TEN_SECS; then
+               echo "I:dnsrps serial for $Z is $RSN instead of $SN"
+               exit 1
+           fi
+           $WAIT_CMD
+       done
+    done
+}
+
+# check the serial number in an SOA to ensure that a policy zone has
+#      been (re)loaded
+# $1=serial number  $2=domain  $3=DNS server
+ck_soa() {
+    n=0
+    while true; do
+       if test "$DNSRPS_TEST_MODE" = 2; then
+           get_sn_fast "$2"
+           test "$RSN" -eq "$1" && return
+       else
+           get_sn "$2" "$3"
+           test "$SN" -eq "$1" && return
+       fi
+       n=`expr $n + 1`
+       if test "$n" -gt $TEN_SECS; then
+           echo "I:got serial number \"$SN\" instead of \"$1\" from $2 @$3"
+           return
+       fi
+       $WAIT_CMD
+    done
+}
+
 # (re)load the reponse policy zones with the rules in the file $TEST_FILE
 load_db () {
     if test -n "$TEST_FILE"; then
@@ -118,6 +237,7 @@ restart () {
     fi
     $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns$1
     load_db
+    dnsrps_loaded
 }
 
 # $1=server and irrelevant args  $2=error message
@@ -179,6 +299,7 @@ start_group () {
     else
        GROUP_NM=
     fi
+    dnsrps_loaded
     TEST_NUM=0
 }
 
@@ -189,6 +310,7 @@ end_group () {
        TEST_FILE=
     fi
     ckalive $ns3 "I:failed; ns3 server crashed and restarted"
+    dnsrps_loaded
     GROUP_NM=
 }
 
@@ -257,7 +379,7 @@ addr () {
     digcmd $2 >$DIGNM
     #ckalive "$2" "I:server crashed by 'dig $2'" || return 1
     ADDR_ESC=`echo "$ADDR" | sed -e 's/\./\\\\./g'`
-    ADDR_TTL=`sed -n -e "s/^[-.a-z0-9]\{1,\}   *\([0-9]*\)     IN      AA*     ${ADDR_ESC}\$/\1/p" $DIGNM`
+    ADDR_TTL=`sed -n -e "s/^[-.a-z0-9]\{1,\}[   ]*\([0-9]*\)   IN      AA*     ${ADDR_ESC}\$/\1/p" $DIGNM`
     if test -z "$ADDR_TTL"; then
        setret "I:'dig $2' wrong; no address $ADDR record in $DIGNM"
        return 1
@@ -306,9 +428,6 @@ drop () {
 digcmd nonexistent @$ns2 >proto.nxdomain
 digcmd txt-only.tld2 @$ns2 >proto.nodata
 
-
-status=0
-
 start_group "QNAME rewrites" test1
 nochange .                             # 1 do not crash or rewrite root
 nxdomain a0-1.tld2                     # 2
@@ -353,29 +472,28 @@ ckstats $ns5 test1 ns5 1
 ckstats $ns6 test1 ns6 0
 
 start_group "NXDOMAIN/NODATA action on QNAME trigger" test1
-nxdomain a0-1.tld2 @$ns6                   # 1
-nodata a3-1.tld2 @$ns6                     # 2
-nodata a3-2.tld2 @$ns6                     # 3 nodata at DNAME itself
-nxdomain a4-2.tld2 @$ns6                   # 4 rewrite based on CNAME target
-nxdomain a4-2-cname.tld2 @$ns6             # 5
-nodata a4-3-cname.tld2 @$ns6               # 6
+nxdomain a0-1.tld2 @$ns6                  # 1
+nodata a3-1.tld2 @$ns6                    # 2
+nodata a3-2.tld2 @$ns6                    # 3 nodata at DNAME itself
+nxdomain a4-2.tld2 @$ns6                  # 4 rewrite based on CNAME target
+nxdomain a4-2-cname.tld2 @$ns6            # 5
+nodata a4-3-cname.tld2 @$ns6              # 6
 addr 12.12.12.12  "a4-1.sub1.tld2 @$ns6"   # 7 A replacement
 addr 12.12.12.12  "a4-1.sub2.tld2 @$ns6"   # 8 A replacement with wildcard
-addr 127.4.4.1    "a4-4.tld2 @$ns6"        # 9 prefer 1st conflicting QNAME zone
+addr 127.4.4.1    "a4-4.tld2 @$ns6"       # 9 prefer 1st conflicting QNAME zone
 addr 12.12.12.12  "nxc1.sub1.tld2 @$ns6"   # 10 replace NXDOMAIN w/ CNAME
 addr 12.12.12.12  "nxc2.sub1.tld2 @$ns6"   # 11 replace NXDOMAIN w/ CNAME chain
-addr 127.6.2.1    "a6-2.tld2 @$ns6"        # 12
-addr 56.56.56.56  "a3-6.tld2 @$ns6"        # 13 wildcard CNAME
+addr 127.6.2.1    "a6-2.tld2 @$ns6"       # 12
+addr 56.56.56.56  "a3-6.tld2 @$ns6"       # 13 wildcard CNAME
 addr 57.57.57.57  "a3-7.sub1.tld2 @$ns6"   # 14 wildcard CNAME
 addr 127.0.0.16   "a4-5-cname3.tld2 @$ns6" # 15 CNAME chain
 addr 127.0.0.17   "a4-6-cname3.tld2 @$ns6" # 16 stop short in CNAME chain
-nxdomain c1.crash2.tld3 @$ns6              # 17 assert in rbtdb.c
-nxdomain a0-1.tld2 +dnssec @$ns6           # 18 simple DO=1 without sigs
+nxdomain c1.crash2.tld3 @$ns6             # 17 assert in rbtdb.c
+nxdomain a0-1.tld2 +dnssec @$ns6          # 18 simple DO=1 without sigs
 nxdomain a0-1s-cname.tld2s  +dnssec @$ns6  # 19
-drop a3-8.tld2 any @$ns6                   # 20 drop
-
+drop a3-8.tld2 any @$ns6                  # 20 drop
 end_group
-ckstatsrange $ns3 test1 ns3 22 28
+ckstatsrange $ns3 test1 ns3 22 30
 ckstats $ns5 test1 ns5 0
 ckstats $ns6 test1 ns6 0
 
@@ -399,25 +517,13 @@ nxdomain c2.crash2.tld3                   # 16 assert in rbtdb.c
 addr 127.0.0.17 "a4-4.tld2 -b $ns1"    # 17 client-IP address trigger
 nxdomain a7-1.tld2                     # 18 slave policy zone (RT34450)
 cp ns2/blv2.tld2.db.in ns2/bl.tld2.db
-$RNDCCMD 10.53.0.2 reload bl.tld2
-goodsoa="rpz.tld2. hostmaster.ns.tld2. 2 3600 1200 604800 60"
-for i in 0 1 2 3 4 5 6 7 8 9 10
-do
-       soa=`$DIG -p 5300 +short soa bl.tld2 @10.53.0.3 -b10.53.0.3`
-       test "$soa" = "$goodsoa" && break
-       sleep 1
-done
+$RNDCCMD $ns2 reload bl.tld2
+ck_soa 2 bl.tld2 $ns3
 nochange a7-1.tld2                     # 19 PASSTHRU
-sleep 1        # ensure that a clock tick has occured so that the reload takes effect
+sleep 1        # ensure that a clock tick has occured so that named will do the reload
 cp ns2/blv3.tld2.db.in ns2/bl.tld2.db
-goodsoa="rpz.tld2. hostmaster.ns.tld2. 3 3600 1200 604800 60"
-$RNDCCMD 10.53.0.2 reload bl.tld2
-for i in 0 1 2 3 4 5 6 7 8 9 10
-do
-       soa=`$DIG -p 5300 +short soa bl.tld2 @10.53.0.3 -b10.53.0.3`
-       test "$soa" = "$goodsoa" && break
-       sleep 1
-done
+$RNDCCMD $ns2 reload bl.tld2
+ck_soa 3 bl.tld2 $ns3
 nxdomain a7-1.tld2                     # 20 slave policy zone (RT34450)
 end_group
 ckstats $ns3 test2 ns3 12
@@ -438,47 +544,53 @@ nochange a5-1-2.tld2
 end_group
 ckstats $ns3 'radix tree deletions' ns3 0
 
-if $FEATURETEST --rpz-nsdname; then
-    # these tests assume "min-ns-dots 0"
-    start_group "NSDNAME rewrites" test3
-    nochange a3-1.tld2                 # 1
-    nochange a3-1.tld2     +dnssec     # 2 this once caused problems
-    nxdomain a3-1.sub1.tld2            # 3 NXDOMAIN *.sub1.tld2 by NSDNAME
-    nxdomain a3-1.subsub.sub1.tld2
-    nxdomain a3-1.subsub.sub1.tld2 -tany
-    addr 12.12.12.12 a4-2.subsub.sub2.tld2 # 6 walled garden for *.sub2.tld2
-    nochange a3-2.tld2.                        # 7 exempt rewrite by name
-    nochange a0-1.tld2.                        # 8 exempt rewrite by address block
-    addr 12.12.12.12 a4-1.tld2         # 9 prefer QNAME policy to NSDNAME
-    addr 127.0.0.1 a3-1.sub3.tld2      # 10 prefer policy for largest NSDNAME
-    addr 127.0.0.2 a3-1.subsub.sub3.tld2
-    nxdomain xxx.crash1.tld2           # 12 dns_db_detachnode() crash
-    end_group
-    ckstats $ns3 test3 ns3 7
+# these tests assume "min-ns-dots 0"
+start_group "NSDNAME rewrites" test3
+nochange a3-1.tld2                     # 1
+nochange a3-1.tld2         +dnssec     # 2 this once caused problems
+nxdomain a3-1.sub1.tld2                        # 3 NXDOMAIN *.sub1.tld2 by NSDNAME
+nxdomain a3-1.subsub.sub1.tld2
+nxdomain a3-1.subsub.sub1.tld2 -tany
+addr 12.12.12.12 a4-2.subsub.sub2.tld2 # 6 walled garden for *.sub2.tld2
+nochange a3-2.tld2.                    # 7 exempt rewrite by name
+nochange a0-1.tld2.                    # 8 exempt rewrite by address block
+addr 12.12.12.12 a4-1.tld2             # 9 prefer QNAME policy to NSDNAME
+addr 127.0.0.1 a3-1.sub3.tld2          # 10 prefer policy for largest NSDNAME
+addr 127.0.0.2 a3-1.subsub.sub3.tld2
+nxdomain xxx.crash1.tld2               # 12 dns_db_detachnode() crash
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+    addr 12.12.12.12 as-ns.tld5.       # 13 qname-as-ns
+fi
+end_group
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+    ckstats $ns3 test3 ns3 8
 else
-    echo "I:NSDNAME not checked; named configured with --disable-rpz-nsdname"
+    ckstats $ns3 test3 ns3 7
+fi
+
+# these tests assume "min-ns-dots 0"
+start_group "NSIP rewrites" test4
+nxdomain a3-1.tld2                     # 1 NXDOMAIN for all of tld2
+nochange a3-2.tld2.                    # 2 exempt rewrite by name
+nochange a0-1.tld2.                    # 3 exempt rewrite by address block
+nochange a3-1.tld4                     # 4 different NS IP address
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+    addr 12.12.12.12 as-ns.tld5.       # 5 ip-as-ns
 fi
+end_group
 
-if $FEATURETEST --rpz-nsip; then
-    # these tests assume "min-ns-dots 0"
-    start_group "NSIP rewrites" test4
-    nxdomain a3-1.tld2                 # 1 NXDOMAIN for all of tld2
-    nochange a3-2.tld2.                        # 2 exempt rewrite by name
-    nochange a0-1.tld2.                        # 3 exempt rewrite by address block
-    nochange a3-1.tld4                 # 4 different NS IP address
-    end_group
-
-    start_group "walled garden NSIP rewrites" test4a
-    addr 41.41.41.41 a3-1.tld2         # 1 walled garden for all of tld2
-    addr 2041::41   'a3-1.tld2 AAAA'   # 2 walled garden for all of tld2
-    here a3-1.tld2 TXT <<'EOF'         # 3 text message for all of tld2
+start_group "walled garden NSIP rewrites" test4a
+addr 41.41.41.41 a3-1.tld2             # 1 walled garden for all of tld2
+addr 2041::41   'a3-1.tld2 AAAA'       # 2 walled garden for all of tld2
+here a3-1.tld2 TXT <<'EOF'             # 3 text message for all of tld2
     ;; status: NOERROR, x
     a3-1.tld2.     x   IN      TXT   "NSIP walled garden"
 EOF
-    end_group
-    ckstats $ns3 test4 ns3 4
+end_group
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+    ckstats $ns3 test4 ns3 5
 else
-    echo "I:NSIP not checked; named configured with --disable-rpz-nsip"
+    ckstats $ns3 test4 ns3 4
 fi
 
 # policies in ./test5 overridden by response-policy{} in ns3/named.conf
@@ -530,7 +642,6 @@ end_group
 ckstats $ns3 bugs ns3 8
 
 
-
 # superficial test for major performance bugs
 QPERF=`sh qperf.sh`
 if test -n "$QPERF"; then
@@ -538,10 +649,10 @@ if test -n "$QPERF"; then
        date "+I:${TS}checking performance $1"
        # Dry run to prime everything
        comment "before dry run $1"
+       $RNDCCMD $ns5 notrace
        $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 \
@@ -558,7 +669,6 @@ if test -n "$QPERF"; then
     # get qps with rpz
     perf 'with RPZ' rpz 'NOERROR:2900 NXDOMAIN:100 '
     RPZ=`trim rpz`
-
     # turn off rpz and measure qps again
     echo "# RPZ off" >ns5/rpz-switch
     RNDCCMD_OUT=`$RNDCCMD $ns5 reload`
@@ -583,6 +693,27 @@ else
     echo "I:performance not checked; queryperf not available"
 fi
 
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+    echo "I:checking that dnsrpzd is automatically restarted"
+    OLD_PID=`cat dnsrpzd.pid`
+    $KILL "$OLD_PID"
+    n=0
+    while true; do
+       NEW_PID=`cat dnsrpzd.pid 2>/dev/null`
+       if test -n "$NEW_PID" -a "0$OLD_PID" -ne "0$NEW_PID"; then
+           #echo "OLD_PID=$OLD_PID  NEW_PID=$NEW_PID"
+           break;
+       fi
+       $DIG -p 5300 +short +norecurse a0-1.tld2 @$ns3 >/dev/null
+       n=`expr $n + 1`
+       if test "$n" -gt $TEN_SECS; then
+           setret "I:dnsrpzd did not restart"
+           break
+       fi
+       $WAIT_CMD
+    done
+fi
+
 
 # restart the main test RPZ server to see if that creates a core file
 if test -z "$HAVE_CORE"; then
@@ -604,7 +735,9 @@ $DIG +noall +answer -p 5300 @$ns3 any a3-2.tld2 > dig.out.any
 ttl=`awk '/a3-2 tld2 text/ {print $2}' dig.out.any`
 if test ${ttl:=0} -eq 0; then setret I:failed; fi
 
-echo "I:checking rpz updates/transfers with parent nodes added after children"
+
+echo "I:checking rpz updates/transfers with parent nodes added after children" \
+    | tr -d '\n'
 # regression test for RT #36272: the success condition
 # is the slave server not crashing.
 nsd() {
@@ -617,26 +750,40 @@ send
 EOF
     sleep 2
 }
-
 for i in 1 2 3 4 5; do
     nsd $ns5 add example.com.policy1. '*.example.com.policy1.'
+    echo . | tr -d '\n'
     nsd $ns5 delete example.com.policy1. '*.example.com.policy1.'
+    echo . | tr -d '\n'
 done
 for i in 1 2 3 4 5; do
     nsd $ns5 add '*.example.com.policy1.' example.com.policy1.
+    echo . | tr -d '\n'
     nsd $ns5 delete '*.example.com.policy1.' example.com.policy1.
+    echo . | tr -d '\n'
 done
+echo
+
 
-echo "I:checking that going from a empty policy zone works"
+echo "I:checking that going from an empty policy zone works"
 nsd $ns5 add '*.x.servfail.policy2.' x.servfail.policy2.
 sleep 1
 $RNDCCMD $ns7 reload policy2
 $DIG z.x.servfail -p 5300 @$ns7 > dig.out.ns7
-grep NXDOMAIN dig.out.ns7 > /dev/null || setret I:failed;
-
-echo "I:checking rpz with delegation fails correctly"
-$DIG -p 5300 @$ns3 ns example.com > dig.out.delegation
-grep "status: SERVFAIL" dig.out.delegation > /dev/null || setret "I:failed"
+grep NXDOMAIN dig.out.ns7 > /dev/null || setret I:failed
+
+# dnsrps does not allow NS RRs in policy zones, so this check
+# with dnsrps results in no rewriting.
+if [ "$DNSRPS_TEST_MODE" = 1 ]; then
+    echo "I:checking rpz with delegation fails correctly"
+    $DIG -p 5300 @$ns3 ns example.com > dig.out.delegation
+    grep "status: SERVFAIL" dig.out.delegation > /dev/null || setret "I:failed"
+fi
 
-echo "I:exit status: $status"
+[ $status -ne 0 ] && pf=fail || pf=pass
+case $DNSRPS_TEST_MODE in
+        1) echo "I:status (native RPZ sub-test): $status ($pf)";;
+        2) echo "I:status (DNSRPS sub-test): $status ($pf)";;
+    *) echo "I:invalid test mode";;
+esac
 [ $status -eq 0 ] || exit 1
index 7800df5ef18b4b90f8bb7524294e2c3690b333ee..3c23011d5fee42ee2ce3470a15895a7b7d7ad3f7 100644 (file)
@@ -9,9 +9,11 @@
 rm -f dig.out.*
 rm -f ns*/named.lock
 rm -f ns*/named.memstats
-rm -f ns*/named.run
+rm -f ns*/*.run
 rm -f ns2/*.local
 rm -f ns2/*.queries
 rm -f ns2/named.[0-9]*.conf
 rm -f ns2/named.conf
 rm -f ns3/named.conf
+rm -f ns*/*core *core
+rm -f dnsrps*.conf dnsrpzd*
index 2b44084a641da1251b84e89f954435720d37fd8f..012e6cb2d78dbc4fb1b89756e293ff86c6041855 100644 (file)
@@ -11,6 +11,7 @@ options {
        notify-source 10.53.0.1;
        transfer-source 10.53.0.1;
        port 5300;
+       session-keyfile "session.key";
        pid-file "named.pid";
        listen-on { 10.53.0.1; };
        listen-on-v6 { none; };
index 6c5376b3d3882e8ffc76c4264eda66533bff5fb9..3245032ad5fcf13de58d3c0dab874ea6ec5df024 100644 (file)
@@ -19,7 +19,9 @@ view "recursive" {
     response-policy {
        zone "clientip1";
        zone "clientip2";
-    } qname-wait-recurse no;
+    } qname-wait-recurse no
+       nsdname-enable yes
+       nsip-enable yes;
 
     # policy zones to be tested
     zone "clientip1" { type master; file "db.clientip1"; };
index 2a42981688187514f9a8342f078cbd62a8c78476..a987854c984b34c6f40852d6c8addd05b2d5320d 100644 (file)
@@ -20,7 +20,9 @@ view "recursive" {
     # policy configuration to be tested
     response-policy {
        zone "clientip21";
-    } qname-wait-recurse no;
+    } qname-wait-recurse no
+       nsdname-enable yes
+       nsip-enable yes;
 
     # policy zones to be tested
     zone "clientip21" { type master; file "db.clientip21"; };
index 364d45c93bfcc5625f40cff88bd5d577e928dae5..09608bf68f660d275222b4d0feb7f5ba19eedc07 100644 (file)
@@ -13,11 +13,15 @@ options {
        notify-source 10.53.0.2;
        transfer-source 10.53.0.2;
        port 5300;
+       session-keyfile "session.key";
        pid-file "named.pid";
        listen-on { 10.53.0.2; };
        listen-on-v6 { none; };
        recursion yes;
        querylog yes;
+
+       # let ns3 start dnsrpzd
+       include "../dnsrps-slave.conf";
 };
 
 key rndc_key {
index 62a82d82dd32a07e33b284776387a862767a1803..52b4b4fa3af6dfd0dc4d33ab81a9ce035232d240 100644 (file)
@@ -20,7 +20,9 @@ view "recursive" {
        zone "log1" log no;
        zone "log2" log yes;
        zone "log3"; # missing log clause
-    } qname-wait-recurse no;
+    } qname-wait-recurse no
+       nsdname-enable yes
+       nsip-enable yes;
 
     # policy zones to be tested
     zone "log1" { type master; file "db.log1"; };
index 0174745c190b10470a2f269edaae95dee35677c3..cc73a5fae1d99d99295577fc58f67646fc6ecf3a 100644 (file)
@@ -18,7 +18,9 @@ view "recursive" {
     # policy configuration to be tested
     response-policy {
        zone "wildcard1" policy NXDOMAIN;
-    };
+    } qname-wait-recurse yes
+       nsdname-enable yes
+       nsip-enable yes;
 
     # policy zones to be tested
     zone "wildcard1" { type master; file "db.wildcard1"; };
index 0547fc251ee15c405d843fb820f144cfd127e0ad..f20a77e606cbe609c7513ba27d1963283daddf56 100644 (file)
@@ -19,7 +19,9 @@ view "recursive" {
     response-policy {
        zone "wildcard2a" policy NXDOMAIN;
        zone "wildcard2b" policy NXDOMAIN;
-    };
+    } qname-wait-recurse yes
+       nsdname-enable yes
+       nsip-enable yes;
 
     # policy zones to be tested
     zone "wildcard2a" { type master; file "db.wildcard2a"; };
index 27a0e991688617ba0f5bd5eaa4a2c6beb97255db..801d7479a3af35e3670d4adf3b163d78ed1b63a1 100644 (file)
@@ -18,7 +18,9 @@ view "recursive" {
     # policy configuration to be tested
     response-policy {
        zone "wildcard3" policy NXDOMAIN;
-    };
+    } qname-wait-recurse yes
+       nsdname-enable yes
+       nsip-enable yes;
 
     # policy zones to be tested
     zone "wildcard3" { type master; file "db.wildcard3"; };
index ba241971e6e7a122e9c45b7a57e7131b86e3fdea..fada50b98c0263274194331c5549d7fea15c2270 100644 (file)
@@ -17,11 +17,17 @@ options {
        notify-source 10.53.0.3;
        transfer-source 10.53.0.3;
        port 5300;
+       session-keyfile "session.key";
        pid-file "named.pid";
        listen-on { 10.53.0.3; };
        listen-on-v6 { none; };
        recursion yes;
-       response-policy { zone "policy"; };
+       response-policy { zone "policy"; }
+               qname-wait-recurse yes
+               nsip-enable yes
+               nsdname-enable yes;
+
+       include "../dnsrps.conf";
 };
 
 zone "policy" { type master; file "policy.db"; };
index 63f64ea7400dd78bb0003cc401118a2c5e5d5756..7d3292b8fe8f234bca940ead99ab35d070fc5e00 100644 (file)
@@ -21,7 +21,12 @@ options {
        listen-on { 10.53.0.3; };
        listen-on-v6 { none; };
        recursion yes;
-       response-policy { zone "policy"; } nsip-wait-recurse no;
+       response-policy { zone "policy"; } nsip-wait-recurse no
+               qname-wait-recurse yes
+               nsip-enable yes
+               nsdname-enable yes;
+
+       include "../dnsrps.conf";
 };
 
 zone "policy" { type master; file "policy.db"; };
index 33cb012b6df8107c07ea744dbd4774d1dd0633c4..d679aae283ea8b93d572b043be91370199233c90 100644 (file)
@@ -13,6 +13,7 @@ options {
        notify-source 10.53.0.4;
        transfer-source 10.53.0.4;
        port 5300;
+       session-keyfile "session.key";
        pid-file "named.pid";
        listen-on { 10.53.0.4; };
        listen-on-v6 { none; };
index 50903bc54f55291c72a96480dafa3aeda8e92a87..84b419bf60f65f3e06d334b6f35fec59e9023e43 100644 (file)
@@ -9,13 +9,4 @@
 SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
-ret=0
-$FEATURETEST --rpz-nsdname || ret=1
-$FEATURETEST --rpz-nsip || ret=1
-
-if [ $ret != 0 ]; then
-    echo "I:This test requires NSIP AND NSDNAME support in RPZ." >&2
-    exit 1
-fi
-
 exec $SHELL ../testcrypto.sh
index e3972b3f70e4f2e99f78fc302843c89174254456..fc94134f7b1e8d8a39d5366ca4bdc53b4ad22fd1 100644 (file)
@@ -6,9 +6,52 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# touch dnsrps-off to not test with DNSRPS
+
+set -e
+
 SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
-perl testgen.pl
+USAGE="$0: [-xD]"
+DEBUG=
+while getopts "xD" c; do
+    case $c in
+       x) set -x; DEBUG=-x;;
+        D) TEST_DNSRPS="-D";;
+       *) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+$SHELL clean.sh $DEBUG
+
+$PERL testgen.pl
 cp -f ns2/named.default.conf ns2/named.conf
 cp -f ns3/named1.conf ns3/named.conf
+
+# decide whether to test DNSRPS
+$SHELL ../rpz/ckdnsrps.sh $TEST_DNSRPS $DEBUG
+test -z "`grep 'dnsrps-enable yes' dnsrps.conf`" && TEST_DNSRPS=
+
+CWD=`pwd`
+cat <<EOF >dnsrpzd.conf
+PID-FILE $CWD/dnsrpzd.pid;
+
+include $CWD/dnsrpzd-license-cur.conf
+
+zone "policy" { type master; file "`pwd`/ns3/policy.db"; };
+EOF
+sed -n -e 's/^ *//' -e "/zone.*.*master/s@file \"@&$CWD/ns2/@p" ns2/*.conf \
+    >>dnsrpzd.conf
+
+# Run dnsrpzd to get the license and prime the static policy zones
+if test -n "$TEST_DNSRPS"; then
+    DNSRPZD="`../rpz/dnsrps -p`"
+    "$DNSRPZD" -D./dnsrpzd.rpzf -S./dnsrpzd.sock -C./dnsrpzd.conf \
+               -w 0 -dddd -L stdout >./dnsrpzd.run 2>&1
+fi
index 987501aa7f35826fd8e5d56d5e5f5042007ce980..14491febc7a9a1ee2cc2c33d2194450efb461f2f 100755 (executable)
@@ -1,10 +1,18 @@
 #!/usr/bin/env perl
 #
-# Copyright (C) 2015, 2016  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
 #
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# 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 strict;
 use warnings;
@@ -24,13 +32,13 @@ view "recursive" {
 EOB
 
 my $no_option = <<'EOB';
-    };
+    } nsdname-enable yes nsip-enable yes;
 
     # policy zones to be tested
 EOB
 
 my $qname_wait_recurse = <<'EOB';
-    } qname-wait-recurse no;
+    } nsdname-enable yes nsip-enable yes qname-wait-recurse no;
 
     # policy zones to be tested
 EOB
index 9772699cab960c66e1207208e36588884680b07f..6566e13f9c64884b290453a668c536ca4ed73fb0 100644 (file)
@@ -6,12 +6,75 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# touch dnsrps-off to not test with DNSRPS
+# touch dnsrps-only to not test with classic RPZ
+
 SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
 status=0
+
 t=0
 
+DEBUG=
+DNSRPS_TEST_MODE=      # "" to test with and then without DNSRPS
+ARGS=
+
+USAGE="$0: [-xS] [-D {1,2}]"
+while getopts "xSD:" c; do
+    case $c in
+       x) set -x; DEBUG=-x; ARGS="$ARGS -x";;
+       D) DNSRPS_TEST_MODE="$OPTARG";;         # with or without DNSRPS
+       *) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+# really quit on control-C
+trap 'exit 1' 1 2 15
+
+
+DNSRPSCMD=../rpz/dnsrps
+RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
+
+# Run the tests twice, first without DNSRPS and then with if it is available
+if [ -z "$DNSRPS_TEST_MODE" ]; then
+    if [ -e dnsrps-only ]; then
+        echo "I:'dnsrps-only' found: skipping native RPZ sub-test"
+    else
+        echo "I:running native RPZ sub-test"
+       $SHELL ./$0 -D1 $ARGS || status=1
+    fi
+
+    if [ -e dnsrps-off ]; then
+       echo "I:'dnsrps-off' found: skipping DNSRPS sub-test"
+    else
+       echo "I:attempting to configure servers with DNSRPS..."
+       $SHELL ./setup.sh -D $DEBUG
+       sed -n 's/^## /I:/p' dnsrps.conf
+       if grep '^#fail' dnsrps.conf >/dev/null; then
+           echo "I:exit status: 1"
+           exit 1
+       fi
+       if test -z "`grep '^#skip' dnsrps.conf`"; then
+           $RNDCCMD 10.53.0.2 reload
+           $RNDCCMD 10.53.0.3 reload
+           $RNDCCMD 10.53.0.2 flush
+           $RNDCCMD 10.53.0.3 flush
+           echo "I:running DNSRPS sub-test"
+           $SHELL ./$0 -D2 $ARGS || status=1
+        else
+            echo "I:DNSRPS sub-test skipped"
+       fi
+    fi
+
+    echo "I:exit status: $status"
+    exit $status
+fi
+
 # $1 = test name (such as 1a, 1b, etc. for which named.$1.conf exists)
 run_server() {
     TESTNAME=$1
@@ -47,8 +110,8 @@ expect_norecurse() {
     t=`expr $t + 1`
     echo "I:testing $NAME doesn't recurse (${t})"
     run_query $TESTNAME $LINE || {
-        echo "I:test ${t} failed"
-        status=1
+       echo "I:test ${t} failed"
+       status=1
     }
 }
 
@@ -62,11 +125,14 @@ expect_recurse() {
     t=`expr $t + 1`
     echo "I:testing $NAME recurses (${t})"
     run_query $TESTNAME $LINE && {
-        echo "I:test ${t} failed"
-        status=1
+       echo "I:test ${t} failed"
+       status=1
     }
 }
 
+# show whether and why DNSRPS is enabled or disabled
+sed -n 's/^## /I:/p' dnsrps.conf
+
 t=`expr $t + 1`
 echo "I:testing that l1.l0 exists without RPZ (${t})"
 $DIG $DIGOPTS l1.l0 ns @10.53.0.2 -p 5300 > dig.out.${t}
@@ -177,6 +243,7 @@ echo "I:adding an NSDNAME policy"
 cp ns2/db.6a.00.policy.local ns2/saved.policy.local
 cp ns2/db.6b.00.policy.local ns2/db.6a.00.policy.local
 $RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+test -f dnsrpzd.pid && kill -USR1 `cat dnsrpzd.pid`
 sleep 1
 t=`expr $t + 1`
 echo "I:running dig to follow CNAME (blocks, so runs in the background) (${t})"
@@ -185,6 +252,7 @@ sleep 1
 echo "I:removing the NSDNAME policy"
 cp ns2/db.6c.00.policy.local ns2/db.6a.00.policy.local
 $RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+test -f dnsrpzd.pid && kill -USR1 `cat dnsrpzd.pid`
 sleep 1
 echo "I:resuming authority server"
 if [ "$CYGWIN" ]; then
@@ -198,8 +266,8 @@ for n in 1 2 3 4 5 6 7 8 9; do
     sleep 1
     [ -s dig.out.${t} ] || continue
     grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || {
-        echo "I:test ${t} failed"
-        status=1
+       echo "I:test ${t} failed"
+       status=1
     }
 done
 
@@ -222,6 +290,7 @@ kill -TSTP $PID
 echo "I:adding an NSDNAME policy"
 cp ns2/db.6b.00.policy.local ns2/db.6a.00.policy.local
 $RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+test -f dnsrpzd.pid && kill -USR1 `cat dnsrpzd.pid`
 sleep 1
 t=`expr $t + 1`
 echo "I:running dig to follow CNAME (blocks, so runs in the background) (${t})"
@@ -230,6 +299,7 @@ sleep 1
 echo "I:removing the policy zone"
 cp ns2/named.default.conf ns2/named.conf
 $RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reconfig 2>&1 | sed 's/^/I:ns2 /'
+test -f dnsrpzd.pid && kill -USR1 `cat dnsrpzd.pid`
 sleep 1
 echo "I:resuming authority server"
 if [ "$CYGWIN" ]; then
@@ -243,8 +313,8 @@ for n in 1 2 3 4 5 6 7 8 9; do
     sleep 1
     [ -s dig.out.${t} ] || continue
     grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || {
-        echo "I:test ${t} failed"
-        status=1
+       echo "I:test ${t} failed"
+       status=1
     }
 done
 
@@ -381,5 +451,10 @@ if test $p1 -le $p2; then ret=1; fi
 if test $ret != 0; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:exit status: $status"
+[ $status -ne 0 ] && pf=fail || pf=pass
+case $DNSRPS_TEST_MODE in
+        1) echo "I:status (native RPZ sub-test): $status ($pf)";;
+        2) echo "I:status (DNSRPS sub-test): $status ($pf)";;
+    *) echo "I:invalid test mode";;
+esac
 [ $status -eq 0 ] || exit 1
index 89fdb143fa584fbc0193435034a865c1097872c4..9cb97efadf212db092dd2abcee27239609a5f947 100644 (file)
@@ -155,16 +155,22 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define if you cannot bind() before connect() for TCP sockets. */
 #undef BROKEN_TCP_BIND_BEFORE_CONNECT
 
+/* dnsrps $librpz_name */
+#undef DNSRPS_LIBRPZ_PATH
+
+/* 0=no DNSRPS 1=static link 2=dlopen() */
+#undef DNSRPS_LIB_OPEN
+
 /* Define to enable "rrset-order fixed" syntax. */
 #undef DNS_RDATASET_FIXED
 
 /* Define to enable American Fuzzy Lop test harness */
 #undef ENABLE_AFL
 
-/* Define to enable rpz-nsdname rules. */
+/* Define to enable rpz nsdname rules. */
 #undef ENABLE_RPZ_NSDNAME
 
-/* Define to enable rpz-nsip rules. */
+/* Define to enable rpz nsip rules. */
 #undef ENABLE_RPZ_NSIP
 
 /* Solaris hack to get select_large_fdset. */
@@ -530,6 +536,9 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define to allow building of objects for dlopen(). */
 #undef ISC_DLZ_DLOPEN
 
+/* have __attribute__s used in librpz.h */
+#undef LIBRPZ_HAVE_ATTR
+
 /* Define to the sub-directory in which libtool stores uninstalled libraries.
    */
 #undef LT_OBJDIR
@@ -578,6 +587,9 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define to use large-system tuning. */
 #undef TUNE_LARGE
 
+/* Enable DNS Response Policy Service API */
+#undef USE_DNSRPS
+
 /* Defined if you need to use ioctl(FIONBIO) instead a fcntl call to make
    non-blocking. */
 #undef USE_FIONBIO_IOCTL
index 6c83d21593c9607acee026ec7011c5a8fa6b4b4e..545c541d5cc133172ca785765c4d67223e57f609 100755 (executable)
--- a/configure
+++ b/configure
@@ -935,6 +935,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -1022,6 +1023,10 @@ enable_atomic
 enable_fixed_rrset
 enable_rpz_nsip
 enable_rpz_nsdname
+enable_dnsrps_dl
+with_dnsrps_libname
+with_dnsrps_dir
+enable_dnsrps
 enable_filter_aaaa
 enable_dnstap
 with_protobuf_c
@@ -1092,6 +1097,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1344,6 +1350,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1481,7 +1496,7 @@ fi
 for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
                datadir sysconfdir sharedstatedir localstatedir includedir \
                oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-               libdir localedir mandir
+               libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1634,6 +1649,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -1702,8 +1718,10 @@ Optional Features:
   --enable-atomic        enable machine specific atomic operations
                          [default=autodetect]
   --enable-fixed-rrset    enable fixed rrset ordering [default=no]
-  --disable-rpz-nsip     disable rpz-nsip rules [default=enabled]
-  --disable-rpz-nsdname          disable rpz-nsdname rules [default=enabled]
+  --disable-rpz-nsip     disable rpz nsip rules [default=enabled]
+  --disable-rpz-nsdname          disable rpz nsdname rules [default=enabled]
+  --enable-dnsrps-dl     DNS Response Policy Service delayed link [default=$librpz_dl]
+  --enable-dnsrps         enable DNS Response Policy Service API
   --enable-filter-aaaa    enable filtering of AAAA records [default=no]
   --enable-dnstap         enable dnstap support (requires fstrm, protobuf-c)
   --enable-querytrace     enable very verbose query trace logging [default=no]
@@ -1743,6 +1761,8 @@ Optional Packages:
   --with-kame=PATH       use Kame IPv6 default path /usr/local/v6
   --with-readline=LIBSPEC    specify readline library default auto
 
+  --with-dnsrps-libname  DNSRPS provider library name (librpz.so)
+  --with-dnsrps-dir      path to DNSRPS provider library
   --with-protobuf-c=path  Path where protobuf-c is installed, for dnstap
   --with-libfstrm=path    Path where libfstrm is installed, for dnstap
   --with-docbook-xsl=PATH specify path for Docbook-XSL stylesheets
@@ -20613,6 +20633,232 @@ $as_echo "#define ENABLE_RPZ_NSDNAME 1" >>confdefs.h
                ;;
 esac
 
+#
+# Response policy rewriting using DNS Response Policy Service (DNSRPS)
+# interface.
+#
+# DNSRPS can be compiled into BIND everywhere with a reasonably
+# modern C compiler.  It is enabled on systems with dlopen() and librpz.so.
+#
+dnsrps_avail=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for librpz __attribute__s" >&5
+$as_echo_n "checking for librpz __attribute__s... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+       extern void f(char *p __attribute__((unused)), ...)
+       __attribute__((format(printf,1,2))) __attribute__((__noreturn__));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  librpz_have_attr=yes
+
+$as_echo "#define LIBRPZ_HAVE_ATTR 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  librpz_have_attr=no
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
+$as_echo_n "checking for library containing dlopen... " >&6; }
+if ${ac_cv_search_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' dl; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_dlopen=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_dlopen+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_dlopen+:} false; then :
+
+else
+  ac_cv_search_dlopen=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
+$as_echo "$ac_cv_search_dlopen" >&6; }
+ac_res=$ac_cv_search_dlopen
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+librpz_dl=yes
+for ac_func in dlopen dlclose dlsym
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  librpz_dl=no
+fi
+done
+
+# Check whether --enable-dnsrps-dl was given.
+if test "${enable_dnsrps_dl+set}" = set; then :
+  enableval=$enable_dnsrps_dl; enable_librpz_dl="$enableval"
+else
+  enable_librpz_dl="$librpz_dl"
+fi
+
+
+# Check whether --with-dnsrps-libname was given.
+if test "${with_dnsrps_libname+set}" = set; then :
+  withval=$with_dnsrps_libname; librpz_name="$withval"
+else
+  librpz_name="librpz.so"
+fi
+
+
+# Check whether --with-dnsrps-dir was given.
+if test "${with_dnsrps_dir+set}" = set; then :
+  withval=$with_dnsrps_dir; librpz_path="$withval/$librpz_name"
+else
+  librpz_path="$librpz_name"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DNSRPS_LIBRPZ_PATH "$librpz_path"
+_ACEOF
+
+if test "x$enable_librpz_dl" = "xyes"; then
+       dnsrps_lib_open=2
+else
+       dnsrps_lib_open=1
+       # Add librpz.so to linked libraries if we are not using dlopen()
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing librpz_client_create" >&5
+$as_echo_n "checking for library containing librpz_client_create... " >&6; }
+if ${ac_cv_search_librpz_client_create+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char librpz_client_create ();
+int
+main ()
+{
+return librpz_client_create ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rpz; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_librpz_client_create=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_librpz_client_create+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_librpz_client_create+:} false; then :
+
+else
+  ac_cv_search_librpz_client_create=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_librpz_client_create" >&5
+$as_echo "$ac_cv_search_librpz_client_create" >&6; }
+ac_res=$ac_cv_search_librpz_client_create
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+  dnsrps_lib_open=0
+                dnsrps_avail=no
+fi
+
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define DNSRPS_LIB_OPEN $dnsrps_lib_open
+_ACEOF
+
+
+# Check whether --enable-dnsrps was given.
+if test "${enable_dnsrps+set}" = set; then :
+  enableval=$enable_dnsrps; enable_dnsrps=$enableval
+else
+  enable_dnsrps=no
+fi
+
+if test "x$enable_dnsrps" != "xno"; then
+        if test "x$dnsrps_avail" != "xyes"; then
+                as_fn_error $? "dlopen and librpz.so needed for DNSRPS" "$LINENO" 5
+        fi
+       if test "x$dnsrps_lib_open" = "x0"; then
+               as_fn_error $? "dlopen and librpz.so needed for DNSRPS" "$LINENO" 5
+       fi
+
+$as_echo "#define USE_DNSRPS 1" >>confdefs.h
+
+fi
+
 #
 # Activate "filter-aaaa-on-v4/v6" or not?
 #
@@ -23306,7 +23552,7 @@ ac_config_commands="$ac_config_commands chmod"
 # elsewhere if there's a good reason for doing so.
 #
 
-ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/isc/Makefile bin/python/isc/utils.py bin/python/isc/tests/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/python/dnssec-keymgr.py bin/python/isc/__init__.py bin/python/isc/checkds.py bin/python/isc/coverage.py bin/python/isc/dnskey.py bin/python/isc/eventlist.py bin/python/isc/keydict.py bin/python/isc/keyevent.py bin/python/isc/keymgr.py bin/python/isc/keyseries.py bin/python/isc/keyzone.py bin/python/isc/policy.py bin/python/isc/rndc.py bin/python/isc/tests/dnskey_test.py bin/python/isc/tests/policy_test.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/dyndb/Makefile bin/tests/system/dyndb/driver/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/pipelined/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/tkey/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/arm/noteversion.xml doc/arm/pkgversion.xml doc/arm/releaseinfo.xml doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/tex/Makefile doc/tex/armstyle.sty doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/irs/tests/Makefile lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/isccfg/tests/Makefile lib/ns/Makefile lib/ns/include/Makefile lib/ns/include/ns/Makefile lib/ns/tests/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh"
+ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/isc/Makefile bin/python/isc/utils.py bin/python/isc/tests/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/python/dnssec-keymgr.py bin/python/isc/__init__.py bin/python/isc/checkds.py bin/python/isc/coverage.py bin/python/isc/dnskey.py bin/python/isc/eventlist.py bin/python/isc/keydict.py bin/python/isc/keyevent.py bin/python/isc/keymgr.py bin/python/isc/keyseries.py bin/python/isc/keyzone.py bin/python/isc/policy.py bin/python/isc/rndc.py bin/python/isc/tests/dnskey_test.py bin/python/isc/tests/policy_test.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/dyndb/Makefile bin/tests/system/dyndb/driver/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/pipelined/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/tkey/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/arm/noteversion.xml doc/arm/pkgversion.xml doc/arm/releaseinfo.xml doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/tex/Makefile doc/tex/armstyle.sty doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/irs/tests/Makefile lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/isccfg/tests/Makefile lib/ns/Makefile lib/ns/include/Makefile lib/ns/include/ns/Makefile lib/ns/tests/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh"
 
 
 #
@@ -24376,6 +24622,7 @@ do
     "bin/tests/system/inline/checkdsa.sh") CONFIG_FILES="$CONFIG_FILES bin/tests/system/inline/checkdsa.sh" ;;
     "bin/tests/system/pipelined/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/pipelined/Makefile" ;;
     "bin/tests/system/rndc/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rndc/Makefile" ;;
+    "bin/tests/system/rpz/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rpz/Makefile" ;;
     "bin/tests/system/rsabigexponent/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rsabigexponent/Makefile" ;;
     "bin/tests/system/tkey/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/tkey/Makefile" ;;
     "bin/tests/tasks/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/tasks/Makefile" ;;
@@ -25805,6 +26052,8 @@ report() {
         echo "    ECDSA algorithm support (--with-ecdsa)"
     test "yes" = "$OPENSSL_ED25519" -o "$PKCS11_ED25519" && \
         echo "    EDDSA algorithm support (--with-eddsa)"
+    test "yes" = "$enable_dnsrps" && \
+        echo "    DNS Response Policy Service interface (--enable-dnsrps)"
     test "yes" = "$enable_fixed" && \
         echo "    Allow 'fixed' rrset-order (--enable-fixed-rrset)"
     test "yes" = "$enable_filter" && \
@@ -25851,6 +26100,9 @@ report() {
     test "no" = "$use_geoip" && echo "    GeoIP access control (--with-geoip)"
     test "no" = "$use_gssapi" && echo "    GSS-API (--with-gssapi)"
 
+    test "no" = "$enable_dnsrps" && \
+        echo "    DNS Response Policy Service interface (--enable-dnsrps)"
+
     test "yes" = "$enable_fixed" || \
         echo "    Allow 'fixed' rrset-order (--enable-fixed-rrset)"
 
index 24f83fe3b8aaa4dc64f1ca24a763d0e6e31ed3c6..4c27d498c91e2aae5a1a0045f2c9601fe31d3627 100644 (file)
@@ -4235,13 +4235,13 @@ esac
 # Enable response policy rewriting using NS IP addresses
 #
 AC_ARG_ENABLE(rpz-nsip,
-       [  --disable-rpz-nsip     disable rpz-nsip rules [[default=enabled]]],
+       [  --disable-rpz-nsip     disable rpz nsip rules [[default=enabled]]],
                        enable_nsip="$enableval",
                        enable_nsip="yes")
 case "$enable_nsip" in
        yes)
                AC_DEFINE(ENABLE_RPZ_NSIP, 1,
-                         [Define to enable rpz-nsip rules.])
+                         [Define to enable rpz nsip rules.])
                ;;
        no)
                ;;
@@ -4253,13 +4253,13 @@ esac
 # Enable response policy rewriting using NS name
 #
 AC_ARG_ENABLE(rpz-nsdname,
-       [  --disable-rpz-nsdname          disable rpz-nsdname rules [[default=enabled]]],
+       [  --disable-rpz-nsdname          disable rpz nsdname rules [[default=enabled]]],
                        enable_nsdname="$enableval",
                        enable_nsdname="yes")
 case "$enable_nsdname" in
        yes)
                AC_DEFINE(ENABLE_RPZ_NSDNAME, 1,
-                         [Define to enable rpz-nsdname rules.])
+                         [Define to enable rpz nsdname rules.])
                ;;
        no)
                ;;
@@ -4267,6 +4267,66 @@ case "$enable_nsdname" in
                ;;
 esac
 
+#
+# Response policy rewriting using DNS Response Policy Service (DNSRPS)
+# interface.
+#
+# DNSRPS can be compiled into BIND everywhere with a reasonably
+# modern C compiler.  It is enabled on systems with dlopen() and librpz.so.
+#
+dnsrps_avail=yes
+AC_MSG_CHECKING([for librpz __attribute__s])
+    AC_TRY_COMPILE(,[
+       extern void f(char *p __attribute__((unused)), ...)
+       __attribute__((format(printf,1,2))) __attribute__((__noreturn__));],
+    librpz_have_attr=yes
+    AC_DEFINE([LIBRPZ_HAVE_ATTR], 1, [have __attribute__s used in librpz.h])
+    AC_MSG_RESULT([yes]),
+    librpz_have_attr=no
+    AC_MSG_RESULT([no]))
+
+AC_SEARCH_LIBS(dlopen, dl)
+librpz_dl=yes
+AC_CHECK_FUNCS(dlopen dlclose dlsym,,librpz_dl=no)
+AC_ARG_ENABLE([dnsrps-dl],
+       [  --enable-dnsrps-dl     DNS Response Policy Service delayed link [[default=$librpz_dl]]],
+       [enable_librpz_dl="$enableval"],
+       [enable_librpz_dl="$librpz_dl"])
+AC_ARG_WITH([dnsrps-libname],
+       [  --with-dnsrps-libname  DNSRPS provider library name (librpz.so)],
+        [librpz_name="$withval"], [librpz_name="librpz.so"])
+AC_ARG_WITH([dnsrps-dir],
+       [  --with-dnsrps-dir      path to DNSRPS provider library],
+       [librpz_path="$withval/$librpz_name"], [librpz_path="$librpz_name"])
+AC_DEFINE_UNQUOTED([DNSRPS_LIBRPZ_PATH], ["$librpz_path"],
+       [dnsrps $librpz_name])
+if test "x$enable_librpz_dl" = "xyes"; then
+       dnsrps_lib_open=2
+else
+       dnsrps_lib_open=1
+       # Add librpz.so to linked libraries if we are not using dlopen()
+       AC_SEARCH_LIBS([librpz_client_create], [rpz], [],
+               [dnsrps_lib_open=0
+                dnsrps_avail=no])
+fi
+AC_DEFINE_UNQUOTED([DNSRPS_LIB_OPEN], [$dnsrps_lib_open],
+       [0=no DNSRPS  1=static link  2=dlopen()])
+
+AC_ARG_ENABLE([dnsrps],
+       AS_HELP_STRING([--enable-dnsrps],
+               [enable DNS Response Policy Service API]),
+       [enable_dnsrps=$enableval],
+        [enable_dnsrps=no])
+if test "x$enable_dnsrps" != "xno"; then
+        if test "x$dnsrps_avail" != "xyes"; then
+                AC_MSG_ERROR([[dlopen and librpz.so needed for DNSRPS]])
+        fi
+       if test "x$dnsrps_lib_open" = "x0"; then
+               AC_MSG_ERROR([[dlopen and librpz.so needed for DNSRPS]])
+       fi
+       AC_DEFINE([USE_DNSRPS], [1], [Enable DNS Response Policy Service API])
+fi
+
 #
 # Activate "filter-aaaa-on-v4/v6" or not?
 #
@@ -5165,6 +5225,7 @@ AC_CONFIG_FILES([
        bin/tests/system/inline/checkdsa.sh
        bin/tests/system/pipelined/Makefile
        bin/tests/system/rndc/Makefile
+       bin/tests/system/rpz/Makefile
        bin/tests/system/rsabigexponent/Makefile
        bin/tests/system/tkey/Makefile
        bin/tests/tasks/Makefile
@@ -5321,6 +5382,8 @@ report() {
         echo "    ECDSA algorithm support (--with-ecdsa)"
     test "yes" = "$OPENSSL_ED25519" -o "$PKCS11_ED25519" && \
         echo "    EDDSA algorithm support (--with-eddsa)"
+    test "yes" = "$enable_dnsrps" && \
+        echo "    DNS Response Policy Service interface (--enable-dnsrps)"
     test "yes" = "$enable_fixed" && \
         echo "    Allow 'fixed' rrset-order (--enable-fixed-rrset)"
     test "yes" = "$enable_filter" && \
@@ -5367,6 +5430,9 @@ report() {
     test "no" = "$use_geoip" && echo "    GeoIP access control (--with-geoip)"
     test "no" = "$use_gssapi" && echo "    GSS-API (--with-gssapi)"
 
+    test "no" = "$enable_dnsrps" && \
+        echo "    DNS Response Policy Service interface (--enable-dnsrps)"
+
     test "yes" = "$enable_fixed" || \
         echo "    Allow 'fixed' rrset-order (--enable-fixed-rrset)"
 
index 71eef5405c0bf8b2810912c651da3b0b08138ffc..7a18da97b16275c5828c8d6c0c2dbc1f8ba5372d 100644 (file)
@@ -4521,7 +4521,9 @@ badresp:1,adberr:0,findfail:0,valfail:0]
       [ <command>recursive-only</command> <replaceable>yes_or_no</replaceable> ]
       [ <command>log</command> <replaceable>yes_or_no</replaceable> ]
       [ <command>max-policy-ttl</command> <replaceable>number</replaceable> ]
-      [ <command>min-update-interval</command> <replaceable>number</replaceable> ] ;
+      [ <command>min-update-interval</command> <replaceable>number</replaceable> ]
+      [ <command>nsip-enable <replaceable>yes_or_no</replaceable> ]
+      [ <command>nsdname-enable <replaceable>yes_or_no</replaceable> ] ;
         ...
     <command>}</command> [ <command>recursive-only</command> <replaceable>yes_or_no</replaceable> ]
       [ <command>max-policy-ttl</command> <replaceable>number</replaceable> ]
@@ -4529,7 +4531,11 @@ badresp:1,adberr:0,findfail:0,valfail:0]
       [ <command>break-dnssec</command> <replaceable>yes_or_no</replaceable> ]
       [ <command>min-ns-dots</command> <replaceable>number</replaceable> ]
       [ <command>nsip-wait-recurse</command> <replaceable>yes_or_no</replaceable> ]
-      [ <command>qname-wait-recurse</command> <replaceable>yes_or_no</replaceable> ] ; ]
+      [ <command>qname-wait-recurse</command> <replaceable>yes_or_no</replaceable> ]
+      [ <command>nsip-enable</command> <replaceable>yes_or_no</replaceable> ]
+      [ <command>nsdname-enable</command> <replaceable>yes_or_no</replaceable> ]
+      [ <command>dnsrps-enable</command> <replaceable>yes_or_no</replaceable> ]
+      [ <command>dnsrps-options</command> { <replaceable>parameters</replaceable> } ] ; ]
   [ <command>catalog-zones {</command>
        <command>zone</command> <replaceable>quoted_string</replaceable>
          [ <option>default-masters</option> [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] <command>{</command>
@@ -10212,6 +10218,9 @@ deny-answer-aliases { "example.net"; };
                    NSIP triggers match IP addresses in A and
                    AAAA RRsets for domains that can be checked against NSDNAME
                    policy records.
+                   The <command>nsdname-enable</command> phrase turns NSDNAME
+                   triggers off or on for a single policy zone or all
+                   zones.
                  </para>
                </listitem>
              </varlistentry>
@@ -10227,6 +10236,9 @@ deny-answer-aliases { "example.net"; };
                    least <command>min-ns-dots</command> dots.
                    The default value of <command>min-ns-dots</command> is
                    1, to exclude top level domains.
+                   The <command>nsip-enable</command> phrase turns NSIP
+                   triggers off or on for a single policy zone or all
+                   zones.
                  </para>
                  <para>
                    If a name server's IP address is not yet known,
@@ -10475,11 +10487,11 @@ deny-answer-aliases { "example.net"; };
            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
+           have slow authoritative servers, this behavior can cost
            significant time.
-           The <command>qname-wait-recurse no</command> option
-           overrides that default behavior when recursion cannot
-           change a non-error response.
+           The <command>qname-wait-recurse yes</command> option
+           overrides the default and enables that 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
@@ -10493,6 +10505,42 @@ deny-answer-aliases { "example.net"; };
            discover problems at the authoritative server.
          </para>
 
+         <para>
+           The <command>dnsrps-enable yes</command> option turns on
+           the DNS Rsponse Policy Service (DNSRPS) interface, if it has been
+           compiled in to <command>named</command> using
+           <command>configure --enable-dnsrps</command>.
+         </para>
+
+         <para>
+           The <command>dnsrps-options</command> block provides additional
+           RPZ configuration settings, which are passed through to the
+           DNSRPS provider library.
+           Multiple DNSRPS settings in an <command>dnsrps-options</command>
+           string should be separated with semi-colons.
+           The DNSRPS provider, librpz, is passed a configuration string
+           consisting of the <command>dnsrps-options</command> text,
+           concatenated with settings derived from the
+           <command>response-policy</command> statement.
+         </para>
+
+         <para>
+           Note: The <command>dnsrps-options</command> text should only include
+           configuration settings that are specific to the DNSRPS
+           provider.  For example, the DNSRPS provider from
+           Farsight Security takes options such as
+           <command>dnsrpzd-conf</command>,
+           <command>dnsrpzd-sock</command>, and
+           <command>dnzrpzd-args</command> (for details of these options,
+           see the <command>librpz</command> documentation).
+           Other RPZ configuration settings could be included in
+           <command>dnsrps-options</command>
+           as well, but if <command>named</command> were switched
+           back to traditional RPZ by setting
+           <command>dnsrps-enable</command> to "no", those options would
+           be ignored.
+         </para>
+
          <para>
            The TTL of a record modified by RPZ policies is set from the
            TTL of the relevant record in policy zone.  It is then limited
index 308e5a174c28d3ddcff13f65de2fe1bf21e1500f..f5244f99a146f4ed4a172820b11ebda8a1fbb8f4 100644 (file)
 
   <section xml:id="relnotes_features"><info><title>New Features</title></info>
     <itemizedlist>
+      <listitem>
+       <para>
+         Added support for the DNS Response Policy Service (DNSRPS) API,
+         a mechanism to allow <command>named</command> to use an external
+         response policy provider. (One example of such a provider is
+         "FastRPZ" from Farsight Security, Inc.) This allows the same
+         types of policy filtering as standard RPZ, but can reduce the
+         workload for <command>named</command>, particularly when using
+         large and frequently-updated policy zones. It also enables
+         <command>named</command> to share response policy providers
+         with other DNS implementations such as Unbound.
+       </para>
+       <para>
+         This feature is avaiable if BIND is built with
+         <command>configure --enable-dnsrps</command>
+         and if <command>dnsrps-enable</command> is set to "yes" in
+         <filename>named.conf</filename>.
+       </para>
+       <para>
+         Thanks to Vernon Schryver and Farsight Security for the
+         contribution. [RT #43376]
+       </para>
+      </listitem>
       <listitem>
        <para>
          Code implementing name server query processing has been moved
index 213e82322df964418d41aa034dfc96ab55bf04c6..479396bd146a34ceed4ef05b8ec071665a42b9a9 100644 (file)
@@ -60,8 +60,9 @@ DNSTAPOBJS = dnstap.@O@ dnstap.pb-c.@O@
 DNSOBJS =      acl.@O@ adb.@O@ badcache.@O@ byaddr.@O@ \
                cache.@O@ callbacks.@O@ catz.@O@ clientinfo.@O@ compress.@O@ \
                db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
-               dlz.@O@ dns64.@O@ dnssec.@O@ ds.@O@ dyndb.@O@ ecs.@O@ \
-               forward.@O@ ipkeylist.@O@ iptable.@O@ journal.@O@ keydata.@O@ \
+               dlz.@O@ dns64.@O@ dnsrps.@O@ dnssec.@O@ ds.@O@ dyndb.@O@ \
+               ecs.@O@ forward.@O@ \
+               ipkeylist.@O@ iptable.@O@ journal.@O@ keydata.@O@ \
                keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \
                master.@O@ masterdump.@O@ message.@O@ \
                name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ nta.@O@ \
@@ -103,7 +104,7 @@ DNSTAPSRCS = dnstap.c dnstap.pb-c.c
 DNSSRCS =      acl.c adb.c badcache. byaddr.c \
                cache.c callbacks.c clientinfo.c compress.c \
                db.c dbiterator.c dbtable.c diff.c dispatch.c \
-               dlz.c dns64.c dnssec.c ds.c dyndb.c ecs.c forward.c \
+               dlz.c dns64.c dnsrps.c dnssec.c ds.c dyndb.c ecs.c forward.c \
                ipkeylist.c iptable.c journal.c keydata.c keytable.c lib.c \
                log.c lookup.c master.c masterdump.c message.c \
                name.c ncache.c nsec.c nsec3.c nta.c \
diff --git a/lib/dns/dnsrps.c b/lib/dns/dnsrps.c
new file mode 100644 (file)
index 0000000..4c38732
--- /dev/null
@@ -0,0 +1,995 @@
+/*
+ * Copyright (C) 2017  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#ifdef USE_DNSRPS
+
+#include <isc/mem.h>
+#include <isc/stdlib.h>
+#include <isc/string.h>
+
+#include <dns/db.h>
+#define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN
+#include <dns/dnsrps.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/rpz.h>
+
+librpz_t *librpz;
+librpz_emsg_t librpz_lib_open_emsg;
+static void *librpz_handle;
+
+#define RPSDB_MAGIC ISC_MAGIC('R', 'P', 'Z', 'F')
+#define VALID_RPSDB(rpsdb) ((rpsdb)->common.impmagic == RPSDB_MAGIC)
+
+#define RD_DB(r)       ((r)->private1)
+#define RD_CUR_RR(r)   ((r)->private2)
+#define RD_NEXT_RR(r)  ((r)->resign)
+#define RD_COUNT(r)    ((r)->privateuint4)
+
+typedef struct {
+       dns_rdatasetiter_t      common;
+       dns_rdatatype_t         type;
+       dns_rdataclass_t        class;
+       uint32_t                ttl;
+       uint                    count;
+       librpz_idx_t            next_rr;
+} rpsdb_rdatasetiter_t;
+
+static dns_dbmethods_t rpsdb_db_methods;
+static dns_rdatasetmethods_t rpsdb_rdataset_methods;
+static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods;
+
+static librpz_clist_t *clist;
+
+static isc_mutex_t dnsrps_mutex;
+
+static void
+dnsrps_lock(void *mutex0) {
+       isc_mutex_t *mutex = mutex0;
+
+       LOCK(mutex);
+}
+
+static void
+dnsrps_unlock(void *mutex0) {
+       isc_mutex_t *mutex = mutex0;
+
+       UNLOCK(mutex);
+}
+
+static void
+dnsrps_mutex_destroy(void *mutex0) {
+       isc_mutex_t *mutex = mutex0;
+
+       DESTROYLOCK(mutex);
+}
+
+static void
+dnsrps_log_fnc(librpz_log_level_t level, void *ctxt, const char *buf) {
+       int isc_level;
+
+       UNUSED(ctxt);
+
+       /* Setting librpz_log_level in the configuration overrides the
+        * BIND9 logging levels. */
+       if (level > LIBRPZ_LOG_TRACE1 &&
+           level <= librpz->log_level_val(LIBRPZ_LOG_INVALID))
+               level = LIBRPZ_LOG_TRACE1;
+
+       switch(level) {
+       case LIBRPZ_LOG_FATAL:
+       case LIBRPZ_LOG_ERROR:          /* errors */
+       default:
+               isc_level = DNS_RPZ_ERROR_LEVEL;
+               break;
+
+       case LIBRPZ_LOG_TRACE1:         /* big events such as dnsrpzd starts */
+               isc_level = DNS_RPZ_INFO_LEVEL;
+               break;
+
+       case LIBRPZ_LOG_TRACE2:         /* smaller dnsrpzd zone transfers */
+               isc_level = DNS_RPZ_DEBUG_LEVEL1;
+               break;
+
+       case LIBRPZ_LOG_TRACE3:         /* librpz hits */
+               isc_level = DNS_RPZ_DEBUG_LEVEL2;
+               break;
+
+       case LIBRPZ_LOG_TRACE4:         /* librpz lookups */
+               isc_level = DNS_RPZ_DEBUG_LEVEL3;
+               break;
+       }
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB,
+                     isc_level, "dnsrps: %s", buf);
+}
+
+/*
+ * Start dnsrps for the entire server.
+ *     This is not thread safe, but it is called by a single thread.
+ */
+isc_result_t
+dns_dnsrps_server_create(void) {
+       librpz_emsg_t emsg;
+       isc_result_t result;
+
+       INSIST(clist == NULL);
+       INSIST(librpz == NULL);
+       INSIST(librpz_handle == NULL);
+
+       /*
+        * Notice if librpz is available.
+        */
+       librpz = librpz_lib_open(&librpz_lib_open_emsg,
+                                &librpz_handle, DNSRPS_LIBRPZ_PATH);
+       /*
+        * Stop now without complaining if librpz is not available.
+        * Complain later if and when librpz is needed for a view with
+        * "dnsrps-enable yse" (including the default view).
+        */
+       if (librpz == NULL)
+               return (ISC_R_SUCCESS);
+
+       result = isc_mutex_init(&dnsrps_mutex);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       librpz->set_log(&dnsrps_log_fnc, NULL);
+
+       clist = librpz->clist_create(&emsg, dnsrps_lock, dnsrps_unlock,
+                                    dnsrps_mutex_destroy, &dnsrps_mutex,
+                                    dns_lctx);
+       if (clist == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "dnsrps: %s", emsg.c);
+               return (ISC_R_NOMEMORY);
+       }
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Stop dnsrps for the entire server.
+ *     This is not thread safe.
+ */
+void
+dns_dnsrps_server_destroy(void) {
+       if (clist != NULL)
+               librpz->clist_detach(&clist);
+
+#ifdef LIBRPZ_USE_DLOPEN
+       if (librpz != NULL) {
+               INSIST(librpz_handle != NULL);
+               if (dlclose(librpz_handle) != 0)
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                                     DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                                     "dnsrps: dlclose(): %s", dlerror());
+               librpz_handle = NULL;
+       }
+#endif
+}
+
+/*
+ * Ready dnsrps for a view.
+ */
+isc_result_t
+dns_dnsrps_view_init(dns_rpz_zones_t *new, char *rps_cstr) {
+       librpz_emsg_t emsg;
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                     DNS_LOGMODULE_RBTDB, DNS_RPZ_DEBUG_LEVEL3,
+                     "dnsrps configuration \"%s\"", rps_cstr);
+
+       new->rps_client = librpz->client_create(&emsg, clist,
+                                                rps_cstr, ISC_FALSE);
+       if (new->rps_client == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "librpz->client_create(): %s", emsg.c);
+               new->p.dnsrps_enabled = ISC_FALSE;
+               return (ISC_R_FAILURE);
+       }
+
+       new->p.dnsrps_enabled = ISC_TRUE;
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Connect to and start the dnsrps daemon, dnsrpzd.
+ */
+isc_result_t
+dns_dnsrps_connect(dns_rpz_zones_t *rpzs) {
+       librpz_emsg_t emsg;
+
+       if (rpzs == NULL || !rpzs->p.dnsrps_enabled)
+               return (ISC_R_SUCCESS);
+
+       /*
+        * Fail only if we failed to link to librpz.
+        */
+       if (librpz == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "librpz->connect(): %s", librpz_lib_open_emsg.c);
+               return (ISC_R_FAILURE);
+       }
+
+       if (!librpz->connect(&emsg, rpzs->rps_client, true)) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "librpz->connect(): %s", emsg.c);
+               return (ISC_R_SUCCESS);
+       }
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB,
+                     DNS_RPZ_INFO_LEVEL, "dnsrps: librpz version %s",
+                     librpz->version);
+
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Get ready to try RPZ rewriting.
+ */
+isc_result_t
+dns_dnsrps_rewrite_init(librpz_emsg_t *emsg, dns_rpz_st_t *st,
+                       dns_rpz_zones_t *rpzs, const dns_name_t *qname,
+                       isc_mem_t *mctx, isc_boolean_t have_rd)
+{
+       rpsdb_t *rpsdb;
+
+       rpsdb = isc_mem_get(mctx, sizeof(*rpsdb));
+       if (rpsdb == NULL) {
+               strlcpy(emsg->c, "no memory", sizeof(emsg->c));
+               return (ISC_R_NOMEMORY);
+       }
+       memset(rpsdb, 0, sizeof(*rpsdb));
+
+       if (!librpz->rsp_create(emsg, &rpsdb->rsp, NULL,
+                               rpzs->rps_client, have_rd, false)) {
+               isc_mem_put(mctx, rpsdb, sizeof(*rpsdb));
+               return (DNS_R_SERVFAIL);
+       }
+       if (rpsdb->rsp == NULL) {
+               isc_mem_put(mctx, rpsdb, sizeof(*rpsdb));
+               return (DNS_R_DISALLOWED);
+       }
+
+       rpsdb->common.magic = DNS_DB_MAGIC;
+       rpsdb->common.impmagic = RPSDB_MAGIC;
+       rpsdb->common.methods = &rpsdb_db_methods;
+       rpsdb->common.rdclass = dns_rdataclass_in;
+       dns_name_init(&rpsdb->common.origin, NULL);
+       isc_mem_attach(mctx, &rpsdb->common.mctx);
+
+       rpsdb->ref_cnt = 1;
+       rpsdb->qname = qname;
+
+       st->rpsdb = &rpsdb->common;
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Convert a dnsrps policy to a classic BIND9 RPZ policy.
+ */
+dns_rpz_policy_t
+dns_dnsrps_2policy(librpz_policy_t rps_policy) {
+       switch (rps_policy) {
+       case LIBRPZ_POLICY_UNDEFINED:
+               return (DNS_RPZ_POLICY_MISS);
+       case LIBRPZ_POLICY_PASSTHRU:
+               return (DNS_RPZ_POLICY_PASSTHRU);
+       case LIBRPZ_POLICY_DROP:
+               return (DNS_RPZ_POLICY_DROP);
+       case LIBRPZ_POLICY_TCP_ONLY:
+               return (DNS_RPZ_POLICY_TCP_ONLY);
+       case LIBRPZ_POLICY_NXDOMAIN:
+               return (DNS_RPZ_POLICY_NXDOMAIN);
+       case LIBRPZ_POLICY_NODATA:
+               return (DNS_RPZ_POLICY_NODATA);
+       case LIBRPZ_POLICY_RECORD:
+       case LIBRPZ_POLICY_CNAME:
+               return (DNS_RPZ_POLICY_RECORD);
+
+       case LIBRPZ_POLICY_DELETED:
+       case LIBRPZ_POLICY_GIVEN:
+       case LIBRPZ_POLICY_DISABLED:
+       default:
+               INSIST(0);
+       }
+}
+
+/*
+ * Convert a dnsrps trigger to a classic BIND9 RPZ rewrite or trigger type.
+ */
+dns_rpz_type_t
+dns_dnsrps_trig2type(librpz_trig_t trig) {
+       switch (trig) {
+       case LIBRPZ_TRIG_BAD:
+       default:
+               return (DNS_RPZ_TYPE_BAD);
+       case LIBRPZ_TRIG_CLIENT_IP:
+               return (DNS_RPZ_TYPE_CLIENT_IP);
+       case LIBRPZ_TRIG_QNAME:
+               return (DNS_RPZ_TYPE_QNAME);
+       case LIBRPZ_TRIG_IP:
+               return (DNS_RPZ_TYPE_IP);
+       case LIBRPZ_TRIG_NSDNAME:
+               return (DNS_RPZ_TYPE_NSDNAME);
+       case LIBRPZ_TRIG_NSIP:
+               return (DNS_RPZ_TYPE_NSIP);
+       }
+}
+
+/*
+ * Convert a classic BIND9 RPZ rewrite or trigger type to a librpz trigger type.
+ */
+librpz_trig_t
+dns_dnsrps_type2trig(dns_rpz_type_t type) {
+       switch (type) {
+       case DNS_RPZ_TYPE_BAD:
+       default:
+               return (LIBRPZ_TRIG_BAD);
+       case DNS_RPZ_TYPE_CLIENT_IP:
+               return (LIBRPZ_TRIG_CLIENT_IP);
+       case DNS_RPZ_TYPE_QNAME:
+               return (LIBRPZ_TRIG_QNAME);
+       case DNS_RPZ_TYPE_IP:
+               return (LIBRPZ_TRIG_IP);
+       case DNS_RPZ_TYPE_NSDNAME:
+               return (LIBRPZ_TRIG_NSDNAME);
+       case DNS_RPZ_TYPE_NSIP:
+               return (LIBRPZ_TRIG_NSIP);
+       }
+}
+
+static void
+rpsdb_attach(dns_db_t *source, dns_db_t **targetp) {
+       rpsdb_t *rpsdb = (rpsdb_t *)source;
+
+       REQUIRE(VALID_RPSDB(rpsdb));
+
+       /*
+        * Use a simple count because only one thread uses any single rpsdb_t
+        */
+       ++rpsdb->ref_cnt;
+       *targetp = source;
+}
+
+static void
+rpsdb_detach(dns_db_t **dbp) {
+       rpsdb_t *rpsdb = (rpsdb_t *)*dbp;
+
+       REQUIRE(VALID_RPSDB(rpsdb));
+       REQUIRE(rpsdb->ref_cnt > 0);
+
+       *dbp = NULL;
+
+       /*
+        * Simple count because only one thread uses a rpsdb_t.
+        */
+       if (--rpsdb->ref_cnt != 0)
+               return;
+
+       librpz->rsp_detach(&rpsdb->rsp);
+       rpsdb->common.impmagic = 0;
+       isc_mem_putanddetach(&rpsdb->common.mctx, rpsdb, sizeof(*rpsdb));
+}
+
+static void
+rpsdb_attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+       rpsdb_t *rpsdb = (rpsdb_t *)db;
+
+       REQUIRE(VALID_RPSDB(rpsdb));
+       REQUIRE(targetp != NULL && *targetp == NULL);
+       REQUIRE(source == &rpsdb->origin_node ||
+               source == &rpsdb->data_node);
+
+       /*
+        * Simple count because only one thread uses a rpsdb_t.
+        */
+       ++rpsdb->ref_cnt;
+       *targetp = source;
+}
+
+static void
+rpsdb_detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
+       rpsdb_t *rpsdb = (rpsdb_t *)db;
+
+       REQUIRE(VALID_RPSDB(rpsdb));
+       REQUIRE(*targetp == &rpsdb->origin_node ||
+               *targetp == &rpsdb->data_node);
+
+       *targetp = NULL;
+       rpsdb_detach(&db);
+}
+
+static isc_result_t
+rpsdb_findnode(dns_db_t *db, const dns_name_t *name, isc_boolean_t create,
+              dns_dbnode_t **nodep)
+{
+       rpsdb_t *rpsdb = (rpsdb_t *)db;
+       dns_db_t *dbp;
+
+       REQUIRE(VALID_RPSDB(rpsdb));
+       REQUIRE(nodep != NULL && *nodep == NULL);
+       REQUIRE(!create);
+
+       /*
+        * A fake/shim rpsdb has two nodes.
+        * One is the origin to support query_addsoa() in bin/named/query.c.
+        * The other contains rewritten RRs.
+        */
+       if (dns_name_equal(name, &db->origin))
+               *nodep = &rpsdb->origin_node;
+       else
+               *nodep = &rpsdb->data_node;
+       dbp = NULL;
+       rpsdb_attach(db, &dbp);
+
+       return (ISC_R_SUCCESS);
+}
+
+static void
+rpsdb_bind_rdataset(dns_rdataset_t *rdataset, uint count, librpz_idx_t next_rr,
+                   dns_rdatatype_t type, uint16_t class, uint32_t ttl,
+                   rpsdb_t *rpsdb)
+{
+       dns_db_t *dbp;
+
+       INSIST(rdataset->methods == NULL);  /* We must be disassociated. */
+       REQUIRE(type != dns_rdatatype_none);
+
+       rdataset->methods = &rpsdb_rdataset_methods;
+       rdataset->rdclass = class;
+       rdataset->type = type;
+       rdataset->ttl = ttl;
+       dbp = NULL;
+       dns_db_attach(&rpsdb->common, &dbp);
+       RD_DB(rdataset) = dbp;
+       RD_COUNT(rdataset) = count;
+       RD_NEXT_RR(rdataset) = next_rr;
+       RD_CUR_RR(rdataset) = NULL;
+}
+
+static isc_result_t
+rpsdb_bind_soa(dns_rdataset_t *rdataset, rpsdb_t *rpsdb) {
+       uint32_t ttl;
+       librpz_emsg_t emsg;
+
+       if (!librpz->rsp_soa(&emsg, &ttl, NULL, NULL,
+                            &rpsdb->result, rpsdb->rsp)) {
+               librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+               return (DNS_R_SERVFAIL);
+       }
+       rpsdb_bind_rdataset(rdataset, 1, LIBRPZ_IDX_BAD, dns_rdatatype_soa,
+                            dns_rdataclass_in, ttl, rpsdb);
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Forge an rdataset of the desired type from a librpz result.
+ * This is written for simplicity instead of speed, because RPZ rewriting
+ * should be rare compared to normal BIND operations.
+ */
+static isc_result_t
+rpsdb_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+                  dns_rdatatype_t type, dns_rdatatype_t covers,
+                  isc_stdtime_t now, dns_rdataset_t *rdataset,
+                  dns_rdataset_t *sigrdataset)
+{
+       rpsdb_t *rpsdb = (rpsdb_t *)db;
+       dns_rdatatype_t foundtype;
+       dns_rdataclass_t class;
+       uint32_t ttl;
+       uint count;
+       librpz_emsg_t emsg;
+
+       UNUSED(version);
+       UNUSED(covers);
+       UNUSED(now);
+       UNUSED(sigrdataset);
+
+       REQUIRE(VALID_RPSDB(rpsdb));
+
+       if (node == &rpsdb->origin_node) {
+               if (type == dns_rdatatype_any)
+                       return (ISC_R_SUCCESS);
+               if (type == dns_rdatatype_soa)
+                       return (rpsdb_bind_soa(rdataset, rpsdb));
+               return (DNS_R_NXRRSET);
+       }
+
+       REQUIRE(node == &rpsdb->data_node);
+
+       switch (rpsdb->result.policy) {
+       case LIBRPZ_POLICY_UNDEFINED:
+       case LIBRPZ_POLICY_DELETED:
+       case LIBRPZ_POLICY_PASSTHRU:
+       case LIBRPZ_POLICY_DROP:
+       case LIBRPZ_POLICY_TCP_ONLY:
+       case LIBRPZ_POLICY_GIVEN:
+       case LIBRPZ_POLICY_DISABLED:
+       default:
+               librpz->log(LIBRPZ_LOG_ERROR, NULL,
+                           "impossible dnsrps policy %d at %s:%d",
+                           rpsdb->result.policy, __FILE__, __LINE__);
+               return (DNS_R_SERVFAIL);
+
+       case LIBRPZ_POLICY_NXDOMAIN:
+               return (DNS_R_NXDOMAIN);
+
+       case LIBRPZ_POLICY_NODATA:
+               return (DNS_R_NXRRSET);
+
+       case LIBRPZ_POLICY_RECORD:
+       case LIBRPZ_POLICY_CNAME:
+               break;
+       }
+
+       if (type == dns_rdatatype_soa)
+               return (rpsdb_bind_soa(rdataset, rpsdb));
+
+       /*
+        * There is little to do for an ANY query.
+        */
+       if (type == dns_rdatatype_any)
+               return (ISC_R_SUCCESS);
+
+       /*
+        * Reset to the start of the RRs.
+        * This function is only used after a policy has been chosen,
+        * and so without caring whether it is after recursion.
+        */
+       if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) {
+               librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+               return (DNS_R_SERVFAIL);
+       }
+       if (!librpz->rsp_rr(&emsg, &foundtype, &class, &ttl, NULL,
+                           &rpsdb->result, rpsdb->qname->ndata,
+                           rpsdb->qname->length, rpsdb->rsp)) {
+               librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+               return (DNS_R_SERVFAIL);
+       }
+       REQUIRE(foundtype != dns_rdatatype_none);
+
+       /*
+        * Ho many of the target RR type are available?
+        */
+       count = 0;
+       do {
+               if (type == foundtype || type == dns_rdatatype_any)
+                       ++count;
+
+               if (!librpz->rsp_rr(&emsg, &foundtype, NULL, NULL, NULL,
+                                   &rpsdb->result, rpsdb->qname->ndata,
+                                   rpsdb->qname->length, rpsdb->rsp)) {
+                       librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+                       return (DNS_R_SERVFAIL);
+               }
+       } while (foundtype != dns_rdatatype_none);
+       if (count == 0)
+               return (DNS_R_NXRRSET);
+       rpsdb_bind_rdataset(rdataset, count, rpsdb->result.next_rr,
+                            type, class, ttl, rpsdb);
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rpsdb_finddb(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
+            dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+            dns_dbnode_t **nodep, dns_name_t *foundname,
+            dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+       dns_dbnode_t *node;
+       isc_result_t result;
+
+       UNUSED(version);
+       UNUSED(options);
+       UNUSED(now);
+       UNUSED(sigrdataset);
+
+       if (nodep == NULL) {
+               node = NULL;
+               nodep = &node;
+       }
+       rpsdb_findnode(db, name, false, nodep);
+       result = dns_name_copy(name, foundname, NULL);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       return (rpsdb_findrdataset(db, *nodep, NULL, type, 0, 0,
+                                   rdataset, sigrdataset));
+}
+
+static isc_result_t
+rpsdb_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+                  isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
+{
+       rpsdb_t *rpsdb = (rpsdb_t *)db;
+       rpsdb_rdatasetiter_t *rpsdb_iter;
+
+       UNUSED(version);
+       UNUSED(now);
+
+       REQUIRE(VALID_RPSDB(rpsdb));
+       REQUIRE(node == &rpsdb->origin_node || node == &rpsdb->data_node);
+
+       rpsdb_iter = isc_mem_get(rpsdb->common.mctx, sizeof(*rpsdb_iter));
+       if (rpsdb_iter == NULL)
+               return (ISC_R_NOMEMORY);
+
+       memset(rpsdb_iter, 0, sizeof(*rpsdb_iter));
+       rpsdb_iter->common.magic = DNS_RDATASETITER_MAGIC;
+       rpsdb_iter->common.methods = &rpsdb_rdatasetiter_methods;
+       rpsdb_iter->common.db = db;
+       rpsdb_attachnode(db, node, &rpsdb_iter->common.node);
+
+       *iteratorp = &rpsdb_iter->common;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+rpsdb_issecure(dns_db_t *db) {
+       UNUSED(db);
+
+       return (ISC_FALSE);
+}
+
+static isc_result_t
+rpsdb_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
+       rpsdb_t *rpsdb = (rpsdb_t *)db;
+
+       REQUIRE(VALID_RPSDB(rpsdb));
+       REQUIRE(nodep != NULL && *nodep == NULL);
+
+       rpsdb_attachnode(db, &rpsdb->origin_node, nodep);
+       return (ISC_R_SUCCESS);
+}
+
+static void
+rpsdb_rdataset_disassociate(dns_rdataset_t *rdataset) {
+       dns_db_t *db;
+
+       /*
+        * Detach the last RR delivered.
+        */
+       if (RD_CUR_RR(rdataset) != NULL) {
+               free(RD_CUR_RR(rdataset));
+               RD_CUR_RR(rdataset) = NULL;
+       }
+
+       db = RD_DB(rdataset);
+       RD_DB(rdataset) = NULL;
+       dns_db_detach(&db);
+}
+
+static isc_result_t
+rpsdb_rdataset_next(dns_rdataset_t *rdataset) {
+       rpsdb_t *rpsdb;
+       uint16_t type;
+       dns_rdataclass_t class;
+       librpz_rr_t *rr;
+       librpz_emsg_t emsg;
+
+       rpsdb = RD_DB(rdataset);
+
+       /*
+        * Detach the previous RR.
+        */
+       if (RD_CUR_RR(rdataset) != NULL) {
+               free(RD_CUR_RR(rdataset));
+               RD_CUR_RR(rdataset) = NULL;
+       }
+
+       /*
+        * Get the next RR of the specified type.
+        * SOAs differ.
+        */
+       if (rdataset->type == dns_rdatatype_soa) {
+               if (RD_NEXT_RR(rdataset) == LIBRPZ_IDX_NULL)
+                       return (ISC_R_NOMORE);
+               RD_NEXT_RR(rdataset) = LIBRPZ_IDX_NULL;
+               if (!librpz->rsp_soa(&emsg, NULL, &rr, NULL,
+                                    &rpsdb->result, rpsdb->rsp)) {
+                       librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+                       return (DNS_R_SERVFAIL);
+               }
+               RD_CUR_RR(rdataset) = rr;
+               return (ISC_R_SUCCESS);
+       }
+
+       rpsdb->result.next_rr = RD_NEXT_RR(rdataset);
+       for (;;) {
+               if (!librpz->rsp_rr(&emsg, &type, &class, NULL, &rr,
+                                   &rpsdb->result, rpsdb->qname->ndata,
+                                   rpsdb->qname->length, rpsdb->rsp)) {
+                       librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+                       return (DNS_R_SERVFAIL);
+               }
+               if (rdataset->type == type &&
+                   rdataset->rdclass == class) {
+                       RD_CUR_RR(rdataset) = rr;
+                       RD_NEXT_RR(rdataset) = rpsdb->result.next_rr;
+                       return (ISC_R_SUCCESS);
+               }
+               if (type == dns_rdatatype_none)
+                       return (ISC_R_NOMORE);
+               free(rr);
+       }
+}
+
+static isc_result_t
+rpsdb_rdataset_first(dns_rdataset_t *rdataset) {
+       rpsdb_t *rpsdb;
+       librpz_emsg_t emsg;
+
+       rpsdb = RD_DB(rdataset);
+       REQUIRE(VALID_RPSDB(rpsdb));
+
+       if (RD_CUR_RR(rdataset) != NULL) {
+               free(RD_CUR_RR(rdataset));
+               RD_CUR_RR(rdataset) = NULL;
+       }
+
+       if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) {
+               librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+               return (DNS_R_SERVFAIL);
+       }
+       if (rdataset->type == dns_rdatatype_soa)
+               RD_NEXT_RR(rdataset) = LIBRPZ_IDX_BAD;
+       else
+               RD_NEXT_RR(rdataset) = rpsdb->result.next_rr;
+
+       return (rpsdb_rdataset_next(rdataset));
+}
+
+static void
+rpsdb_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+       rpsdb_t *rpsdb;
+       librpz_rr_t *rr;
+       isc_region_t r;
+
+       rpsdb = RD_DB(rdataset);
+       REQUIRE(VALID_RPSDB(rpsdb));
+       rr = RD_CUR_RR(rdataset);
+       REQUIRE(rr != NULL);
+
+       r.length = ntohs(rr->rdlength);
+       r.base = rr->rdata;
+       dns_rdata_fromregion(rdata, ntohs(rr->class), ntohs(rr->type), &r);
+}
+
+static void
+rpsdb_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+       rpsdb_t *rpsdb;
+       dns_db_t *dbp;
+
+       INSIST(!ISC_LINK_LINKED(target, link));
+       *target = *source;
+       ISC_LINK_INIT(target, link);
+       rpsdb = RD_DB(source);
+       REQUIRE(VALID_RPSDB(rpsdb));
+       dbp = NULL;
+       dns_db_attach(&rpsdb->common, &dbp);
+       RD_DB(target) = dbp;
+       RD_CUR_RR(target) = NULL;
+       RD_NEXT_RR(target) = LIBRPZ_IDX_NULL;
+}
+
+static unsigned int
+rpsdb_rdataset_count(dns_rdataset_t *rdataset) {
+       rpsdb_t *rpsdb;
+
+       rpsdb = RD_DB(rdataset);
+       REQUIRE(VALID_RPSDB(rpsdb));
+
+       return (RD_COUNT(rdataset));
+}
+
+static void
+rpsdb_rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+       rpsdb_t *rpsdb;
+       dns_rdatasetiter_t *iterator;
+       isc_mem_t *mctx;
+
+       iterator = *iteratorp;
+       rpsdb = (rpsdb_t *)iterator->db;
+       REQUIRE(VALID_RPSDB(rpsdb));
+
+       mctx = iterator->db->mctx;
+       dns_db_detachnode(iterator->db, &iterator->node);
+       isc_mem_put(mctx, iterator, sizeof(rpsdb_rdatasetiter_t));
+       *iteratorp = NULL;
+}
+
+static isc_result_t
+rpsdb_rdatasetiter_next(dns_rdatasetiter_t *iter) {
+       rpsdb_t *rpsdb;
+       rpsdb_rdatasetiter_t *rpsdb_iter;
+       dns_rdatatype_t next_type, type;
+       dns_rdataclass_t next_class, class;
+       uint32_t ttl;
+       librpz_emsg_t emsg;
+
+       rpsdb = (rpsdb_t *)iter->db;
+       REQUIRE(VALID_RPSDB(rpsdb));
+       rpsdb_iter = (rpsdb_rdatasetiter_t *)iter;
+
+       /*
+        * This function is only used after a policy has been chosen,
+        * and so without caring whether it is after recursion.
+        */
+       if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) {
+               librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+               return (DNS_R_SERVFAIL);
+       }
+       /*
+        * Find the next class and type after the current class and type
+        * among the RRs in current result.
+        * As a side effect, count the number of those RRs.
+        */
+       rpsdb_iter->count = 0;
+       next_class = dns_rdataclass_reserved0;
+       next_type = dns_rdatatype_none;
+       for (;;) {
+               if (!librpz->rsp_rr(&emsg, &type, &class, &ttl,
+                                   NULL, &rpsdb->result, rpsdb->qname->ndata,
+                                   rpsdb->qname->length, rpsdb->rsp)) {
+                       librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+                       return (DNS_R_SERVFAIL);
+               }
+               if (type == dns_rdatatype_none) {
+                       if (next_type == dns_rdatatype_none)
+                               return (ISC_R_NOMORE);
+                       rpsdb_iter->type = next_type;
+                       rpsdb_iter->class = next_class;
+                       return (ISC_R_SUCCESS);
+               }
+               /*
+                * Skip RRs with the current class and type or before.
+                */
+               if (rpsdb_iter->class > class ||
+                   (rpsdb_iter->class = class && rpsdb_iter->type >= type))
+                       continue;
+               if (next_type == dns_rdatatype_none ||
+                   next_class > class ||
+                   (next_class == class && next_type > type)) {
+                       /*
+                        * This is the first of a subsequent class and type.
+                        */
+                       next_type = type;
+                       next_class = class;
+                       rpsdb_iter->ttl = ttl;
+                       rpsdb_iter->count = 1;
+                       rpsdb_iter->next_rr = rpsdb->result.next_rr;
+               } else if (next_type == type && next_class == class) {
+                       ++rpsdb_iter->count;
+               }
+       }
+}
+
+static isc_result_t
+rpsdb_rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+       rpsdb_t *rpsdb;
+       rpsdb_rdatasetiter_t *rpsdb_iter;
+
+       rpsdb = (rpsdb_t *)iterator->db;
+       REQUIRE(VALID_RPSDB(rpsdb));
+       rpsdb_iter = (rpsdb_rdatasetiter_t *)iterator;
+
+       rpsdb_iter->type = dns_rdatatype_none;
+       rpsdb_iter->class = dns_rdataclass_reserved0;
+       return (rpsdb_rdatasetiter_next(iterator));
+}
+
+static void
+rpsdb_rdatasetiter_current(dns_rdatasetiter_t *iterator,
+                          dns_rdataset_t *rdataset)
+{
+       rpsdb_t *rpsdb;
+       rpsdb_rdatasetiter_t *rpsdb_iter;
+
+       rpsdb = (rpsdb_t *)iterator->db;
+       REQUIRE(VALID_RPSDB(rpsdb));
+       rpsdb_iter = (rpsdb_rdatasetiter_t *)iterator;
+       REQUIRE(rpsdb_iter->type != dns_rdatatype_none);
+
+       rpsdb_bind_rdataset(rdataset,
+                            rpsdb_iter->count, rpsdb_iter->next_rr,
+                            rpsdb_iter->type, rpsdb_iter->class,
+                            rpsdb_iter->ttl, rpsdb);
+}
+
+static dns_dbmethods_t rpsdb_db_methods = {
+       rpsdb_attach,
+       rpsdb_detach,
+       NULL,                   /* beginload */
+       NULL,                   /* endload */
+       NULL,                   /* serialize */
+       NULL,                   /* dump */
+       NULL,                   /* currentversion */
+       NULL,                   /* newversion */
+       NULL,                   /* attachversion */
+       NULL,                   /* closeversion */
+       rpsdb_findnode,
+       rpsdb_finddb,
+       NULL,                   /* findzonecut*/
+       rpsdb_attachnode,
+       rpsdb_detachnode,
+       NULL,                   /* expirenode */
+       NULL,                   /* printnode */
+       NULL,                   /* createiterator */
+       rpsdb_findrdataset,
+       rpsdb_allrdatasets,
+       NULL,                   /* addrdataset */
+       NULL,                   /* subtractrdataset */
+       NULL,                   /* deleterdataset */
+       rpsdb_issecure,
+       NULL,                   /* nodecount */
+       NULL,                   /* ispersistent */
+       NULL,                   /* overmem */
+       NULL,                   /* settask */
+       rpsdb_getoriginnode,
+       NULL,                   /* transfernode */
+       NULL,                   /* getnsec3parameters */
+       NULL,                   /* findnsec3node */
+       NULL,                   /* setsigningtime */
+       NULL,                   /* getsigningtime */
+       NULL,                   /* resigned */
+       NULL,                   /* isdnssec */
+       NULL,                   /* getrrsetstats */
+       NULL,                   /* rpz_attach */
+       NULL,                   /* rpz_ready */
+       NULL,                   /* findnodeext */
+       NULL,                   /* findext */
+       NULL,                   /* setcachestats */
+       NULL,                   /* hashsize */
+       NULL,                   /* nodefullname */
+       NULL                    /* getsize */
+};
+
+static dns_rdatasetmethods_t rpsdb_rdataset_methods = {
+       rpsdb_rdataset_disassociate,
+       rpsdb_rdataset_first,
+       rpsdb_rdataset_next,
+       rpsdb_rdataset_current,
+       rpsdb_rdataset_clone,
+       rpsdb_rdataset_count,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL
+};
+
+static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods = {
+       rpsdb_rdatasetiter_destroy,
+       rpsdb_rdatasetiter_first,
+       rpsdb_rdatasetiter_next,
+       rpsdb_rdatasetiter_current
+};
+
+#endif /* USE_DNSRPS */
index 0f13808f91ca2e096ad3ea481babe79cdd7be709..dec7a7b86127ae5b5359069e33e44d484ce4fb75 100644 (file)
@@ -14,12 +14,12 @@ HEADERS =   acl.h adb.h badcache.h bit.h byaddr.h \
                cache.h callbacks.h catz.h cert.h \
                client.h clientinfo.h compress.h \
                db.h dbiterator.h dbtable.h diff.h dispatch.h \
-               dlz.h dlz_dlopen.h dns64.h dnssec.h ds.h dsdigest.h \
+               dlz.h dlz_dlopen.h dns64.h dnsrps.h dnssec.h ds.h dsdigest.h \
                dnstap.h dyndb.h ecs.h \
                edns.h ecdb.h events.h fixedname.h forward.h geoip.h \
                ipkeylist.h iptable.h \
                journal.h keydata.h keyflags.h keytable.h keyvalues.h \
-               lib.h lookup.h log.h master.h masterdump.h message.h \
+               lib.h librpz.h lookup.h log.h master.h masterdump.h message.h \
                name.h ncache.h nsec.h nsec3.h nta.h opcode.h order.h \
                peer.h portlist.h private.h \
                rbt.h rcode.h rdata.h rdataclass.h rdatalist.h \
diff --git a/lib/dns/include/dns/dnsrps.h b/lib/dns/include/dns/dnsrps.h
new file mode 100644 (file)
index 0000000..f0acc4f
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef DNS_DNSRPS_H
+#define DNS_DNSRPS_H
+
+#include <isc/lang.h>
+#include <dns/types.h>
+
+#include <config.h>
+
+#ifdef USE_DNSRPS
+
+#include <dns/librpz.h>
+#include <dns/rpz.h>
+
+/*
+ * Error message if dlopen(librpz) failed.
+ */
+extern librpz_emsg_t librpz_lib_open_emsg;
+
+
+/*
+ * These shim BIND9 database, node, and rdataset are handles on RRs from librpz.
+ *
+ * All of these structures are used by a single thread and so need no locks.
+ *
+ * rpsdb_t holds the state for a set of RPZ queries.
+ *
+ * rpsnode_t is a link to the rpsdb_t for the set of  RPZ queries
+ * and a flag saying whether it is pretending to be a node with RRs for
+ * the qname or the node with the SOA for the zone containing the rewritten
+ * RRs or justifying NXDOMAIN.
+ */
+typedef struct {
+       uint8_t                 unused;
+} rpsnode_t;
+typedef struct rpsdb {
+       dns_db_t                common;
+       int                     ref_cnt;
+       librpz_result_id_t      hit_id;
+       librpz_result_t         result;
+       librpz_rsp_t*           rsp;
+       librpz_domain_buf_t     origin_buf;
+       const dns_name_t        *qname;
+       rpsnode_t               origin_node;
+       rpsnode_t               data_node;
+} rpsdb_t;
+
+
+/*
+ * Convert a dnsrps policy to a classic BIND9 RPZ policy.
+ */
+dns_rpz_policy_t dns_dnsrps_2policy(librpz_policy_t rps_policy);
+
+/*
+ * Convert a dnsrps trigger to a classic BIND9 RPZ rewrite or trigger type.
+ */
+dns_rpz_type_t dns_dnsrps_trig2type(librpz_trig_t trig);
+
+/*
+ * Convert a classic BIND9 RPZ rewrite or trigger type to a librpz trigger type.
+ */
+librpz_trig_t dns_dnsrps_type2trig(dns_rpz_type_t type);
+
+/*
+ * Start dnsrps for the entire server.
+ */
+isc_result_t dns_dnsrps_server_create(void);
+
+/*
+ * Stop dnsrps for the entire server.
+ */
+void dns_dnsrps_server_destroy(void);
+
+/*
+ * Ready dnsrps for a view.
+ */
+isc_result_t dns_dnsrps_view_init(dns_rpz_zones_t *new, char *rps_cstr);
+
+/*
+ * Connect to and start the dnsrps daemon, dnsrpzd.
+ */
+isc_result_t dns_dnsrps_connect(dns_rpz_zones_t *rpzs);
+
+/*
+ * Get ready to try dnsrps rewriting.
+ */
+isc_result_t dns_dnsrps_rewrite_init(librpz_emsg_t *emsg, dns_rpz_st_t *st,
+                                     dns_rpz_zones_t *rpzs,
+                                     const dns_name_t *qname, isc_mem_t *mctx,
+                                     isc_boolean_t have_rd);
+
+#endif /* USE_DNSRPS */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DNSRPS_H */
diff --git a/lib/dns/include/dns/librpz.h b/lib/dns/include/dns/librpz.h
new file mode 100644 (file)
index 0000000..75778a3
--- /dev/null
@@ -0,0 +1,963 @@
+/*
+ * Copyright (C) 2017  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Define the interface from a DNS resolver to the Response Policy Zone
+ * library, librpz.
+ *
+ * This file should be included only the interface functions between the
+ * resolver and librpz to avoid name space pollution.
+ *
+ * Copyright (c) 2016-2017 Farsight Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * version 1.2.12
+ */
+
+#ifndef LIBRPZ_H
+#define LIBRPZ_H
+
+#include <arpa/nameser.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+
+/*
+ * Allow either ordinary or dlopen() linking.
+ */
+#ifdef LIBRPZ_INTERNAL
+#define LIBDEF(t,s) extern t s;
+#define LIBDEF_F(f) LIBDEF(librpz_##f##_t, librpz_##f)
+#else
+#define LIBDEF(t,s)
+#define LIBDEF_F(f)
+#endif
+
+/*
+ * Response Policy Zone triggers.
+ *     Comparisons of trigger precedences require
+ *     LIBRPZ_TRIG_CLIENT_IP < LIBRPZ_TRIG_QNAME < LIBRPZ_TRIG_IP
+ *         < LIBRPZ_TRIG_NSDNAME < LIBRPZ_TRIG_NSIP}
+ */
+typedef enum {
+       LIBRPZ_TRIG_BAD         =0,
+       LIBRPZ_TRIG_CLIENT_IP   =1,
+       LIBRPZ_TRIG_QNAME       =2,
+       LIBRPZ_TRIG_IP          =3,
+       LIBRPZ_TRIG_NSDNAME     =4,
+       LIBRPZ_TRIG_NSIP        =5
+} librpz_trig_t;
+#define LIBRPZ_TRIG_SIZE       3       /* sizeof librpz_trig_t in bits */
+typedef uint8_t                librpz_tbit_t;  /* one bit for each of the TRIGS_NUM
+                                        * trigger types */
+
+
+/*
+ * Response Policy Zone Actions or policies
+ */
+typedef enum {
+       LIBRPZ_POLICY_UNDEFINED =0,     /* an empty entry or no decision yet */
+       LIBRPZ_POLICY_DELETED   =1,     /* placeholder for a deleted policy */
+
+       LIBRPZ_POLICY_PASSTHRU  =2,     /* 'passthru': do not rewrite */
+       LIBRPZ_POLICY_DROP      =3,     /* 'drop': do not respond */
+       LIBRPZ_POLICY_TCP_ONLY  =4,     /* 'tcp-only': answer UDP with TC=1 */
+       LIBRPZ_POLICY_NXDOMAIN  =5,     /* 'nxdomain': answer with NXDOMAIN */
+       LIBRPZ_POLICY_NODATA    =6,     /* 'nodata': answer with ANCOUNT=0 */
+       LIBRPZ_POLICY_RECORD    =7,     /* rewrite with the policy's RR */
+
+       /* only in client configurations to override the zone */
+       LIBRPZ_POLICY_GIVEN,            /* 'given': what policy record says */
+       LIBRPZ_POLICY_DISABLED,         /* at most log */
+       LIBRPZ_POLICY_CNAME,            /* answer with 'cname x' */
+} librpz_policy_t;
+#define LIBRPZ_POLICY_BITS     4
+
+/*
+ * Special policies that appear as targets of CNAMEs
+ * NXDOMAIN is signaled by a CNAME with a "." target.
+ * NODATA is signaled by a CNAME with a "*." target.
+ */
+#define LIBRPZ_RPZ_PREFIX      "rpz-"
+#define LIBRPZ_RPZ_PASSTHRU    LIBRPZ_RPZ_PREFIX"passthru"
+#define LIBRPZ_RPZ_DROP                LIBRPZ_RPZ_PREFIX"drop"
+#define LIBRPZ_RPZ_TCP_ONLY    LIBRPZ_RPZ_PREFIX"tcp-only"
+
+
+typedef        uint16_t    librpz_dznum_t;     /* dnsrpzd zone # in [0,DZNUM_MAX] */
+typedef        uint8_t     librpz_cznum_t;     /* client zone # in [0,CZNUM_MAX] */
+
+
+/*
+ * CIDR block
+ */
+typedef struct librpz_prefix {
+       union {
+               struct in_addr  in;
+               struct in6_addr in6;
+       } addr;
+       uint8_t             family;
+       uint8_t             len;
+} librpz_prefix_t;
+
+/*
+ * A domain
+ */
+typedef uint8_t        librpz_dsize_t;
+typedef struct librpz_domain {
+       librpz_dsize_t      size;       /* of only .d */
+       uint8_t             d[0];       /* variable length wire format */
+} librpz_domain_t;
+
+/*
+ * A maximal domain buffer
+ */
+typedef struct librpz_domain_buf {
+       librpz_dsize_t      size;
+       uint8_t             d[NS_MAXCDNAME];
+} librpz_domain_buf_t;
+
+/*
+ * A resource record without the owner name.
+ * C compilers say that sizeof(librpz_rr_t)=12 instead of 10.
+ */
+typedef struct {
+       uint16_t            type;       /* network byte order */
+       uint16_t            class;      /* network byte order */
+       uint32_t            ttl;        /* network byte order */
+       uint16_t            rdlength;   /* network byte order */
+       uint8_t             rdata[0];   /* variable length */
+} librpz_rr_t;
+
+/*
+ * The database file might be mapped with different starting addresses
+ * by concurrent clients (resolvers), and so all pointers are offsets.
+ */
+typedef uint32_t       librpz_idx_t;
+#define LIBRPZ_IDX_NULL        0
+#define LIBRPZ_IDX_MIN 1
+#define LIBRPZ_IDX_BAD  ((librpz_idx_t)-1)
+/**
+ * Partial decoded results of a set of RPZ queries for a single DNS response
+ * or iteration through the mapped file.
+ */
+typedef int16_t librpz_result_id_t;
+typedef struct librpz_result {
+       librpz_idx_t        next_rr;
+       librpz_result_id_t  hit_id;             /* trigger ID from resolver */
+       librpz_policy_t     zpolicy;    /* policy from zone */
+       librpz_policy_t     policy;     /* adjusted by client configuration */
+       librpz_dznum_t      dznum;      /* dnsrpzd zone number */
+       librpz_cznum_t      cznum;      /* librpz client zone number */
+       librpz_trig_t       trig:LIBRPZ_TRIG_SIZE;
+       bool                log:1;      /* log rewrite given librpz_log_level */
+} librpz_result_t;
+
+
+/**
+ * librpz trace or log levels.
+ */
+typedef enum {
+       LIBRPZ_LOG_FATAL    =0,         /* always print fatal errors */
+       LIBRPZ_LOG_ERROR    =1,         /* errors have this level */
+       LIBRPZ_LOG_TRACE1   =2,         /* big events such as dnsrpzd starts */
+       LIBRPZ_LOG_TRACE2   =3,         /* smaller dnsrpzd zone transfers */
+       LIBRPZ_LOG_TRACE3   =4,         /* librpz hits */
+       LIBRPZ_LOG_TRACE4   =5,         /* librpz lookups */
+       LIBRPZ_LOG_INVALID   =999,
+} librpz_log_level_t;
+typedef librpz_log_level_t (librpz_log_level_val_t)(librpz_log_level_t level);
+LIBDEF_F(log_level_val)
+
+/**
+ * Logging function that can be supplied by the resolver.
+ * @param level is one of librpz_log_level_t
+ * @param ctx is for use by the resolver's logging system.
+ *     NULL mean a context-free message.
+ */
+typedef void(librpz_log_fnc_t)(librpz_log_level_t level, void *ctx,
+                              const char *buf);
+
+/**
+ * Point librpz logging functions to the resolver's choice.
+ */
+typedef void (librpz_set_log_t)(librpz_log_fnc_t *new_log, const char *prog_nm);
+LIBDEF_F(set_log)
+
+
+/**
+ * librpz error messages are put in these buffers.
+ * Use a structure instead of naked char* to let the compiler check the length.
+ * A function defined with "foo(char buf[120])" can be called with
+ * "char sbuf[2]; foo(sbuf)" and suffer a buffer overrun.
+ */
+typedef struct {
+       char    c[120];
+} librpz_emsg_t;
+
+
+#ifdef LIBRPZ_HAVE_ATTR
+#define LIBRPZ_UNUSED  __attribute__((unused))
+#define LIBRPZ_PF(f,l) __attribute__((format(printf,f,l)))
+#define        LIBRPZ_NORET    __attribute__((__noreturn__))
+#else
+#define LIBRPZ_UNUSED
+#define LIBRPZ_PF(f,l)
+#define        LIBRPZ_NORET
+#endif
+
+#ifdef HAVE_BUILTIN_EXPECT
+#define LIBRPZ_LIKELY(c) __builtin_expect(!!(c), 1)
+#define LIBRPZ_UNLIKELY(c) __builtin_expect(!!(c), 0)
+#else
+#define LIBRPZ_LIKELY(c) (c)
+#define LIBRPZ_UNLIKELY(c) (c)
+#endif
+
+typedef bool (librpz_parse_log_opt_t)(librpz_emsg_t *emsg, const char *arg);
+LIBDEF_F(parse_log_opt)
+
+typedef void (librpz_vpemsg_t)(librpz_emsg_t *emsg,
+                              const char *p, va_list args);
+LIBDEF_F(vpemsg)
+typedef void (librpz_pemsg_t)(librpz_emsg_t *emsg,
+                             const char *p, ...) LIBRPZ_PF(2,3);
+LIBDEF_F(pemsg)
+
+typedef void (librpz_vlog_t)(librpz_log_level_t level, void *ctx,
+                            const char *p, va_list args);
+LIBDEF_F(vlog)
+typedef void (librpz_log_t)(librpz_log_level_t level, void *ctx,
+                           const char *p, ...) LIBRPZ_PF(3,4);
+LIBDEF_F(log)
+
+typedef void (librpz_fatal_t)(int ex_code,
+                             const char *p, ...) LIBRPZ_PF(2,3);
+extern void librpz_fatal(int ex_code,
+                        const char *p, ...) LIBRPZ_PF(2,3) LIBRPZ_NORET;
+
+typedef void (librpz_rpz_assert_t)(const char *file, unsigned line,
+                                  const char *p, ...) LIBRPZ_PF(3,4);
+extern void librpz_rpz_assert(const char *file, unsigned line,
+                             const char *p, ...) LIBRPZ_PF(3,4) LIBRPZ_NORET;
+
+typedef void (librpz_rpz_vassert_t)(const char *file, uint line,
+                                   const char *p, va_list args);
+extern void librpz_rpz_vassert(const char *file, uint line,
+                              const char *p, va_list args) LIBRPZ_NORET;
+
+
+/*
+ * As far as clients are concerned, all relative pointers or indexes in a
+ * version of the mapped file except trie node parent pointers remain valid
+ * forever.  A client must release a version so that it can be garbage
+ * collected by the file system.  When dnsrpzd needs to expand the file,
+ * it copies the old file to a new, larger file.  Clients can continue
+ * using the old file.
+ *
+ * Versions can also appear in a single file.  Old nodes and trie values
+ * within the file are not destroyed until all clients using the version
+ * that contained the old values release the version.
+ *
+ * A client is marked as using version by connecting to the daemon.  It is
+ * marked as using all subsequent versions.  A client releases all versions
+ * by closing the connection or a range of versions by updating is slot
+ * in the shared memory version table.
+ *
+ * As far as clients are concerned, there are the following possible librpz
+ * failures:
+ *     - malloc() or other fatal internal librpz problems indicated by
+ *         a failing return from a librpz function
+ *         All operations will fail until client handle is destroyed and
+ *         recreated with librpz_client_detach() and librpz_client_create().
+ *     - corrupt database detected by librpz code, corrupt database detected
+ *         by dnsrpzd, or disconnection from the daemon.
+ *         Current operations will fail.
+ *
+ * Clients assume that the file has already been unlinked before
+ *     the corrupt flag is set so that they do not race with the server
+ *     over the corruption of a single file.  A client that finds the
+ *     corrupt set knows that dnsrpzd has already crashed with
+ *     abort() and is restarting.  The client can re-connect to dnsrpzd
+ *     and retransmit its configuration, backing off as usual if anything
+ *     goes wrong.
+ *
+ * Searches of the database by a client do not need locks against dnsrpzd or
+ *     other clients, but a lock is used to protect changes to the connection
+ *     by competing threads in the client.  The client provides functions
+ *     to serialize the concurrent use of any single client handle.
+ *     Functions that do nothing are appropriate for applications that are
+ *     not "threaded" or that do not share client handles among threads.
+ *     Otherwise, functions must be provided to librpz_clientcreate().
+ *     Something like the following works with pthreads:
+ *
+ * static void
+ * lock(void *mutex) { assert(pthread_mutex_lock(mutex) == 0); }
+ *
+ * static void
+ * unlock(void *mutex) { assert(pthread_mutex_unlock(mutex) == 0); }
+ *
+ * static void
+ * mutex_destroy(void *mutex) { assert(pthread_mutex_destroy(mutex) == 0); }
+ *
+ *
+ *
+ * At every instant, all of the data and pointers in the mapped file are valid.
+ *     Changes to trie node or other data are always made so that it and
+ *     all pointers in and to it remain valid for a time.  Old versions are
+ *     eventually discarded.
+ *
+ * Dnsrpzd periodically defines a new version by setting aside all changes
+ *     made since the previous version was defined.  Subsequent changes
+ *     made (only!) by dnsrpzd will be part of the next version.
+ *
+ * To discard an old version, dnsrpzd must know that all clients have stopped
+ *     using that version.  Clients do that by using part of the mapped file
+ *     to tell dnsrpzd the oldest version that each client is using.
+ *     Dnsrpzd assigns each connecting client an entry in the cversions array
+ *     in the mapped file.  The client puts version numbers into that entry
+ *     to signal to dnsrpzd which versions that can be discarded.
+ *     Dnsrpzd is free, as far as that client is concerned, to discard all
+ *     numerically smaller versions.  A client can disclaim all versions with
+ *     the version number VERSIONS_ALL or 0.
+ *
+ * The race between a client changing its entry and dnsrpzd discarding a
+ *     version is resolved by allowing dnsrpzd to discard all versions
+ *     smaller or equal to the client's version number.  If dnsrpzd is in
+ *     the midst of discarding or about to discard version N when the
+ *     client asserts N, no harm is done.  The client depends only on
+ *     the consistency of version N+1.
+ *
+ * This version mechanism depends in part on not being exercised too frequently
+ *     Version numbers are 32 bits long and dnsrpzd creates new versions
+ *     at most once every 30 seconds.
+ */
+
+
+/*
+ * Lock functions for concurrent use of a single librpz_client_t client handle.
+ */
+typedef void(librpz_mutex_t)(void *mutex);
+
+/*
+ * List of connections to dnsrpzd daemons.
+ */
+typedef struct librpz_clist librpz_clist_t;
+
+/*
+ * Client's handle on dnsrpzd.
+ */
+typedef struct librpz_client librpz_client_t;
+
+/**
+ * Create the list of connections to the dnsrpzd daemon.
+ * @param[out] emsg: error message
+ * @param lock: start exclusive access to the client handle
+ * @param unlock: end exclusive access to the client handle
+ * @param mutex_destroy: release the lock
+ * @param mutex: pointer to the lock for the client handle
+ * @param log_ctx: NULL or resolver's context log messages
+ */
+typedef librpz_clist_t *(librpz_clist_create_t)(librpz_emsg_t *emsg,
+                                               librpz_mutex_t *lock,
+                                               librpz_mutex_t *unlock,
+                                               librpz_mutex_t *mutex_destroy,
+                                               void *mutex, void *log_ctx);
+LIBDEF_F(clist_create)
+
+
+/**
+ * Release the list of dnsrpzd connections.
+ */
+typedef void (librpz_clist_detach_t)(librpz_clist_t **clistp);
+LIBDEF_F(clist_detach)
+
+/**
+ * Create a librpz client handle.
+ * @param[out] emsg: error message
+ * @param: list of dnsrpzd connections
+ * @param cstr: string of configuration settings separated by ';' or '\n'
+ * @param use_expired: true to not ignore expired zones
+ * @return client handle or NULL if the handle could not be created
+ */
+typedef librpz_client_t *(librpz_client_create_t)(librpz_emsg_t *emsg,
+                                                 librpz_clist_t *clist,
+                                                 const char *cstr,
+                                                 bool use_expired);
+LIBDEF_F(client_create)
+
+/**
+ * Start (if necessary) dnsrpzd and connect to it.
+ * @param[out] emsg: error message
+ * @param client handle
+ * @param optional: true if it is ok if starting the daemon is not allowed
+ */
+typedef bool (librpz_connect_t)(librpz_emsg_t *emsg, librpz_client_t *client,
+                               bool optional);
+LIBDEF_F(connect)
+
+/**
+ * Start to destroy a librpz client handle.
+ * It will not be destroyed until the last set of RPZ queries represented
+ * by a librpz_rsp_t ends.
+ * @param client handle to be released
+ * @return false on error
+ */
+typedef void (librpz_client_detach_t)(librpz_client_t **clientp);
+LIBDEF_F(client_detach)
+
+/**
+ * State for a set of RPZ queries for a single DNS response
+ * or for listing the database.
+ */
+typedef struct librpz_rsp librpz_rsp_t;
+
+/**
+ * Start a set of RPZ queries for a single DNS response.
+ * @param[out] emsg: error message for false return or *rspp=NULL
+ * @param[out] rspp created context or NULL
+ * @param[out] min_ns_dotsp: NULL or pointer to configured MIN-NS-DOTS value
+ * @param client state
+ * @param have_rd: RD=1 in the DNS request
+ * @param have_do: DO=1 in the DNS request
+ * @return false on error
+ */
+typedef bool (librpz_rsp_create_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp,
+                                  int *min_ns_dotsp, librpz_client_t *client,
+                                  bool have_rd, bool have_do);
+LIBDEF_F(rsp_create)
+
+/**
+ * Finish RPZ work for a DNS response.
+ */
+typedef void (librpz_rsp_detach_t)(librpz_rsp_t **rspp);
+LIBDEF_F(rsp_detach)
+
+/**
+ * Get the final, accumulated result of a set of RPZ queries.
+ * Yield LIBRPZ_POLICY_UNDEFINED if
+ *  - there were no hits,
+ *  - there was a dispositive hit, be we have not recursed and are required
+ *     to recurse so that evil DNS authorities will not know we are using RPZ
+ *  - we have a hit and have recursed, but later data such as NSIP could
+ *     override
+ * @param[out] emsg
+ * @param[out] result describes the hit
+ *     or result->policy=LIBRPZ_POLICY_UNDEFINED without a hit
+ * @param[out] result: current policy rewrite values
+ * @param recursed: recursion has now been done even if it was not done
+ *     when the hit was found
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_result_t)(librpz_emsg_t *emsg, librpz_result_t *result,
+                                  bool recursed, const librpz_rsp_t *rsp);
+LIBDEF_F(rsp_result)
+
+/**
+ * Might looking for a trigger be worthwhile?
+ * @param trig: look for this type of trigger
+ * @param ipv6: true if trig is LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP,
+ *     or LIBRPZ_TRIG_NSIP and the IP address is IPv6
+ * @return: true if looking could be worthwhile
+ */
+typedef bool (librpz_have_trig_t)(librpz_trig_t trig, bool ipv6,
+                                 const librpz_rsp_t *rsp);
+LIBDEF_F(have_trig)
+
+/**
+ * Might looking for NSDNAME and NSIP triggers be worthwhile?
+ * @return: true if looking could be worthwhile
+ */
+typedef bool (librpz_have_ns_trig_t)(const librpz_rsp_t *rsp);
+LIBDEF_F(have_ns_trig)
+
+/**
+ * Convert the found client IP trie key to a CIDR block
+ * @param[out] emsg
+ * @param[out] prefix trigger
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_clientip_prefix_t)(librpz_emsg_t *emsg,
+                                           librpz_prefix_t *prefix,
+                                           librpz_rsp_t *rsp);
+LIBDEF_F(rsp_clientip_prefix)
+
+/**
+ * Compute the owner name of the found or result trie key, usually to log it.
+ * An IP address key might be returned as 8.0.0.0.127.rpz-client-ip.
+ * example.com. might be a qname trigger.  example.com.rpz-nsdname. could
+ * be an NSDNAME trigger.
+ * @param[out] emsg
+ * @param[out] owner domain
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_domain_t)(librpz_emsg_t *emsg,
+                                  librpz_domain_buf_t *owner,
+                                  librpz_rsp_t *rsp);
+LIBDEF_F(rsp_domain)
+
+/**
+ * Get the next RR of the LIBRPZ_POLICY_RECORD result after an initial use of
+ * librpz_rsp_result() or librpz_itr_node() or after a previous use of
+ * librpz_rsp_rr().  The RR is in uncompressed wire format including type,
+ * class, ttl and length in network byte order.
+ * @param[out] emsg
+ * @param[out] typep: optional host byte order record type or ns_t_invalid (0)
+ * @param[out] classp: class such as ns_c_in
+ * @param[out] ttlp: TTL
+ * @param[out] rrp: optional malloc() buffer containing the next RR or
+ *     NULL after the last RR
+ * @param[out] result: current policy rewrite values
+ * @param qname: used construct a wildcard CNAME
+ * @param qname_size
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_rr_t)(librpz_emsg_t *emsg, uint16_t *typep,
+                              uint16_t *classp, uint32_t *ttlp,
+                              librpz_rr_t **rrp, librpz_result_t *result,
+                              const uint8_t *qname, size_t qname_size,
+                              librpz_rsp_t *rsp);
+LIBDEF_F(rsp_rr)
+
+/**
+ * Get the next RR of the LIBRPZ_POLICY_RECORD result.
+ * @param[out] emsg
+ * @param[out] ttlp: TTL
+ * @param[out] rrp: malloc() buffer with SOA RR without owner name
+ * @param[out] result: current policy rewrite values
+ * @param[out] origin: SOA owner name
+ * @param[out] origin_size
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_soa_t)(librpz_emsg_t *emsg, uint32_t *ttlp,
+                               librpz_rr_t **rrp, librpz_domain_buf_t *origin,
+                               librpz_result_t *result, librpz_rsp_t *rsp);
+LIBDEF_F(rsp_soa)
+
+/**
+ * Get the SOA serial number for a policy zone to compare with a known value
+ * to check whether a zone transfer is complete.
+ */
+typedef bool (librpz_soa_serial_t)(librpz_emsg_t *emsg, uint32_t *serialp,
+                                  const char *domain_nm, librpz_rsp_t *rsp);
+LIBDEF_F(soa_serial)
+
+/**
+ * Save the current policy checking state.
+ * @param[out] emsg
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_push_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
+LIBDEF_F(rsp_push)
+#define LIBRPZ_RSP_STACK_DEPTH 3
+
+/**
+ * Restore the previous policy checking state.
+ * @param[out] emsg
+ * @param[out] result: NULL or restored policy rewrite values
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_pop_t)(librpz_emsg_t *emsg, librpz_result_t *result,
+                               librpz_rsp_t *rsp);
+LIBDEF_F(rsp_pop)
+
+/**
+ * Discard the most recently save policy checking state.
+ * @param[out] emsg
+ * @param[out] result: NULL or restored policy rewrite values
+ * @return false on error
+ */
+typedef bool (librpz_rsp_pop_discard_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
+LIBDEF_F(rsp_pop_discard)
+
+/**
+ * Disable a zone.
+ * @param[out] emsg
+ * @param znum
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_forget_zone_t)(librpz_emsg_t *emsg,
+                                       librpz_cznum_t znum, librpz_rsp_t *rsp);
+LIBDEF_F(rsp_forget_zone)
+
+/**
+ * Apply RPZ to an IP address.
+ * @param[out] emsg
+ * @param addr: address to check
+ * @param ipv6: true for 16 byte IPv6 instead of 4 byte IPv4
+ * @param trig LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP, or LIBRPZ_TRIG_NSIP
+ * @param hit_id: caller chosen
+ * @param recursed: recursion has been done
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_ck_ip_t)(librpz_emsg_t *emsg,
+                             const void *addr, uint family,
+                             librpz_trig_t trig, librpz_result_id_t hit_id,
+                             bool recursed, librpz_rsp_t *rsp);
+LIBDEF_F(ck_ip)
+
+/**
+ * Apply RPZ to a wire-format domain.
+ * @param[out] emsg
+ * @param domain in wire format
+ * @param domain_size
+ * @param trig LIBRPZ_TRIG_QNAME or LIBRPZ_TRIG_NSDNAME
+ * @param hit_id: caller chosen
+ * @param recursed: recursion has been done
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_ck_domain_t)(librpz_emsg_t *emsg,
+                                 const uint8_t *domain, size_t domain_size,
+                                 librpz_trig_t trig, librpz_result_id_t hit_id,
+                                 bool recursed, librpz_rsp_t *rsp);
+LIBDEF_F(ck_domain)
+
+/**
+ * Ask dnsrpzd to refresh a zone.
+ * @param[out] emsg error message
+ * @param librpz_domain_t domain to refresh
+ * @param client context
+ * @return false after error
+ */
+typedef bool (librpz_zone_refresh_t)(librpz_emsg_t *emsg, const char *domain,
+                                      librpz_rsp_t *rsp);
+LIBDEF_F(zone_refresh)
+
+/**
+ * Get a string describing the database
+ * @param license: include the license
+ * @param cfiles: include the configuration file names
+ * @param listens: include the local notify IP addresses
+ * @param[out] emsg error message if the result is null
+ * @param client context
+ * @return malloc'ed string or NULL after error
+ */
+typedef char *(librpz_db_info_t)(librpz_emsg_t *emsg,
+                                bool license, bool cfiles, bool listens,
+                                librpz_rsp_t *rsp);
+LIBDEF_F(db_info)
+
+/**
+ * Start a context for listing the nodes and/or zones in the mapped file
+ * @param[out] emsg: error message for false return or *rspp=NULL
+ * @param[out[ rspp created context or NULL
+ * @param client context
+ * @return false after error
+ */
+typedef bool (librpz_itr_start_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp,
+                                 librpz_client_t *client);
+LIBDEF_F(itr_start)
+
+/**
+ * Get mapped file memory allocation statistics.
+ * @param[out] emsg: error message
+ * @param rsp state from librpz_itr_start()
+ * @return malloc'ed string or NULL after error
+ */
+typedef char *(librpz_mf_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
+LIBDEF_F(mf_stats)
+
+/**
+ * Get versions currently used by clients.
+ * @param[out] emsg: error message
+ * @param[in,out] rsp: state from librpz_itr_start()
+ * @return malloc'ed string or NULL after error
+ */
+typedef char *(librpz_vers_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
+LIBDEF_F(vers_stats)
+
+/**
+ * Allocate a string describing the next zone or "" after the last zone.
+ * @param[out] emsg
+ * @param all_zones to list all instead of only requested zones
+ * @param[in,out] rsp state from librpz_rsp_start()
+ * @return malloc'ed string or NULL after error
+ */
+typedef char *(librpz_itr_zone_t)(librpz_emsg_t *emsg, bool all_zones,
+                                 librpz_rsp_t *rsp);
+LIBDEF_F(itr_zone)
+
+/**
+ * Describe the next trie node while dumping the database.
+ * @param[out] emsg
+ * @param[out] result describes node
+ *     or result->policy=LIBRPZ_POLICY_UNDEFINED after the last node.
+ * @param all_zones to list all instead of only requested zones
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return: false on error
+ */
+typedef bool (librpz_itr_node_t)(librpz_emsg_t *emsg, librpz_result_t *result,
+                                bool all_zones, librpz_rsp_t *rsp);
+LIBDEF_F(itr_node)
+
+/**
+ * RPZ policy to string with a backup buffer of POLICY2STR_SIZE size
+ */
+typedef const char *(librpz_policy2str_t)(librpz_policy_t policy,
+                                         char *buf, size_t buf_size);
+#define POLICY2STR_SIZE sizeof("policy xxxxxx")
+LIBDEF_F(policy2str)
+
+/**
+ * Trigger type to string.
+ */
+typedef const char *(librpz_trig2str_t)(librpz_trig_t trig);
+LIBDEF_F(trig2str)
+
+/**
+ * Convert a number of seconds to a zone file duration string
+ */
+typedef const char *(librpz_secs2str_t)(time_t secs,
+                                       char *buf, size_t buf_size);
+#define SECS2STR_SIZE sizeof("1234567w7d24h59m59s")
+LIBDEF_F(secs2str)
+
+/**
+ * Parse a duration with 's', 'm', 'h', 'd', and 'w' units.
+ */
+typedef bool (librpz_str2secs_t)(librpz_emsg_t *emsg, time_t *val,
+                                const char *str0);
+LIBDEF_F(str2secs)
+
+/**
+ * Translate selected rtypes to strings
+ */
+typedef const char *(librpz_rtype2str_t)(uint type, char *buf, size_t buf_size);
+#define RTYPE2STR_SIZE sizeof("type xxxxx")
+LIBDEF_F(rtype2str)
+
+/**
+ * Local version of ns_name_ntop() for portability.
+ */
+typedef int (librpz_domain_ntop_t)(const u_char *src, char *dst, size_t dstsiz);
+LIBDEF_F(domain_ntop)
+
+/**
+ * Local version of ns_name_pton().
+ */
+typedef int (librpz_domain_pton2_t)(const char *src, u_char *dst, size_t dstsiz,
+                                   size_t *dstlen, bool lower);
+LIBDEF_F(domain_pton2)
+
+typedef union socku socku_t;
+typedef socku_t *(librpz_mk_inet_su_t)(socku_t *su, const struct in_addr *addrp,
+                                    in_port_t port);
+LIBDEF_F(mk_inet_su)
+
+typedef socku_t *(librpz_mk_inet6_su_t)(socku_t *su, const
+                                       struct in6_addr *addrp,
+                                       uint32_t scope_id, in_port_t port);
+LIBDEF_F(mk_inet6_su)
+
+typedef bool (librpz_str2su_t)(socku_t *sup, const char *str);
+LIBDEF_F(str2su)
+
+typedef char *(librpz_su2str_t)(char *str, size_t str_len, const socku_t *su);
+LIBDEF_F(su2str)
+#define SU2STR_SIZE (INET6_ADDRSTRLEN+1+6+1)
+
+
+/**
+ * default path to dnsrpzd
+ */
+const char *librpz_dnsrpzd_path;
+
+
+#undef LIBDEF
+
+/*
+ * This is the dlopen() interface to librpz.
+ */
+typedef const struct {
+       const char                      *dnsrpzd_path;
+       const char                      *version;
+       librpz_parse_log_opt_t          *parse_log_opt;
+       librpz_log_level_val_t          *log_level_val;
+       librpz_set_log_t                *set_log;
+       librpz_vpemsg_t                 *vpemsg;
+       librpz_pemsg_t                  *pemsg;
+       librpz_vlog_t                   *vlog;
+       librpz_log_t                    *log;
+       librpz_fatal_t                  *fatal LIBRPZ_NORET;
+       librpz_rpz_assert_t             *rpz_assert LIBRPZ_NORET;
+       librpz_rpz_vassert_t            *rpz_vassert LIBRPZ_NORET;
+       librpz_clist_create_t           *clist_create;
+       librpz_clist_detach_t           *clist_detach;
+       librpz_client_create_t          *client_create;
+       librpz_connect_t                *connect;
+       librpz_client_detach_t          *client_detach;
+       librpz_rsp_create_t             *rsp_create;
+       librpz_rsp_detach_t             *rsp_detach;
+       librpz_rsp_result_t             *rsp_result;
+       librpz_have_trig_t              *have_trig;
+       librpz_have_ns_trig_t           *have_ns_trig;
+       librpz_rsp_clientip_prefix_t    *rsp_clientip_prefix;
+       librpz_rsp_domain_t             *rsp_domain;
+       librpz_rsp_rr_t                 *rsp_rr;
+       librpz_rsp_soa_t                *rsp_soa;
+       librpz_soa_serial_t             *soa_serial;
+       librpz_rsp_push_t               *rsp_push;
+       librpz_rsp_pop_t                *rsp_pop;
+       librpz_rsp_pop_discard_t        *rsp_pop_discard;
+       librpz_rsp_forget_zone_t        *rsp_forget_zone;
+       librpz_ck_ip_t                  *ck_ip;
+       librpz_ck_domain_t              *ck_domain;
+       librpz_zone_refresh_t           *zone_refresh;
+       librpz_db_info_t                *db_info;
+       librpz_itr_start_t              *itr_start;
+       librpz_mf_stats_t               *mf_stats;
+       librpz_vers_stats_t             *vers_stats;
+       librpz_itr_zone_t               *itr_zone;
+       librpz_itr_node_t               *itr_node;
+       librpz_policy2str_t             *policy2str;
+       librpz_trig2str_t               *trig2str;
+       librpz_secs2str_t               *secs2str;
+       librpz_str2secs_t               *str2secs;
+       librpz_rtype2str_t              *rtype2str;
+       librpz_domain_ntop_t            *domain_ntop;
+       librpz_domain_pton2_t           *domain_pton2;
+       librpz_mk_inet_su_t             *mk_inet_su;
+       librpz_mk_inet6_su_t            *mk_inet6_su;
+       librpz_str2su_t                 *str2su;
+       librpz_su2str_t                 *su2str;
+} librpz_0_t;
+extern librpz_0_t librpz_def_0;
+
+/*
+ * Future versions can be upward compatible by defining LIBRPZ_DEF as
+ * librpz_X_t.
+ */
+#define LIBRPZ_DEF     librpz_def_0
+#define LIBRPZ_DEF_STR "librpz_def_0"
+
+typedef librpz_0_t librpz_t;
+extern librpz_t *librpz;
+
+
+#if LIBRPZ_LIB_OPEN == 2
+#include <dlfcn.h>
+
+/**
+ * link-load librpz
+ * @param[out] emsg: error message
+ * @param[in,out] dl_handle: NULL or pointer to new dlopen handle
+ * @param[in] path: librpz.so path
+ * @return address of interface structure or NULL on failure
+ */
+static inline librpz_t *
+librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path)
+{
+       void *handle;
+       librpz_t *new_librpz;
+
+       emsg->c[0] = '\0';
+
+       /*
+        * Close a previously opened handle on librpz.so.
+        */
+       if (dl_handle != NULL && *dl_handle != NULL) {
+               if (dlclose(*dl_handle) != 0) {
+                       snprintf(emsg->c, sizeof(librpz_emsg_t),
+                                "dlopen(NULL): %s", dlerror());
+                       return (NULL);
+               }
+               *dl_handle = NULL;
+       }
+
+       /*
+        * First try the main executable of the process in case it was
+        * linked to librpz.
+        * Do not worry if we cannot search the main executable of the process.
+        */
+       handle = dlopen(NULL, RTLD_NOW | RTLD_LOCAL);
+       if (handle != NULL) {
+               new_librpz = dlsym(handle, LIBRPZ_DEF_STR);
+               if (new_librpz != NULL) {
+                       if (dl_handle != NULL)
+                               *dl_handle = handle;
+                       return (new_librpz);
+               }
+               if (dlclose(handle) != 0) {
+                       snprintf(emsg->c, sizeof(librpz_emsg_t),
+                                "dlsym(NULL, "LIBRPZ_DEF_STR"): %s",
+                                dlerror());
+                       return (NULL);
+               }
+       }
+
+       if (path == NULL || path[0] == '\0') {
+               snprintf(emsg->c, sizeof(librpz_emsg_t),
+                        "librpz not linked and no dlopen() path provided");
+               return (NULL);
+       }
+
+       handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
+       if (handle == NULL) {
+               snprintf(emsg->c, sizeof(librpz_emsg_t), "dlopen(%s): %s",
+                        path, dlerror());
+               return (NULL);
+       }
+       new_librpz = dlsym(handle, LIBRPZ_DEF_STR);
+       if (new_librpz != NULL) {
+               if (dl_handle != NULL)
+                       *dl_handle = handle;
+               return (new_librpz);
+       }
+       snprintf(emsg->c, sizeof(librpz_emsg_t),
+                "dlsym(%s, "LIBRPZ_DEF_STR"): %s",
+                path, dlerror());
+       dlclose(handle);
+       return (NULL);
+}
+#elif defined(LIBRPZ_LIB_OPEN)
+/*
+ * Statically link to the librpz.so DSO on systems without dlopen()
+ */
+static inline librpz_t *
+librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path)
+{
+       (void)(path);
+
+       if (dl_handle != NULL)
+               *dl_handle = NULL;
+
+#if LIBRPZ_LIB_OPEN == 1
+       emsg->c[0] = '\0';
+       return (&LIBRPZ_DEF);
+#else
+       snprintf(emsg->c, sizeof(librpz_emsg_t),
+                "librpz not available via ./configure");
+       return (NULL);
+#endif /* LIBRPZ_LIB_OPEN */
+}
+#endif /* LIBRPZ_LIB_OPEN */
+
+#endif /* LIBRPZ_H */
index 9c0d22c8ad15a5e97759daab408f12125b8e8f20..2fd604128a4349918d0cf2b87d27394d4d0c4260 100644 (file)
@@ -6,8 +6,6 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-/* $Id$ */
-
 
 #ifndef DNS_RPZ_H
 #define DNS_RPZ_H 1
@@ -187,6 +185,9 @@ typedef struct dns_rpz_popt dns_rpz_popt_t;
 struct dns_rpz_popt {
        dns_rpz_zbits_t     no_rd_ok;
        dns_rpz_zbits_t     no_log;
+       dns_rpz_zbits_t     nsip_on;
+       dns_rpz_zbits_t     nsdname_on;
+       isc_boolean_t       dnsrps_enabled;
        isc_boolean_t       break_dnssec;
        isc_boolean_t       qname_wait_recurse;
        isc_boolean_t       nsip_wait_recurse;
@@ -203,8 +204,9 @@ struct dns_rpz_zones {
        dns_rpz_triggers_t      triggers[DNS_RPZ_MAX_ZONES];
 
        /*
-        * RPZ policy version number (initially 0, increases whenever
-        * the server is reconfigured with new zones or policy)
+        * RPZ policy version number.
+        * It is initially 0 and it increases whenever the server is
+        * reconfigured with new zones or policy.
         */
        int                     rpz_ver;
 
@@ -250,6 +252,13 @@ struct dns_rpz_zones {
 
        dns_rpz_cidr_node_t     *cidr;
        dns_rbt_t               *rbt;
+
+       /*
+        * DNSRPZ librpz configuration string and handle on librpz connection
+        */
+       char                    *rps_cstr;
+       size_t                  rps_cstr_size;
+       struct librpz_client    *rps_client;
 };
 
 
@@ -319,6 +328,11 @@ typedef struct {
        dns_rpz_popt_t          popt;
        int                     rpz_ver;
 
+       /*
+        * Shim db between BIND and DNRPS librpz.
+        */
+       dns_db_t                *rpsdb;
+
        /*
         * p_name: current policy owner name
         * r_name: recursing for this name to possible policy triggers
@@ -360,7 +374,8 @@ dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
                     dns_name_t *selfname);
 
 isc_result_t
-dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, char *rps_cstr,
+                 size_t rps_cstr_size, isc_mem_t *mctx,
                  isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr);
 
 isc_result_t
index 0c8e4535782e70d00671d43efe81cdf705514ae5..06d97532c2b2570fcfc618faec4729a7fab4626f 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <dns/db.h>
 #include <dns/dbiterator.h>
+#include <dns/dnsrps.h>
 #include <dns/events.h>
 #include <dns/fixedname.h>
 #include <dns/log.h>
@@ -397,11 +398,10 @@ fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
         * qname_wait_recurse and qname_skip_recurse are used to
         * implement the "qname-wait-recurse" config option.
         *
-        * By default, "qname-wait-recurse" is yes, so no
-        * processing happens without recursion. In this case,
-        * qname_wait_recurse is true, and qname_skip_recurse
-        * (a bit field indicating which policy zones can be
-        * processed without recursion) is set to all 0's by
+        * When "qname-wait-recurse" is yes, no processing happens without
+        * recursion. In this case, qname_wait_recurse is true, and
+        * qname_skip_recurse (a bit field indicating which policy zones
+        * can be processed without recursion) is set to all 0's by
         * fix_qname_skip_recurse().
         *
         * When "qname-wait-recurse" is no, qname_skip_recurse may be
@@ -681,6 +681,12 @@ badname(int level, const dns_name_t *name, const char *str1, const char *str2) {
  * Convert an IP address from radix tree binary (host byte order) to
  * to its canonical response policy domain name without the origin of the
  * policy zone.
+ *
+ * Generate a name for an IPv6 address that fits RFC 5952, except that our
+ * reversed format requires that when the length of the consecutive 16-bit
+ * 0 fields are equal (e.g., 1.0.0.1.0.0.db8.2001 corresponding to
+ * 2001:db8:0:0:1:0:0:1), we shorted the last instead of the first
+ * (e.g., 1.0.0.1.zz.db8.2001 corresponding to 2001:db8::1:0:0:1).
  */
 static isc_result_t
 ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
@@ -693,7 +699,7 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
        char str[1+8+1+INET6_ADDRSTRLEN+1];
        isc_buffer_t buffer;
        isc_result_t result;
-       isc_boolean_t zeros;
+       int best_first, best_len, cur_first, cur_len;
        int i, n, len;
 
        if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
@@ -703,43 +709,53 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
                               (tgt_ip->w[3]>>8) & 0xff,
                               (tgt_ip->w[3]>>16) & 0xff,
                               (tgt_ip->w[3]>>24) & 0xff);
-               if (len < 0 || len > (int)sizeof(str))
+               if (len < 0 || len > (int)sizeof(str)) {
                        return (ISC_R_FAILURE);
+               }
        } else {
+               len = snprintf(str, sizeof(str), "%d", tgt_prefix);
+               if (len == -1) {
+                       return (ISC_R_FAILURE);
+               }
+
                for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) {
                        w[i*2+1] = ((tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] >> 16)
                                    & 0xffff);
                        w[i*2] = tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] & 0xffff;
                }
-               zeros = ISC_FALSE;
-               len = snprintf(str, sizeof(str), "%d", tgt_prefix);
-               if (len == -1)
-                       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) {
-                               INSIST((size_t)len <= sizeof(str));
-                               n = snprintf(&str[len], sizeof(str) - len,
-                                            ".%x", w[i++]);
-                               if (n < 0)
-                                       return (ISC_R_FAILURE);
-                               len += n;
+               /*
+                * Find the start and length of the first longest sequence
+                * of zeros in the address.
+                */
+               best_first = -1;
+               best_len = 0;
+               cur_first = -1;
+               cur_len = 0;
+               for (n = 0; n <=7; ++n) {
+                       if (w[n] != 0) {
+                               cur_len = 0;
+                               cur_first = -1;
                        } else {
-                               zeros = ISC_TRUE;
-                               INSIST((size_t)len <= sizeof(str));
-                               n = snprintf(&str[len], sizeof(str) - len,
-                                            ".zz");
-                               if (n < 0)
-                                       return (ISC_R_FAILURE);
-                               len += n;
-                               i += 2;
-                               while (i < DNS_RPZ_CIDR_WORDS * 2 && w[i] == 0)
-                                       ++i;
+                               ++cur_len;
+                               if (cur_first < 0) {
+                                       cur_first = n;
+                               } else if (cur_len >= best_len) {
+                                       best_first = cur_first;
+                                       best_len = cur_len;
+                               }
+                       }
+               }
+
+               for (n = 0; n <= 7; ++n) {
+                       INSIST(len < (int)sizeof(str));
+                       if (n == best_first) {
+                               len += snprintf(str + len, sizeof(str) - len,
+                                               ".zz");
+                               n += best_len - 1;
+                       } else {
+                               len += snprintf(str + len, sizeof(str) - len,
+                                               ".%x", w[n]);
                        }
-                       if (len >= (int)sizeof(str))
-                               return (ISC_R_FAILURE);
                }
        }
 
@@ -750,26 +766,31 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
 }
 
 /*
- * Determine the type of a name in a response policy zone.
+ * Determine the type of a name in a response policy zone.
  */
 static dns_rpz_type_t
-type_from_name(dns_rpz_zone_t *rpz, const dns_name_t *name) {
-
-       if (dns_name_issubdomain(name, &rpz->ip))
+type_from_name(const dns_rpz_zones_t *rpzs,
+              dns_rpz_zone_t *rpz, const dns_name_t *name)
+{
+       if (dns_name_issubdomain(name, &rpz->ip)) {
                return (DNS_RPZ_TYPE_IP);
+       }
 
-       if (dns_name_issubdomain(name, &rpz->client_ip))
+       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))
+       if ((rpzs->p.nsip_on & DNS_RPZ_ZBIT(rpz->num)) != 0 &&
+           dns_name_issubdomain(name, &rpz->nsip))
+       {
                return (DNS_RPZ_TYPE_NSIP);
-#endif
+       }
 
-#ifdef ENABLE_RPZ_NSDNAME
-       if (dns_name_issubdomain(name, &rpz->nsdname))
+       if ((rpzs->p.nsdname_on & DNS_RPZ_ZBIT(rpz->num)) != 0 &&
+           dns_name_issubdomain(name, &rpz->nsdname))
+       {
                return (DNS_RPZ_TYPE_NSDNAME);
-#endif
+       }
 
        return (DNS_RPZ_TYPE_QNAME);
 }
@@ -787,7 +808,7 @@ name2ipkey(int log_level,
           dns_rpz_addr_zbits_t *new_set)
 {
        dns_rpz_zone_t *rpz;
-       char ip_str[DNS_NAME_FORMATSIZE];
+       char ip_str[DNS_NAME_FORMATSIZE], ip2_str[DNS_NAME_FORMATSIZE];
        dns_offsets_t ip_name_offsets;
        dns_fixedname_t ip_name2f;
        dns_name_t ip_name, *ip_name2;
@@ -830,7 +851,6 @@ name2ipkey(int log_level,
                        "; invalid leading prefix length", "");
                return (ISC_R_FAILURE);
        }
-       *cp2 = '\0';
        if (prefix_num < 1U || prefix_num > 128U) {
                badname(log_level, src_name,
                        "; invalid prefix length of ", prefix_str);
@@ -926,21 +946,27 @@ name2ipkey(int log_level,
        }
 
        /*
-        * XXXMUKS: Should the following check be enabled in a
-        * production build?  It can be expensive for large IP zones
-        * from 3rd parties.
-        */
-
-       /*
-        * Convert the address back to a canonical domain name
-        * to ensure that the original name is in canonical form.
+        * Complain about bad names but be generous and accept them.
         */
-       dns_fixedname_init(&ip_name2f);
-       ip_name2 = dns_fixedname_name(&ip_name2f);
-       result = ip2name(tgt_ip, (dns_rpz_prefix_t)prefix_num, NULL, ip_name2);
-       if (result != ISC_R_SUCCESS || !dns_name_equal(&ip_name, ip_name2)) {
-               badname(log_level, src_name, "; not canonical", "");
-               return (ISC_R_FAILURE);
+       if (log_level < DNS_RPZ_DEBUG_QUIET &&
+           isc_log_wouldlog(dns_lctx, log_level)) {
+               /*
+                * Convert the address back to a canonical domain name
+                * to ensure that the original name is in canonical form.
+                */
+               dns_fixedname_init(&ip_name2f);
+               ip_name2 = dns_fixedname_name(&ip_name2f);
+               result = ip2name(tgt_ip, (dns_rpz_prefix_t)prefix_num,
+                                NULL, ip_name2);
+               if (result != ISC_R_SUCCESS ||
+                   !dns_name_equal(&ip_name, ip_name2)) {
+                       dns_name_format(ip_name2, ip2_str, sizeof(ip2_str));
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+                                     DNS_LOGMODULE_RBTDB, log_level,
+                                     "rpz IP address \"%s\""
+                                     " is not the canonical \"%s\"",
+                                     ip_str, ip2_str);
+               }
        }
 
        return (ISC_R_SUCCESS);
@@ -1396,10 +1422,11 @@ rpz_node_deleter(void *nm_data, void *mctx) {
 }
 
 /*
- * Get ready for a new set of policy zones.
+ * Get ready for a new set of policy zones for a view.
  */
 isc_result_t
-dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, char *rps_cstr,
+                 size_t rps_cstr_size, isc_mem_t *mctx,
                  isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
 {
        dns_rpz_zones_t *zones;
@@ -1424,7 +1451,20 @@ dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
        if (result != ISC_R_SUCCESS)
                goto cleanup_refcount;
 
-       result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &zones->rbt);
+       zones->rps_cstr = rps_cstr;
+       zones->rps_cstr_size = rps_cstr_size;
+#ifdef USE_DNSRPS
+       if (rps_cstr != NULL) {
+               result = dns_dnsrps_view_init(zones, rps_cstr);
+       }
+#else
+       INSIST(!zones->p.dnsrps_enabled);
+#endif
+       if (result == ISC_R_SUCCESS && !zones->p.dnsrps_enabled) {
+               result = dns_rbt_create(mctx, rpz_node_deleter,
+                                       mctx, &zones->rbt);
+       }
+
        if (result != ISC_R_SUCCESS)
                goto cleanup_rbt;
 
@@ -2074,26 +2114,38 @@ dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) {
 
        *rpzsp = NULL;
        isc_refcount_decrement(&rpzs->refs, &refs);
+       if (refs != 0) {
+               return;
+       }
 
        /*
         * Forget the last of view's rpz machinery after the last reference.
         */
-       if (refs == 0) {
-               for (rpz_num = 0; rpz_num < DNS_RPZ_MAX_ZONES; ++rpz_num) {
-                       rpz = rpzs->zones[rpz_num];
-                       rpzs->zones[rpz_num] = NULL;
-                       if (rpz != NULL)
-                               rpz_detach(&rpz, rpzs);
+       for (rpz_num = 0; rpz_num < DNS_RPZ_MAX_ZONES; ++rpz_num) {
+               rpz = rpzs->zones[rpz_num];
+               rpzs->zones[rpz_num] = NULL;
+               if (rpz != NULL) {
+                       rpz_detach(&rpz, rpzs);
                }
+       }
+
+       if (rpzs->rps_cstr_size != 0) {
+#ifdef USE_DNSRPS
+               librpz->client_detach(&rpzs->rps_client);
+#endif
+               isc_mem_put(rpzs->mctx, rpzs->rps_cstr,
+                           rpzs->rps_cstr_size);
+       }
 
-               cidr_free(rpzs);
+       cidr_free(rpzs);
+       if (rpzs->rbt != NULL) {
                dns_rbt_destroy(&rpzs->rbt);
-               DESTROYLOCK(&rpzs->maint_lock);
-               isc_rwlock_destroy(&rpzs->search_lock);
-               isc_refcount_destroy(&rpzs->refs);
-               isc_task_destroy(&rpzs->updater);
-               isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
        }
+       DESTROYLOCK(&rpzs->maint_lock);
+       isc_rwlock_destroy(&rpzs->search_lock);
+       isc_refcount_destroy(&rpzs->refs);
+       isc_task_destroy(&rpzs->updater);
+       isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
 }
 
 /*
@@ -2140,7 +2192,7 @@ dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
        REQUIRE(rpz != NULL);
        RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
 
-       rpz_type = type_from_name(rpz, src_name);
+       rpz_type = type_from_name(rpzs, rpz, src_name);
 
 
        switch (rpz_type) {
@@ -2350,7 +2402,7 @@ dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
 
        RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
 
-       rpz_type = type_from_name(rpz, src_name);
+       rpz_type = type_from_name(rpzs, rpz, src_name);
 
        switch (rpz_type) {
        case DNS_RPZ_TYPE_QNAME:
index b575af2f5eccdbc95337939ca4cbdc8cd1698958..15a141164803bd209c7f6e69cfd8880dfc95154c 100644 (file)
@@ -1419,11 +1419,15 @@ static cfg_type_t cfg_type_dnstapoutput = {
  *     zone <string> [ policy (given|disabled|passthru|drop|tcp-only|
  *                                     nxdomain|nodata|cname <domain> ) ]
  *                   [ recursive-only yes|no ] [ log yes|no ]
- *                   [ max-policy-ttl number ] [ min-update-interval number ] ;
+ *                   [ max-policy-ttl number ]
+ *                   [ nsip-enable yes|no ] [ nsdname-enable yes|no ];
  *  } [ recursive-only yes|no ] [ max-policy-ttl number ]
  *      [ min-update-interval number ]
  *      [ break-dnssec yes|no ] [ min-ns-dots number ]
- *      [ qname-wait-recurse yes|no ] ;
+ *      [ qname-wait-recurse yes|no ]
+ *      [ nsip-enable yes|no ] [ nsdname-enable yes|no ]
+ *      [ dnsrps-enable yes|no ]
+ *      [ dnsrps-options { DNSRPS configuration string } ];
  */
 
 static void
@@ -1614,6 +1618,8 @@ static cfg_tuplefielddef_t rpz_zone_fields[] = {
        { "min-update-interval", &cfg_type_uint32, 0 },
        { "policy", &cfg_type_rpz_policy, 0 },
        { "recursive-only", &cfg_type_boolean, 0 },
+       { "nsip-enable", &cfg_type_boolean, 0 },
+       { "nsdname-enable", &cfg_type_boolean, 0 },
        { NULL, NULL, 0 }
 };
 static cfg_type_t cfg_type_rpz_tuple = {
@@ -1635,6 +1641,16 @@ static cfg_tuplefielddef_t rpz_fields[] = {
        { "nsip-wait-recurse", &cfg_type_boolean, 0 },
        { "qname-wait-recurse", &cfg_type_boolean, 0 },
        { "recursive-only", &cfg_type_boolean, 0 },
+       { "nsip-enable", &cfg_type_boolean, 0 },
+       { "nsdname-enable", &cfg_type_boolean, 0 },
+#ifdef USE_DNSRPS
+       { "dnsrps-enable", &cfg_type_boolean, 0 },
+       { "dnsrps-options", &cfg_type_bracketed_text, 0 },
+#else
+       { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
+       { "dnsrps-options", &cfg_type_bracketed_text,
+               CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif
        { NULL, NULL, 0 }
 };
 static cfg_type_t cfg_type_rpz = {
@@ -1851,6 +1867,14 @@ view_clauses[] = {
        { "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI },
        { "dns64-contact", &cfg_type_astring, 0 },
        { "dns64-server", &cfg_type_astring, 0 },
+#ifdef USE_DNSRPS
+       { "dnsrps-enable", &cfg_type_boolean, 0 },
+       { "dnsrps-options", &cfg_type_bracketed_text, 0 },
+#else
+       { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
+       { "dnsrps-options", &cfg_type_bracketed_text,
+               CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif
        { "dnssec-accept-expired", &cfg_type_boolean, 0 },
        { "dnssec-enable", &cfg_type_boolean, 0 },
        { "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI },
index 4d6f6524f2c79b501d6ad709101219bbb193d345..931a13354322b404f0aea3ffbeeeb52de7ddc91d 100644 (file)
@@ -29,6 +29,7 @@
 #include <dns/db.h>
 #include <dns/dlz.h>
 #include <dns/dns64.h>
+#include <dns/dnsrps.h>
 #include <dns/dnssec.h>
 #include <dns/events.h>
 #include <dns/message.h>
@@ -715,6 +716,7 @@ query_reset(ns_client_t *client, isc_boolean_t everything) {
        if (client->query.rpz_st != NULL) {
                rpz_st_clear(client);
                if (everything) {
+                       INSIST(client->query.rpz_st->rpsdb == NULL);
                        isc_mem_put(client->mctx, client->query.rpz_st,
                                    sizeof(*client->query.rpz_st));
                        client->query.rpz_st = NULL;
@@ -1285,8 +1287,7 @@ rpz_log_fail_helper(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;
-       const char *slash;
+       const char *failed, *via, *slash, *str_blank;
        const char *rpztypestr1;
        const char *rpztypestr2;
 
@@ -1310,13 +1311,23 @@ rpz_log_fail_helper(ns_client_t *client, int level, dns_name_t *p_name,
                rpztypestr2 = "";
        }
 
+       str_blank = (*str != ' ' && *str != '\0') ? " " : "";
+
        dns_name_format(client->query.qname, qnamebuf, sizeof(qnamebuf));
-       dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
+
+       if (p_name != NULL) {
+               via = " via ";
+               dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
+       } else {
+               via = "";
+               p_namebuf[0] = '\0';
+       }
+
        ns_client_log(client, NS_LOGCATEGORY_QUERY_ERRORS,
                      NS_LOGMODULE_QUERY, level,
-                     "rpz %s%s%s rewrite %s via %s%s%s%s",
+                     "rpz %s%s%s rewrite %s%s%s%s%s%s : %s",
                      rpztypestr1, slash, rpztypestr2,
-                     qnamebuf, p_namebuf,
+                     qnamebuf, via, p_namebuf, str_blank,
                      str, failed, isc_result_totext(result));
 }
 
@@ -2512,24 +2523,33 @@ rpz_st_clear(ns_client_t *client) {
 
        CTRACE(ISC_LOG_DEBUG(3), "rpz_st_clear");
 
-       if (st->m.rdataset != NULL)
+       if (st->m.rdataset != NULL) {
                query_putrdataset(client, &st->m.rdataset);
+       }
        rpz_match_clear(st);
 
        rpz_clean(NULL, &st->r.db, NULL, NULL);
-       if (st->r.ns_rdataset != NULL)
+       if (st->r.ns_rdataset != NULL) {
                query_putrdataset(client, &st->r.ns_rdataset);
-       if (st->r.r_rdataset != NULL)
+       }
+       if (st->r.r_rdataset != NULL) {
                query_putrdataset(client, &st->r.r_rdataset);
+       }
 
        rpz_clean(&st->q.zone, &st->q.db, &st->q.node, NULL);
-       if (st->q.rdataset != NULL)
+       if (st->q.rdataset != NULL) {
                query_putrdataset(client, &st->q.rdataset);
-       if (st->q.sigrdataset != NULL)
+       }
+       if (st->q.sigrdataset != NULL) {
                query_putrdataset(client, &st->q.sigrdataset);
+       }
        st->state = 0;
        st->m.type = DNS_RPZ_TYPE_BAD;
        st->m.policy = DNS_RPZ_POLICY_MISS;
+       if (st->rpsdb != NULL) {
+               dns_db_detach(&st->rpsdb);
+       }
+
 }
 
 static dns_rpz_zbits_t
@@ -2544,6 +2564,19 @@ rpz_get_zbits(ns_client_t *client,
 
        st = client->query.rpz_st;
 
+#ifdef USE_DNSRPS
+       if (st->popt.dnsrps_enabled) {
+               if (st->rpsdb == NULL ||
+                   librpz->have_trig(dns_dnsrps_type2trig(rpz_type),
+                                     ip_type == dns_rdatatype_aaaa,
+                                     ((rpsdb_t *)st->rpsdb)->rsp))
+               {
+                       return (DNS_RPZ_ALL_ZBITS);
+               }
+               return (0);
+       }
+#endif
+
        switch (rpz_type) {
        case DNS_RPZ_TYPE_CLIENT_IP:
                zbits = st->have.client_ip;
@@ -2733,7 +2766,7 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
                rpz_clean(NULL, dbp, &node, rdatasetp);
                version = NULL;
                dns_db_attach(client->view->cachedb, dbp);
-               result = dns_db_findext(*dbp, name, version, dns_rdatatype_ns,
+               result = dns_db_findext(*dbp, name, version, type,
                                        0, client->now, &node, found,
                                        &cm, &ci, *rdatasetp, NULL);
        }
@@ -2921,8 +2954,7 @@ rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
                                             p_name, rpz_type,
                                             " rdatasetiter", result);
                                CTRACE(ISC_LOG_ERROR,
-                                      "rpz_find_p: rdatasetiter_destroy "
-                                      "failed");
+                                      "rpz_find_p: rdatasetiter failed");
                                return (DNS_R_SERVFAIL);
                        }
                        /*
@@ -3017,6 +3049,283 @@ rpz_save_p(dns_rpz_st_t *st, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
        SAVE(st->m.version, version);
 }
 
+#ifdef USE_DNSRPS
+/*
+ * Check the results of a RPZ service interface lookup.
+ * Stop after an error (<0) or not a hit on a disabled zone (0).
+ * Continue after a hit on a disabled zone (>0).
+ */
+static int
+dnsrps_ck(librpz_emsg_t *emsg, ns_client_t *client, rpsdb_t *rpsdb,
+         isc_boolean_t recursed)
+{
+       isc_region_t region;
+       librpz_domain_buf_t pname_buf;
+
+       if (!librpz->rsp_result(emsg, &rpsdb->result, recursed, rpsdb->rsp)) {
+               return (-1);
+       }
+
+       /*
+        * Forget the state from before the IP address or domain check
+        * if the lookup hit nothing.
+        */
+       if (rpsdb->result.policy == LIBRPZ_POLICY_UNDEFINED ||
+           rpsdb->result.hit_id != rpsdb->hit_id ||
+           rpsdb->result.policy != LIBRPZ_POLICY_DISABLED)
+       {
+               if (!librpz->rsp_pop_discard(emsg, rpsdb->rsp)) {
+                       return (-1);
+               }
+               return (0);
+       }
+
+       /*
+        * Log a hit on a disabled zone.
+        * Forget the zone to not try it again, and restore the pre-hit state.
+        */
+       if (!librpz->rsp_domain(emsg, &pname_buf, rpsdb->rsp)) {
+               return (-1);
+       }
+       region.base = pname_buf.d;
+       region.length = pname_buf.size;
+       dns_name_fromregion(client->query.rpz_st->p_name, &region);
+       rpz_log_rewrite(client, ISC_TRUE,
+                       dns_dnsrps_2policy(rpsdb->result.zpolicy),
+                       dns_dnsrps_trig2type(rpsdb->result.trig), NULL,
+                       client->query.rpz_st->p_name, NULL,
+                       rpsdb->result.cznum);
+
+       if (!librpz->rsp_forget_zone(emsg, rpsdb->result.cznum, rpsdb->rsp) ||
+           !librpz->rsp_pop(emsg, &rpsdb->result, rpsdb->rsp))
+       {
+               return (-1);
+       }
+       return (1);
+}
+
+/*
+ * Ready the shim database and rdataset for a DNSRPS hit.
+ */
+static isc_boolean_t
+dnsrps_set_p(librpz_emsg_t *emsg, ns_client_t *client, dns_rpz_st_t *st,
+            dns_rdatatype_t qtype, dns_rdataset_t **p_rdatasetp,
+            isc_boolean_t recursed)
+{
+       rpsdb_t *rpsdb;
+       librpz_domain_buf_t pname_buf;
+       isc_region_t region;
+       dns_zone_t *p_zone;
+       dns_db_t *p_db;
+       dns_dbnode_t *p_node;
+       dns_rpz_policy_t policy;
+       dns_fixedname_t foundf;
+       dns_name_t *found;
+       dns_rdatatype_t foundtype, searchtype;
+       isc_result_t result;
+
+       rpsdb = (rpsdb_t *)st->rpsdb;
+
+       if (!librpz->rsp_result(emsg, &rpsdb->result, recursed, rpsdb->rsp)) {
+               return (ISC_FALSE);
+       }
+
+       if (rpsdb->result.policy == LIBRPZ_POLICY_UNDEFINED) {
+               return (ISC_TRUE);
+       }
+
+       /*
+        * Give the fake or shim DNSRPS database its new origin.
+        */
+       if (!librpz->rsp_soa(emsg, NULL, NULL, &rpsdb->origin_buf,
+                            &rpsdb->result, rpsdb->rsp))
+       {
+               return (ISC_FALSE);
+       }
+       region.base = rpsdb->origin_buf.d;
+       region.length = rpsdb->origin_buf.size;
+       dns_name_fromregion(&rpsdb->common.origin, &region);
+
+       if (!librpz->rsp_domain(emsg, &pname_buf, rpsdb->rsp)) {
+               return (ISC_FALSE);
+       }
+       region.base = pname_buf.d;
+       region.length = pname_buf.size;
+       dns_name_fromregion(st->p_name, &region);
+
+       p_zone = NULL;
+       p_db = NULL;
+       p_node = NULL;
+       rpz_ready(client, p_rdatasetp);
+       dns_db_attach(st->rpsdb, &p_db);
+       policy = dns_dnsrps_2policy(rpsdb->result.policy);
+       if (policy != DNS_RPZ_POLICY_RECORD) {
+               result = ISC_R_SUCCESS;
+       } else if (qtype == dns_rdatatype_rrsig) {
+               /*
+                * dns_find_db() refuses to look for and fail to
+                * find dns_rdatatype_rrsig.
+                */
+               result = DNS_R_NXRRSET;
+               policy = DNS_RPZ_POLICY_NODATA;
+       } else {
+               /*
+                * Get the next (and so first) RR from the policy node.
+                * If it is a CNAME, then look for it regardless of the
+                * query type.
+                */
+               if (!librpz->rsp_rr(emsg, &foundtype, NULL, NULL, NULL,
+                                   &rpsdb->result, rpsdb->qname->ndata,
+                                   rpsdb->qname->length, rpsdb->rsp))
+               {
+                       return (ISC_FALSE);
+               }
+               if (foundtype == dns_rdatatype_cname) {
+                       searchtype = dns_rdatatype_cname;
+               } else {
+                       searchtype = qtype;
+               }
+               /*
+                * Get the DNSPRS imitation rdataset.
+                */
+               dns_fixedname_init(&foundf);
+               found = dns_fixedname_name(&foundf);
+               result = dns_db_find(p_db, st->p_name, NULL, searchtype,
+                                    0, 0, &p_node, found, *p_rdatasetp, NULL);
+
+               if (result == ISC_R_SUCCESS) {
+                       if (searchtype == dns_rdatatype_cname &&
+                           qtype != dns_rdatatype_cname)
+                       {
+                               result = DNS_R_CNAME;
+                       }
+               } else if (result == DNS_R_NXRRSET) {
+                       policy = DNS_RPZ_POLICY_NODATA;
+               } else {
+                       snprintf(emsg->c, sizeof(emsg->c), "dns_db_find(): %s",
+                                isc_result_totext(result));
+                       return (ISC_FALSE);
+               }
+       }
+
+       rpz_save_p(st, client->view->rpzs->zones[rpsdb->result.cznum],
+                  dns_dnsrps_trig2type(rpsdb->result.trig), policy,
+                  st->p_name, 0, result, &p_zone, &p_db, &p_node,
+                  p_rdatasetp, NULL);
+
+       rpz_clean(NULL, NULL, NULL, p_rdatasetp);
+
+       return (ISC_TRUE);
+}
+
+static isc_result_t
+dnsrps_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
+                 dns_rpz_type_t rpz_type, dns_rdataset_t **p_rdatasetp)
+{
+       dns_rpz_st_t *st;
+       rpsdb_t *rpsdb;
+       librpz_trig_t trig = LIBRPZ_TRIG_CLIENT_IP;
+       isc_boolean_t recursed = ISC_FALSE;
+       int res;
+       librpz_emsg_t emsg;
+       isc_result_t result;
+
+       st = client->query.rpz_st;
+       rpsdb = (rpsdb_t *)st->rpsdb;
+
+       result = rpz_ready(client, p_rdatasetp);
+       if (result != ISC_R_SUCCESS) {
+               st->m.policy = DNS_RPZ_POLICY_ERROR;
+               return (result);
+       }
+
+       switch (rpz_type) {
+       case DNS_RPZ_TYPE_CLIENT_IP:
+               trig = LIBRPZ_TRIG_CLIENT_IP;
+               recursed = ISC_FALSE;
+               break;
+       case DNS_RPZ_TYPE_IP:
+               trig = LIBRPZ_TRIG_IP;
+               recursed = ISC_TRUE;
+               break;
+       case DNS_RPZ_TYPE_NSIP:
+               trig = LIBRPZ_TRIG_NSIP;
+               recursed = ISC_TRUE;
+               break;
+       default:
+               INSIST(0);
+       }
+
+       do {
+               if (!librpz->rsp_push(&emsg, rpsdb->rsp) ||
+                   !librpz->ck_ip(&emsg,
+                                  netaddr->family == AF_INET
+                                  ? (const void *)&netaddr->type.in
+                                  : (const void *)&netaddr->type.in6,
+                                  netaddr->family, trig, ++rpsdb->hit_id,
+                                  recursed, rpsdb->rsp) ||
+                   (res = dnsrps_ck(&emsg, client, rpsdb, recursed)) < 0)
+               {
+                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, NULL,
+                                    rpz_type, emsg.c, DNS_R_SERVFAIL);
+                       st->m.policy = DNS_RPZ_POLICY_ERROR;
+                       return (DNS_R_SERVFAIL);
+               }
+       } while (res != 0);
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dnsrps_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
+                   isc_boolean_t recursed, dns_rpz_type_t rpz_type,
+                   dns_rdataset_t **p_rdatasetp)
+{
+       dns_rpz_st_t *st;
+       rpsdb_t *rpsdb;
+       librpz_trig_t trig = LIBRPZ_TRIG_CLIENT_IP;
+       isc_region_t r;
+       int res;
+       librpz_emsg_t emsg;
+       isc_result_t result;
+
+       st = client->query.rpz_st;
+       rpsdb = (rpsdb_t *)st->rpsdb;
+
+       result = rpz_ready(client, p_rdatasetp);
+       if (result != ISC_R_SUCCESS) {
+               st->m.policy = DNS_RPZ_POLICY_ERROR;
+               return (result);
+       }
+
+       switch (rpz_type) {
+       case DNS_RPZ_TYPE_QNAME:
+               trig = LIBRPZ_TRIG_QNAME;
+               break;
+       case DNS_RPZ_TYPE_NSDNAME:
+               trig = LIBRPZ_TRIG_NSDNAME;
+               break;
+       default:
+               INSIST(0);
+       }
+
+       dns_name_toregion(trig_name, &r);
+       do {
+               if (!librpz->rsp_push(&emsg, rpsdb->rsp) ||
+                   !librpz->ck_domain(&emsg, r.base, r.length,
+                                      trig, ++rpsdb->hit_id,
+                                      recursed, rpsdb->rsp) ||
+                   (res = dnsrps_ck(&emsg, client, rpsdb, recursed)) < 0)
+               {
+                       rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, NULL,
+                                    rpz_type, emsg.c, DNS_R_SERVFAIL);
+                       st->m.policy = DNS_RPZ_POLICY_ERROR;
+                       return (DNS_R_SERVFAIL);
+               }
+       } while (res != 0);
+       return (ISC_R_SUCCESS);
+}
+#endif /* USE_DNSRPS */
+
 /*
  * Check this address in every eligible policy zone.
  */
@@ -3041,6 +3350,15 @@ rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
 
        CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip");
 
+       rpzs = client->view->rpzs;
+       st = client->query.rpz_st;
+#ifdef USE_DNSRPS
+       if (st->popt.dnsrps_enabled) {
+               return (dnsrps_rewrite_ip(client, netaddr, rpz_type,
+                                        p_rdatasetp));
+       }
+#endif
+
        dns_fixedname_init(&ip_namef);
        ip_name = dns_fixedname_name(&ip_namef);
 
@@ -3048,8 +3366,6 @@ rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
        p_db = NULL;
        p_node = NULL;
 
-       rpzs = client->view->rpzs;
-       st = client->query.rpz_st;
        while (zbits != 0) {
                rpz_num = dns_rpz_find_ip(rpzs, rpz_type, zbits, netaddr,
                                          ip_name, &prefix);
@@ -3319,7 +3635,8 @@ rpz_rewrite_ip_rrsets(ns_client_t *client, dns_name_t *name,
 static isc_result_t
 rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
                 dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
-                dns_rpz_zbits_t allowed_zbits, dns_rdataset_t **rdatasetp)
+                dns_rpz_zbits_t allowed_zbits, isc_boolean_t recursed,
+                dns_rdataset_t **rdatasetp)
 {
        dns_rpz_zones_t *rpzs;
        dns_rpz_zone_t *rpz;
@@ -3335,15 +3652,27 @@ rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
        dns_rpz_policy_t policy;
        isc_result_t result;
 
+#ifndef USE_DNSRPS
+       UNUSED(recursed);
+#endif
+
        CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_name");
 
+       rpzs = client->view->rpzs;
+       st = client->query.rpz_st;
+
+#ifdef USE_DNSRPS
+       if (st->popt.dnsrps_enabled) {
+               return (dnsrps_rewrite_name(client, trig_name, recursed,
+                                            rpz_type, rdatasetp));
+       }
+#endif
+
        zbits = rpz_get_zbits(client, qtype, rpz_type);
        zbits &= allowed_zbits;
        if (zbits == 0)
                return (ISC_R_SUCCESS);
 
-       rpzs = client->view->rpzs;
-
        /*
         * Use the summary database to find the bit mask of policy zones
         * with policies for this trigger name. We do this even if there
@@ -3361,8 +3690,6 @@ rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
        p_db = NULL;
        p_node = NULL;
 
-       st = client->query.rpz_st;
-
        /*
         * Check the trigger name in every policy zone that the summary data
         * says has a hit for the trigger name.
@@ -3513,6 +3840,15 @@ rpz_rewrite_ns_skip(ns_client_t *client, dns_name_t *nsname,
        st->r.label--;
 }
 
+/*
+ * RPZ query result types 
+ */
+typedef enum {
+       qresult_type_done = 0,
+       qresult_type_restart = 1,
+       qresult_type_recurse = 2
+} qresult_type_t;
+
 /*
  * Look for response policy zone QNAME, NSIP, and NSDNAME rewriting.
  */
@@ -3526,12 +3862,15 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
        dns_rdataset_t *rdataset;
        dns_fixedname_t nsnamef;
        dns_name_t *nsname;
-       int qresult_type;
+       qresult_type_t qresult_type;
        dns_rpz_zbits_t zbits;
        isc_result_t result = ISC_R_SUCCESS;
        dns_rpz_have_t have;
        dns_rpz_popt_t popt;
        int rpz_ver;
+#ifdef USE_DNSRPS
+       librpz_emsg_t emsg;
+#endif
 
        CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite");
 
@@ -3543,7 +3882,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
                return (DNS_R_DISALLOWED);
 
        RWLOCK(&rpzs->search_lock, isc_rwlocktype_read);
-       if (rpzs->p.num_zones == 0 ||
+       if ((rpzs->p.num_zones == 0 && !rpzs->p.dnsrps_enabled) ||
            (!RECURSIONOK(client) && rpzs->p.no_rd_ok == 0) ||
            !rpz_ck_dnssec(client, qresult, ordataset, osigset))
        {
@@ -3560,6 +3899,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
                if (st == NULL)
                        return (ISC_R_NOMEMORY);
                st->state = 0;
+               st->rpsdb = NULL;
        }
        if (st->state == 0) {
                st->state |= DNS_RPZ_ACTIVE;
@@ -3579,6 +3919,26 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
                st->popt = popt;
                st->rpz_ver = rpz_ver;
                client->query.rpz_st = st;
+               if (popt.dnsrps_enabled) {
+#ifdef USE_DNSRPS
+                       if (st->rpsdb != NULL) {
+                               dns_db_detach(&st->rpsdb);
+                       }
+                       result = dns_dnsrps_rewrite_init(&emsg, st, rpzs,
+                                                         client->query.qname,
+                                                         client->mctx,
+                                                         RECURSIONOK(client));
+                       if (result != ISC_R_SUCCESS) {
+                               rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
+                                            NULL, DNS_RPZ_TYPE_QNAME,
+                                            emsg.c, result);
+                               st->m.policy = DNS_RPZ_POLICY_ERROR;
+                               return (ISC_R_SUCCESS);
+                       }
+#else
+                       INSIST(0);
+#endif
+               }
        }
 
        /*
@@ -3588,7 +3948,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
        case ISC_R_SUCCESS:
        case DNS_R_GLUE:
        case DNS_R_ZONECUT:
-               qresult_type = 0;
+               qresult_type = qresult_type_done;
                break;
        case DNS_R_EMPTYNAME:
        case DNS_R_NXRRSET:
@@ -3598,7 +3958,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
        case DNS_R_NCACHENXRRSET:
        case DNS_R_CNAME:
        case DNS_R_DNAME:
-               qresult_type = 1;
+               qresult_type = qresult_type_restart;
                break;
        case DNS_R_DELEGATION:
        case ISC_R_NOTFOUND:
@@ -3608,19 +3968,19 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
                 * can rewrite.
                 */
                if (RECURSIONOK(client))
-                       qresult_type = 2;
+                       qresult_type = qresult_type_recurse;
                else
-                       qresult_type = 1;
+                       qresult_type = qresult_type_restart;
                break;
        case ISC_R_FAILURE:
        case ISC_R_TIMEDOUT:
        case DNS_R_BROKENCHAIN:
-               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, client->query.qname,
+               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, NULL,
                             DNS_RPZ_TYPE_QNAME,
                             " stop on qresult in rpz_rewrite()", qresult);
                return (ISC_R_SUCCESS);
        default:
-               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, client->query.qname,
+               rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, NULL,
                             DNS_RPZ_TYPE_QNAME,
                             " stop on unrecognized qresult in rpz_rewrite()",
                             qresult);
@@ -3630,19 +3990,23 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
        rdataset = NULL;
 
        if ((st->state & (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) !=
-           (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) {
+               if (!st->popt.dnsrps_enabled &&
+                   qresult_type == qresult_type_recurse)
+               {
                        /*
                         * This request needs recursion that has not been done.
                         * Get bits for the policy zones that do not need
                         * to wait for the results of recursion.
                         */
                        allowed = st->have.qname_skip_recurse;
-                       if (allowed == 0)
+                       if (allowed == 0) {
                                return (ISC_R_SUCCESS);
+                       }
                } else {
                        allowed = DNS_RPZ_ALL_ZBITS;
                }
@@ -3671,9 +4035,12 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
                 * There is a first time for each name in a CNAME chain
                 */
                if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
+                       isc_boolean_t norec =
+                               ISC_TF(qresult_type != qresult_type_recurse);
                        result = rpz_rewrite_name(client, client->query.qname,
                                                  qtype, DNS_RPZ_TYPE_QNAME,
-                                                 allowed, &rdataset);
+                                                 allowed, norec,
+                                                 &rdataset);
                        if (result != ISC_R_SUCCESS)
                                goto cleanup;
 
@@ -3699,7 +4066,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
                 * Do not bother saving the work from this attempt,
                 * because recusion is so slow.
                 */
-               if (qresult_type == 2)
+               if (qresult_type == qresult_type_recurse)
                        goto cleanup;
 
                /*
@@ -3710,13 +4077,14 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
        }
 
        /*
-        * Check known IP addresses for the query name if the database
-        * lookup resulted in some addresses (qresult_type == 0)
+        * Check known IP addresses for the query name if the database lookup
+        * resulted in some addresses (qresult_type == qresult_type_done)
         * and if we have not already checked them.
         * Any recursion required for the query has already happened.
         * Do not check addresses that will not be in the ANSWER section.
         */
-       if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 && qresult_type == 0 &&
+       if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 &&
+           qresult_type == qresult_type_done &&
            rpz_get_zbits(client, qtype, DNS_RPZ_TYPE_IP) != 0) {
                result = rpz_rewrite_ip_rrsets(client,
                                               client->query.qname, qtype,
@@ -3799,13 +4167,13 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
                        case ISC_R_FAILURE:
                                rpz_rewrite_ns_skip(client, nsname, result,
                                                DNS_RPZ_DEBUG_LEVEL3,
-                                               " NS rpz_rrset_find() ");
+                                               " NS rpz_rrset_find()");
                                continue;
                        default:
                                rpz_rewrite_ns_skip(client, nsname, result,
                                                DNS_RPZ_INFO_LEVEL,
                                                " unrecognized NS"
-                                               " rpz_rrset_find() ");
+                                               " rpz_rrset_find()");
                                continue;
                        }
                }
@@ -3843,6 +4211,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
                                                        qtype,
                                                        DNS_RPZ_TYPE_NSDNAME,
                                                        DNS_RPZ_ALL_ZBITS,
+                                                       ISC_TRUE,
                                                        &rdataset);
                                if (result != ISC_R_SUCCESS) {
                                        dns_rdata_freestruct(&ns);
@@ -3879,6 +4248,17 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
        result = ISC_R_SUCCESS;
 
 cleanup:
+#ifdef USE_DNSRPS
+       if (st->popt.dnsrps_enabled &&
+           st->m.policy != DNS_RPZ_POLICY_ERROR &&
+           !dnsrps_set_p(&emsg, client, st, qtype, &rdataset,
+                         ISC_TF(qresult_type != qresult_type_recurse)))
+       {
+               rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, NULL,
+                            DNS_RPZ_TYPE_BAD, emsg.c, DNS_R_SERVFAIL);
+               st->m.policy = DNS_RPZ_POLICY_ERROR;
+       }
+#endif
        if (st->m.policy != DNS_RPZ_POLICY_MISS &&
            st->m.policy != DNS_RPZ_POLICY_ERROR &&
            st->m.rpz->policy != DNS_RPZ_POLICY_GIVEN)
@@ -5427,7 +5807,8 @@ query_resume(query_ctx_t *qctx) {
                if (qctx->rpz_st->rpz_ver != qctx->client->view->rpzs->rpz_ver)
                {
                        ns_client_log(qctx->client, NS_LOGCATEGORY_CLIENT,
-                                     NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+                                     NS_LOGMODULE_QUERY,
+                                     DNS_RPZ_INFO_LEVEL,
                                      "query_resume: RPZ settings "
                                      "out of date "
                                      "(rpz_ver %d, expected %d)",
@@ -5789,6 +6170,17 @@ query_checkrpz(query_ctx_t *qctx, isc_result_t result) {
                RESTORE(qctx->version, qctx->rpz_st->m.version);
                RESTORE(qctx->zone, qctx->rpz_st->m.zone);
 
+               /*
+                * Add SOA record to additional section
+                */
+               rresult = query_addsoa(qctx,
+                              dns_rdataset_isassociated(qctx->rdataset),
+                              DNS_SECTION_ADDITIONAL);
+               if (rresult != ISC_R_SUCCESS) {
+                       QUERY_ERROR(qctx, result);
+                       return (ISC_R_COMPLETE);
+               }
+
                switch (qctx->rpz_st->m.policy) {
                case DNS_RPZ_POLICY_TCP_ONLY:
                        qctx->client->message->flags |= DNS_MESSAGEFLAG_TC;
@@ -7584,7 +7976,6 @@ query_nodata(query_ctx_t *qctx, isc_result_t result) {
  */
 isc_result_t
 query_sign_nodata(query_ctx_t *qctx) {
-       dns_section_t section;
        isc_result_t result;
        /*
         * Look for a NSEC3 record if we don't have a NSEC record.
@@ -7688,16 +8079,16 @@ query_sign_nodata(query_ctx_t *qctx) {
        }
 
        /*
-        * Add SOA to the additional section if generated by a RPZ
-        * rewrite.
+        * The RPZ SOA has already been added to the additional section
+        * if this was an RPZ rewrite, but if it wasn't, add it now.
         */
-       section = qctx->nxrewrite ? DNS_SECTION_ADDITIONAL
-                                 : DNS_SECTION_AUTHORITY;
-
-       result = query_addsoa(qctx, ISC_UINT32_MAX, section);
-       if (result != ISC_R_SUCCESS) {
-               QUERY_ERROR(qctx, result);
-               return (query_done(qctx));
+       if (!qctx->nxrewrite) {
+               result = query_addsoa(qctx, ISC_UINT32_MAX,
+                                     DNS_SECTION_AUTHORITY);
+               if (result != ISC_R_SUCCESS) {
+                       QUERY_ERROR(qctx, result);
+                       return (query_done(qctx));
+               }
        }
 
        /*