]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
delete duplicate file.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 22 Aug 2019 11:32:34 +0000 (13:32 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 22 Aug 2019 11:32:34 +0000 (13:32 +0200)
contrib/fastrpz.patch.again [deleted file]

diff --git a/contrib/fastrpz.patch.again b/contrib/fastrpz.patch.again
deleted file mode 100644 (file)
index d82226a..0000000
+++ /dev/null
@@ -1,3504 +0,0 @@
-Description: based on the included patch contrib/fastrpz.patch
-Author: fastrpz@farsightsecurity.com
----
-diff --git a/Makefile.in b/Makefile.in
-index e9042712..870d503b 100644
---- a/Makefile.in
-+++ b/Makefile.in
-@@ -23,6 +23,8 @@ CHECKLOCK_SRC=testcode/checklocks.c
- CHECKLOCK_OBJ=@CHECKLOCK_OBJ@
- DNSTAP_SRC=@DNSTAP_SRC@
- DNSTAP_OBJ=@DNSTAP_OBJ@
-+FASTRPZ_SRC=@FASTRPZ_SRC@
-+FASTRPZ_OBJ=@FASTRPZ_OBJ@
- DNSCRYPT_SRC=@DNSCRYPT_SRC@
- DNSCRYPT_OBJ=@DNSCRYPT_OBJ@
- WITH_PYTHONMODULE=@WITH_PYTHONMODULE@
-@@ -126,7 +128,7 @@ validator/val_sigcrypt.c validator/val_utils.c dns64/dns64.c \
- edns-subnet/edns-subnet.c edns-subnet/subnetmod.c \
- edns-subnet/addrtree.c edns-subnet/subnet-whitelist.c \
- cachedb/cachedb.c cachedb/redis.c respip/respip.c $(CHECKLOCK_SRC) \
--$(DNSTAP_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC) $(IPSET_SRC)
-+$(DNSTAP_SRC) $(FASTRPZ_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC) $(IPSET_SRC)
- COMMON_OBJ_WITHOUT_NETCALL=dns.lo infra.lo rrset.lo dname.lo msgencode.lo \
- as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
- iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \
-@@ -139,7 +141,7 @@ autotrust.lo val_anchor.lo \
- validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \
- val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo redis.lo authzone.lo \
- $(SUBNET_OBJ) $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ) \
--$(IPSECMOD_OBJ) $(IPSET_OBJ) respip.lo
-+$(FASTRPZ_OBJ) $(IPSECMOD_OBJ) $(IPSET_OBJ) respip.lo
- COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \
- outside_network.lo
- COMMON_OBJ=$(COMMON_OBJ_WITHOUT_UB_EVENT) ub_event.lo
-@@ -408,6 +410,11 @@ dnscrypt.lo dnscrypt.o: $(srcdir)/dnscrypt/dnscrypt.c config.h \
-       $(srcdir)/util/config_file.h $(srcdir)/util/log.h \
-       $(srcdir)/util/netevent.h
-+# fastrpz
-+rpz.lo rpz.o: $(srcdir)/fastrpz/rpz.c config.h fastrpz/rpz.h fastrpz/librpz.h \
-+      $(srcdir)/util/config_file.h $(srcdir)/daemon/daemon.h \
-+      $(srcdir)/util/log.h
-+
- # Python Module
- pythonmod.lo pythonmod.o: $(srcdir)/pythonmod/pythonmod.c config.h \
-       pythonmod/interface.h \
-diff --git a/config.h.in b/config.h.in
-index 1bfe4426..0136421d 100644
---- a/config.h.in
-+++ b/config.h.in
-@@ -1315,4 +1315,11 @@ void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
- /** the version of unbound-control that this software implements */
- #define UNBOUND_CONTROL_VERSION 1
--
-+/* have __attribute__s used in librpz.h */
-+#undef LIBRPZ_HAVE_ATTR
-+/** fastrpz librpz.so */
-+#undef FASTRPZ_LIBRPZ_PATH
-+/** 0=no fastrpz  1=static link  2=dlopen() */
-+#undef FASTRPZ_LIB_OPEN
-+/** turn on fastrpz response policy zones */
-+#undef ENABLE_FASTRPZ
-diff --git a/configure.ac b/configure.ac
-index 811ad007..a8346f11 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -6,6 +6,7 @@ sinclude(ax_pthread.m4)
- sinclude(acx_python.m4)
- sinclude(ac_pkg_swig.m4)
- sinclude(dnstap/dnstap.m4)
-+sinclude(fastrpz/rpz.m4)
- sinclude(dnscrypt/dnscrypt.m4)
- # must be numbers. ac_defun because of later processing
-@@ -1649,6 +1650,9 @@ case "$enable_ipset" in
-               ;;
- esac
-+# check for Fastrpz with fastrpz/rpz.m4
-+ck_FASTRPZ
-+
- AC_MSG_CHECKING([if ${MAKE:-make} supports $< with implicit rule in scope])
- # on openBSD, the implicit rule make $< work.
- # on Solaris, it does not work ($? is changed sources, $^ lists dependencies).
-diff --git a/daemon/daemon.c b/daemon/daemon.c
-index 96cc443e..d08b2e56 100644
---- a/daemon/daemon.c
-+++ b/daemon/daemon.c
-@@ -91,6 +91,9 @@
- #include "sldns/keyraw.h"
- #include "respip/respip.h"
- #include <signal.h>
-+#ifdef ENABLE_FASTRPZ
-+#include "fastrpz/rpz.h"
-+#endif
- #ifdef HAVE_SYSTEMD
- #include <systemd/sd-daemon.h>
-@@ -460,6 +463,14 @@ daemon_create_workers(struct daemon* daemon)
-               dt_apply_cfg(daemon->dtenv, daemon->cfg);
- #else
-               fatal_exit("dnstap enabled in config but not built with dnstap support");
-+#endif
-+      }
-+      if(daemon->cfg->rpz_enable) {
-+#ifdef ENABLE_FASTRPZ
-+              rpz_init(&daemon->rpz_clist, &daemon->rpz_client, daemon->cfg);
-+#else
-+              fatal_exit("fastrpz enabled in config"
-+                         " but not built with fastrpz");
- #endif
-       }
-       for(i=0; i<daemon->num; i++) {
-@@ -726,6 +737,9 @@ daemon_cleanup(struct daemon* daemon)
- #ifdef USE_DNSCRYPT
-       dnsc_delete(daemon->dnscenv);
-       daemon->dnscenv = NULL;
-+#endif
-+#ifdef ENABLE_FASTRPZ
-+      rpz_delete(&daemon->rpz_clist, &daemon->rpz_client);
- #endif
-       daemon->cfg = NULL;
- }
-diff --git a/daemon/daemon.h b/daemon/daemon.h
-index 5749dbef..64ce230f 100644
---- a/daemon/daemon.h
-+++ b/daemon/daemon.h
-@@ -136,6 +136,11 @@ struct daemon {
-       /** the dnscrypt environment */
-       struct dnsc_env* dnscenv;
- #endif
-+#ifdef ENABLE_FASTRPZ
-+      /** global opaque rpz handles */
-+      struct librpz_clist *rpz_clist;
-+      struct librpz_client *rpz_client;
-+#endif
- };
- /**
-diff --git a/daemon/worker.c b/daemon/worker.c
-index 263fcddf..e6bc84bd 100644
---- a/daemon/worker.c
-+++ b/daemon/worker.c
-@@ -75,6 +75,9 @@
- #include "libunbound/context.h"
- #include "libunbound/libworker.h"
- #include "sldns/sbuffer.h"
-+#ifdef ENABLE_FASTRPZ
-+#include "fastrpz/rpz.h"
-+#endif
- #include "sldns/wire2str.h"
- #include "util/shm_side/shm_main.h"
- #include "dnscrypt/dnscrypt.h"
-@@ -533,8 +536,27 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
-                       /* not secure */
-                       secure = 0;
-                       break;
-+#ifdef ENABLE_FASTRPZ
-+              case sec_status_rpz_rewritten:
-+              case sec_status_rpz_drop:
-+                      fatal_exit("impossible cached RPZ sec_status");
-+                      break;
-+#endif
-               }
-       }
-+#ifdef ENABLE_FASTRPZ
-+      if(repinfo->rpz) {
-+              /* Scan the cached answer for RPZ hits.
-+               * ret=1 use cache entry
-+               * ret=-1 rewritten response already sent or dropped
-+               * ret=0 deny a cached entry exists
-+               */
-+              int ret = rpz_worker_cache(worker, msg->rep, qinfo,
-+                                         id, flags, edns, repinfo);
-+              if(ret != 1)
-+                      return ret;
-+      }
-+#endif
-       /* return this delegation from the cache */
-       edns_bak = *edns;
-       edns->edns_version = EDNS_ADVERTISED_VERSION;
-@@ -699,6 +721,23 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
-                       secure = 0;
-               }
-       } else  secure = 0;
-+#ifdef ENABLE_FASTRPZ
-+      if(repinfo->rpz) {
-+              /* Scan the cached answer for RPZ hits.
-+               * ret=1 use cache entry
-+               * ret=-1 rewritten response already sent or dropped
-+               * ret=0 deny a cached entry exists
-+               */
-+              int ret = rpz_worker_cache(worker, rep, qinfo, id, flags, edns,
-+                                         repinfo);
-+              if(ret != 1) {
-+                      rrset_array_unlock_touch(worker->env.rrset_cache,
-+                                               worker->scratchpad, rep->ref,
-+                                               rep->rrset_count);
-+                      return ret;
-+              }
-+        }
-+#endif
-       edns_bak = *edns;
-       edns->edns_version = EDNS_ADVERTISED_VERSION;
-@@ -1410,6 +1449,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
-               log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
-                       &repinfo->addr, repinfo->addrlen);
-               goto send_reply;
-+#ifdef ENABLE_FASTRPZ
-+      } else {
-+              /* Start to rewrite for response policy zones.
-+               * This can hit a qname trigger and be done. */
-+              if(rpz_start(worker, &qinfo, repinfo, &edns)) {
-+                      regional_free_all(worker->scratchpad);
-+                      return 0;
-+              }
-+#endif
-       }
-       /* If we've found a local alias, replace the qname with the alias
-@@ -1458,12 +1506,21 @@ lookup_cache:
-               h = query_info_hash(lookup_qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
-               if((e=slabhash_lookup(worker->env.msg_cache, h, lookup_qinfo, 0))) {
-                       /* answer from cache - we have acquired a readlock on it */
--                      if(answer_from_cache(worker, &qinfo,
-+                      ret = answer_from_cache(worker, &qinfo,
-                               cinfo, &need_drop, &alias_rrset, &partial_rep,
-                               (struct reply_info*)e->data,
-                               *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
-                               sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
--                              &edns)) {
-+                              &edns);
-+#ifdef ENABLE_FASTRPZ
-+                      if(ret < 0) {
-+                              /* RPZ already dropped or sent a response. */
-+                              lock_rw_unlock(&e->lock);
-+                              regional_free_all(worker->scratchpad);
-+                              return 0;
-+                      }
-+#endif
-+                      if(ret) {
-                               /* prefetch it if the prefetch TTL expired.
-                                * Note that if there is more than one pass
-                                * its qname must be that used for cache
-@@ -1518,11 +1575,19 @@ lookup_cache:
-                       lock_rw_unlock(&e->lock);
-               }
-               if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
--                      if(answer_norec_from_cache(worker, &qinfo,
-+                      ret = answer_norec_from_cache(worker, &qinfo,
-                               *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), 
-                               sldns_buffer_read_u16_at(c->buffer, 2), repinfo, 
--                              &edns)) {
-+                              &edns);
-+                      if(ret) {
-                               regional_free_all(worker->scratchpad);
-+#ifdef ENABLE_FASTRPZ
-+                              if(ret < 0) {
-+                                      /* RPZ already dropped
-+                                       * or sent a response. */
-+                                      return 0;
-+                              }
-+#endif
-                               goto send_reply;
-                       }
-                       verbose(VERB_ALGO, "answer norec from cache -- "
-diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
-index b1d8c790..10c0aa58 100644
---- a/doc/unbound.conf.5.in
-+++ b/doc/unbound.conf.5.in
-@@ -1801,6 +1801,81 @@ List domain for which the AAAA records are ignored and the A record is
- used by dns64 processing instead.  Can be entered multiple times, list a
- new domain for which it applies, one per line.  Applies also to names
- underneath the name given.
-+.SS "Response Policy Zone Rewriting"
-+.LP
-+Response policy zone rewriting is controlled with the
-+.B rpz
-+clause.
-+It must contain a
-+.B rpz\-enable:
-+option, and one or more
-+.B rpz\-zone:
-+options.
-+It will usually also contain
-+.B rpz\-option:
-+clauses with general rewriting options or specifying dnsrpzd parameters.
-+Beneath the surface, the text in
-+.B rpz\-zone: \fI<"domain">\fR
-+is converted to \fI"zone domain\\n"\fR and added to the configuration string
-+given to
-+\fIlibrpz\fR(3).
-+The text in
-+.B rpz-option \fI<"text">\fR
-+is also added to that configuration string.
-+.LP
-+If using chroot, then the chroot directory must contain the \fIdnsrpzd\fR(3)
-+command and the shared libraries that it uses.
-+Those can be found with the \fIldd\fR(1) command.
-+.LP
-+Resolver zone and rewriting options and response policy zone triggers and
-+actions are described in \fIlibrpz\fR(3).
-+The separate control file that specifies the policy zones maintained by
-+the dnsrpzd daemon is described in \fIdnsrpzd\fR(8).
-+.LP
-+Many installations need a local whitelist that exempts local
-+domains from rewriting.
-+Whitelist records can be in zones transferred by dnsrpzd from
-+authorities or in a local zone file.
-+.TP
-+.B rpz-enable: \fI<yes or no>
-+enables Fastrpz.
-+If not enabled, the other options in the
-+.B rpz:
-+clause are ignored.
-+.TP
-+.B rpz-zone: \fI<"zone and options">
-+specifies a policy zone and optional per-zone rewriting parameters.
-+.TP
-+.B rpz-option: \fI<"option">
-+specifies general Fastrpz options.
-+.LP
-+Fastrpz is available only on POSIX compliant UNIX-like systems with the
-+\fImmap\fR(2) system call.
-+.LP
-+Fastrpz in Unbound differs from rpz and fastrpz in BIND by
-+.RS 3
-+.HP 4
-+RPZ-CLIENT-IP triggers can only be used in the first policy zone
-+specified with
-+.B rpz-zone:
-+.HP
-+Policy zone rewriting is disabled by the DO bit in DNS requests
-+even when no DNSSEC signatures are supplied by authorities.
-+.HP
-+Unbound local zones are not subject to rpz rewriting.
-+.HP
-+Like Fastrpz with BIND but unlike classic BIND rpz,
-+the ADDITIONAL sections of rewritten responses contain the SOA record from
-+the policy zone used to rewrite the response.
-+.RE
-+.P
-+.nf
-+# example Fastrpz settings for use with chroot on Freebsd
-+rpz:
-+    rpz-zone: "rpz.example.org"
-+    rpz-zone: "other.rpz.example.org ip-as-ns yes"
-+    rpz-option: "dnsrpzd ./dnsrpzd"
-+.fi
- .SS "DNSCrypt Options"
- .LP
- The
-diff --git a/fastrpz/librpz.h b/fastrpz/librpz.h
-new file mode 100644
-index 00000000..645279d1
---- /dev/null
-+++ b/fastrpz/librpz.h
-@@ -0,0 +1,957 @@
-+/*
-+ * 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.
-+ *
-+ * Fastrpz version 1.2.10
-+ */
-+
-+#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 interation 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 intead 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 deamon.  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.
-+ *
-+ * Searchs 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 fuctions
-+ *    to serialize the conncurrent 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 asside 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 authories 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: optionall malloc() buffer containting 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 tranfer 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 the databasse
-+ * @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 */
-diff --git a/fastrpz/rpz.c b/fastrpz/rpz.c
-new file mode 100644
-index 00000000..c5ab7801
---- /dev/null
-+++ b/fastrpz/rpz.c
-@@ -0,0 +1,1352 @@
-+/*
-+ * fastrpz/rpz.c - interface to the fastrpz response policy zone library
-+ *
-+ * Optimize no-rewrite cases for speed but optimize rewriting for
-+ * simplicity and size.
-+ */
-+
-+#include "config.h"
-+
-+#ifdef ENABLE_FASTRPZ
-+#include "daemon/daemon.h"
-+#define LIBRPZ_LIB_OPEN FASTRPZ_LIB_OPEN
-+#include "fastrpz/rpz.h"
-+#include "daemon/worker.h"
-+#include "iterator/iter_delegpt.h"
-+#include "iterator/iter_utils.h"
-+#include "iterator/iterator.h"
-+#include "util/data/dname.h"
-+#include "util/data/msgencode.h"
-+#include "util/data/msgparse.h"
-+#include "util/data/msgreply.h"
-+#include "util/log.h"
-+#include "util/netevent.h"
-+#include "util/net_help.h"
-+#include "util/regional.h"
-+#include "util/storage/slabhash.h"
-+#include "services/cache/dns.h"
-+#include "services/cache/rrset.h"
-+#include "services/mesh.h"
-+#include "sldns/sbuffer.h"
-+#include "sldns/rrdef.h"
-+
-+
-+typedef enum state {
-+      /* No more rewriting */
-+      st_off = 1,
-+      /* Send SERVFAIL */
-+      st_servfail,
-+      /* No dispositive hit yet */
-+      st_unknown,
-+      /* Let the iterator resolve a CNAME or get a delegation point. */
-+      st_iterate,
-+      /* Let the iterator resolve NS to check NSIP or NSDNAME triggers. */
-+      st_ck_ns,
-+      /* We have an answer */
-+      st_rewritten,
-+} st_t;
-+
-+
-+/* RPZ state pointed to by struct comm_reply */
-+typedef struct commreply_rpz {
-+      /* librpz state */
-+      librpz_rsp_t*   rsp;
-+      /* ID for log messages */
-+      int             log_id;
-+
-+      /* from configuration */
-+      int             min_ns_dots;
-+
-+      /* Running in the iterator */
-+      bool            iterating;
-+
-+      /* current and previous state and librpz result */
-+      st_t            st;
-+      st_t            saved_st[LIBRPZ_RSP_STACK_DEPTH-1];
-+      librpz_result_t result;
-+
-+      /* Stop adding CNAMEs to the prepend list before this owner name. */
-+      librpz_domain_buf_t cname_hit;
-+      /* It is not the first CNAME */
-+      bool            cname_hit_2nd;
-+      librpz_result_id_t hit_id;
-+} commreply_rpz_t;
-+
-+
-+/* Generate an ID for log messages. */
-+static int log_id;
-+
-+librpz_t *librpz;
-+
-+
-+static void LIBRPZ_NORET
-+rpz_assert(const char *s)
-+{
-+      fatal_exit("%s", s);
-+      exit(1);
-+}
-+#define RPZ_ASSERT(c) ((c) ? (void)0 : rpz_assert(#c), (void)0)
-+
-+/*
-+ * librpz client handle locking
-+ */
-+static void
-+lock_destroy(void* mutex)
-+{
-+      lock_basic_destroy(mutex);
-+      free(mutex);
-+}
-+
-+static void
-+lock(void* mutex)
-+{
-+      lock_basic_lock(mutex);
-+}
-+
-+static void
-+unlock(void* mutex)
-+{
-+      lock_basic_unlock(mutex);
-+}
-+
-+
-+static void
-+log_fnc(librpz_log_level_t level, void* ATTR_UNUSED(ctx), const char* buf)
-+{
-+      /* Setting librpz_log_level overrides the unbound "verbose" level. */
-+      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:
-+              log_err("rpz: %s", buf);
-+              break;
-+
-+      case LIBRPZ_LOG_TRACE1:         /* big events such as dnsrpzd starts */
-+              verbose(VERB_OPS, "rpz: %s", buf);
-+              break;
-+
-+      case LIBRPZ_LOG_TRACE2:         /* smaller dnsrpzd zone transfers */
-+              verbose(VERB_DETAIL, "rpz: %s", buf);
-+              break;
-+
-+      case LIBRPZ_LOG_TRACE3:         /* librpz hits */
-+              verbose(VERB_QUERY, "rpz: %s", buf);
-+              break;
-+
-+      case LIBRPZ_LOG_TRACE4:         /* librpz lookups */
-+              verbose(VERB_CLIENT, "rpz: %s", buf);
-+              break;
-+      }
-+}
-+
-+
-+/* Release the librpz version. */
-+static void
-+rpz_off(commreply_rpz_t* rpz, st_t st)
-+{
-+      if(!rpz)
-+              return;
-+      rpz->st = st;
-+      librpz->rsp_detach(&rpz->rsp);
-+}
-+
-+
-+static void LIBRPZ_PF(2,3)
-+log_fail(commreply_rpz_t* rpz, const char* p, ...)
-+{
-+      va_list args;
-+
-+      if(rpz->st == st_servfail)
-+              return;
-+
-+      va_start(args, p);
-+      librpz->vlog(LIBRPZ_LOG_ERROR, rpz, p, args);
-+      va_end(args);
-+      if(!rpz)
-+              return;
-+      rpz_off(rpz, st_servfail);
-+}
-+
-+
-+/* Announce a rewrite. */
-+static void
-+log_rewrite(uint8_t* qname, librpz_policy_t policy, const char* msg,
-+          commreply_rpz_t* rpz)
-+{
-+      char policy_buf[POLICY2STR_SIZE];
-+      char qname_nm[LDNS_MAX_DOMAINLEN+1];
-+      librpz_domain_buf_t tdomain;
-+      char tdomain_nm[LDNS_MAX_DOMAINLEN+1];
-+      librpz_emsg_t emsg;
-+
-+      if(rpz->st == st_servfail || !rpz->result.log)
-+              return;
-+      if(librpz->log_level_val(LIBRPZ_LOG_INVALID) < LIBRPZ_LOG_TRACE1)
-+              return;
-+
-+      dname_str(qname, qname_nm);
-+
-+      if(!librpz->rsp_domain(&emsg, &tdomain, rpz->rsp)) {
-+              librpz->log(LIBRPZ_LOG_ERROR, rpz, "%s", emsg.c);
-+              return;
-+      }
-+      dname_str(tdomain.d, tdomain_nm);
-+
-+      librpz->log(LIBRPZ_LOG_TRACE3, rpz, "%srewriting %s via %s %s to %s",
-+                  msg, qname_nm, tdomain_nm,
-+                  librpz->trig2str(rpz->result.trig),
-+                  librpz->policy2str(policy, policy_buf,
-+                                     sizeof(policy_buf)));
-+}
-+
-+
-+/* Connect to and start dnsrpzd if necessary for the unbound daemon.
-+ *    Require "rpz-conf: path" to specify the rpz configuration file.
-+ *    The unbound server directory name is the default rpz working
-+ *        directory.  If unbound uses chroot, then the dnsrpzd working
-+ *        directory must be in the chroot tree.
-+ *    The database and socket are closed and re-opened.
-+ */
-+void
-+rpz_init(librpz_clist_t** pclist, librpz_client_t** pclient,
-+       const struct config_file* cfg)
-+{
-+      lock_basic_type* mutex;
-+      librpz_emsg_t emsg;
-+
-+      if(!librpz) {
-+              librpz = librpz_lib_open(&emsg, NULL, FASTRPZ_LIBRPZ_PATH);
-+              if(!librpz)
-+                      fatal_exit("rpz: %s", emsg.c);
-+      }
-+
-+      librpz->set_log(&log_fnc, NULL);
-+
-+      if(!cfg->rpz_cstr)
-+              fatal_exit("rpz: rpz-zone: not set");
-+
-+      librpz->client_detach(pclient);
-+      librpz->clist_detach(pclist);
-+
-+      mutex = malloc(sizeof(*mutex));
-+      if(!mutex)
-+              fatal_exit("rpz: no memory for lock");
-+      lock_basic_init(mutex);
-+
-+      *pclist = librpz->clist_create(&emsg, &lock, &unlock, &lock_destroy,
-+                                     mutex, NULL);
-+      if(!pclist)
-+              fatal_exit("rpz: %s", emsg.c);
-+
-+      *pclient = librpz->client_create(&emsg, *pclist, cfg->rpz_cstr, false);
-+      if(!*pclient)
-+              fatal_exit("rpz: %s", emsg.c);
-+
-+      if(!librpz->connect(&emsg, *pclient, true))
-+              fatal_exit("rpz: %s", emsg.c);
-+
-+      verbose(VERB_OPS, "rpz: librpz version %s", librpz->version);
-+}
-+
-+
-+/* Stop using librpz on behalf of a worker thread. */
-+void
-+rpz_delete(librpz_clist_t** pclist, librpz_client_t** pclient)
-+{
-+      if(librpz) {
-+              librpz->client_detach(pclient);
-+              librpz->clist_detach(pclist);
-+      }
-+}
-+
-+
-+/* Release the librpz resources held for a DNS client request. */
-+void
-+rpz_end(struct comm_reply* commreply)
-+{
-+      if(!commreply->rpz)
-+              return;
-+      rpz_off(commreply->rpz, commreply->rpz->st);
-+      free(commreply->rpz);
-+      commreply->rpz = NULL;
-+}
-+
-+
-+static bool
-+push_st(commreply_rpz_t* rpz)
-+{
-+      librpz_emsg_t emsg;
-+
-+      if(rpz->st == st_off || rpz->st == st_servfail) {
-+              librpz->log(LIBRPZ_LOG_ERROR, rpz,
-+                          "state %d in push_st()", rpz->st);
-+              return false;
-+      }
-+      if(!librpz->rsp_push(&emsg, rpz->rsp))
-+              log_fail(rpz, "%s", emsg.c);
-+      memmove(&rpz->saved_st[1], &rpz->saved_st[0],
-+              sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0]));
-+      rpz->saved_st[0] = rpz->st;
-+      return rpz->st != st_servfail;
-+}
-+
-+
-+static bool
-+pop_st(commreply_rpz_t* rpz)
-+{
-+      librpz_emsg_t emsg;
-+
-+      if(rpz->rsp && !librpz->rsp_pop(&emsg, &rpz->result, rpz->rsp))
-+              log_fail(rpz, "%s", emsg.c);
-+      if(rpz->st != st_servfail)
-+              rpz->st = rpz->saved_st[0];
-+      memmove(&rpz->saved_st[0], &rpz->saved_st[1],
-+              sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0]));
-+      return rpz->st != st_servfail;
-+}
-+
-+static bool
-+pop_discard_st(commreply_rpz_t* rpz)
-+{
-+      librpz_emsg_t emsg;
-+
-+      if(rpz->rsp && !librpz->rsp_pop_discard(&emsg, rpz->rsp))
-+              log_fail(rpz, "%s", emsg.c);
-+      memmove(&rpz->saved_st[0], &rpz->saved_st[1],
-+              sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0]));
-+      return rpz->st != st_servfail;
-+}
-+
-+/* Check a rewrite attempt for errors and a disabled zone. */
-+static bool                           /* true=repeat the check */
-+ck_after(uint8_t* qname, bool recursed, librpz_trig_t trig,
-+       commreply_rpz_t* rpz)
-+{
-+      librpz_emsg_t emsg;
-+
-+      if(rpz->st == st_servfail)
-+              return false;
-+
-+      if(!librpz->rsp_result(&emsg, &rpz->result, recursed, rpz->rsp)) {
-+              log_fail(rpz, "%s", emsg.c);
-+              return false;
-+      }
-+
-+      if(rpz->result.policy == LIBRPZ_POLICY_DISABLED) {
-+              /* Log the hit on the disabled zone, do not try the zone again,
-+               * and restore the state from before the check to forget the hit
-+               * before trying again. */
-+              log_rewrite(qname, rpz->result.zpolicy, "disabled ", rpz);
-+              if(!librpz->rsp_forget_zone(&emsg, rpz->result.cznum, rpz->rsp))
-+                      log_fail(rpz, "%s", emsg.c);
-+              return pop_st(rpz);
-+      }
-+
-+      /* Complain about and forget client-IP address hit that is not
-+       * dispositive.  Client-IP triggers have the highest priority
-+       * within a policy zone, but can be overridden by any hit in a policy
-+       * earlier in the client's (resolver's) list of zones, including
-+       * policies that cannot be hit until after recursion. If we allowed
-+       * client-IP triggers in secondary zones, then than two DNS requests
-+       * that differ only in DNS client-IP addresses could properly
-+       * have differing results.  The Unbound iterator treats identical
-+       * DNS requests the same regardless of DNS client-IP address.
-+       * struct query_info would need to be modified to have an optional
-+       * librpz_prefix_t containing the prefix of the client-IP address hit
-+       * from librpz->rsp_clientip_prefix().  Adding to struct query_info
-+       * would require finding and changing the many and obscure places
-+       * including the Unbound tests to memset(0) the struct query_info
-+       * that they create. */
-+      if(trig == LIBRPZ_TRIG_CLIENT_IP) {
-+              if(rpz->result.cznum != 0) {
-+                      log_rewrite(qname, rpz->result.policy,
-+                                   "ignore secondary ", rpz);
-+                      if(!pop_st(rpz))
-+                              log_fail(rpz, "%s", emsg.c);
-+                      return (false);
-+              }
-+      }
-+
-+      /* Forget the state from before the check and keep the new state
-+       * if we do not have a hit on a disabled policy zone. */
-+      pop_discard_st(rpz);
-+      return false;
-+}
-+
-+
-+/* Get the next RR from the policy record. */
-+static bool
-+next_rr(librpz_rr_t** rrp, const uint8_t* qname, size_t qname_len,
-+      commreply_rpz_t* rpz)
-+{
-+      librpz_emsg_t emsg;
-+
-+      if(!librpz->rsp_rr(&emsg, NULL, NULL, NULL, rrp, &rpz->result,
-+                         qname, qname_len, rpz->rsp)) {
-+              log_fail(rpz, "%s", emsg.c);
-+              *rrp = NULL;
-+              return false;
-+      }
-+      return true;
-+}
-+
-+
-+static bool                           /* false=fatal error to be logged */
-+add_rr(struct sldns_buffer* pkt, const uint8_t* owner, size_t owner_len,
-+       librpz_rr_t* rr, commreply_rpz_t* rpz)
-+{
-+      size_t rdlength;
-+
-+      rdlength = ntohs(rr->rdlength);
-+
-+      if(!sldns_buffer_available(pkt, owner_len + 10 + rdlength)) {
-+              log_fail(rpz, "comm_reply buffer exhausted");
-+              free(rr);
-+              return false;
-+      }
-+      sldns_buffer_write(pkt, owner, owner_len);
-+      /* sizeof(librpz_rr_t)=12 instead of 10 */
-+      sldns_buffer_write(pkt, rr, 10 + rdlength);
-+      return true;
-+}
-+
-+
-+/* Convert a fake incoming DNS message to an Unbound struct dns_msg */
-+static void
-+pkt2dns_msg(struct dns_msg** dnsmsg, struct sldns_buffer* pkt,
-+          commreply_rpz_t* rpz, struct regional* region)
-+{
-+      struct msg_parse* msgparse;
-+
-+      msgparse = regional_alloc(region, sizeof(*msgparse));
-+      if(!msgparse) {
-+              log_fail(rpz, "out of memory for msgparse");
-+              *dnsmsg = NULL;
-+              return;
-+      }
-+      memset(msgparse, 0, sizeof(*msgparse));
-+      if(parse_packet(pkt, msgparse, region) != LDNS_RCODE_NOERROR) {
-+              log_fail(rpz, "packet parse error");
-+              *dnsmsg = NULL;
-+              return;
-+      }
-+      *dnsmsg = dns_alloc_msg(pkt, msgparse, region);
-+      if(!*dnsmsg) {
-+              log_fail(rpz, "dns_alloc_msg() failed");
-+              *dnsmsg = NULL;
-+              return;
-+      }
-+      (*dnsmsg)->rep->security = sec_status_rpz_rewritten;
-+}
-+
-+
-+static bool                           /* false=SERVFAIL */
-+ck_ip_rrset(const void* vdata, int family, librpz_trig_t trig,
-+          uint8_t* qname, commreply_rpz_t* rpz)
-+{
-+      const struct packed_rrset_data* data;
-+      uint rr_n;
-+      size_t len;
-+      librpz_emsg_t emsg;
-+
-+      data = vdata;
-+
-+      /* Loop to ignore disabled zones. */
-+      do {
-+              if(!push_st(rpz))
-+                      return false;
-+              for(rr_n = 0; rr_n < data->count; ++rr_n) {
-+                      len = data->rr_len[rr_n];
-+                      /* Skip bogus including negative placeholding rdata. */
-+                      if((family == AF_INET &&
-+                          len != sizeof(struct in_addr)+2) ||
-+                         (family == AF_INET6 &&
-+                          len != sizeof(struct in6_addr)+2))
-+                              continue;
-+                      if(!librpz->ck_ip(&emsg, data->rr_data[rr_n]+2,
-+                                        family, trig, rpz->hit_id, true,
-+                                        rpz->rsp)) {
-+                              log_fail(rpz, "%s", emsg.c);
-+                              return false;
-+                      }
-+              }
-+      } while(ck_after(qname, true, trig, rpz));
-+      return rpz->st != st_servfail;
-+}
-+
-+
-+static bool                           /* false=SERVFAIL */
-+ck_dname(uint8_t* dname, size_t dname_size, librpz_trig_t trig,
-+       uint8_t* qname, bool recursed, commreply_rpz_t* rpz)
-+{
-+      librpz_emsg_t emsg;
-+
-+      /* Refuse to check the root. */
-+      if(dname_is_root(dname))
-+              return rpz->st != st_servfail;
-+
-+      /* Loop to ignore disabled zones. */
-+      do {
-+              if(!push_st(rpz))
-+                      return false;
-+              if(!librpz->ck_domain(&emsg, dname, dname_size, trig,
-+                                    rpz->hit_id, recursed, rpz->rsp)) {
-+                      log_fail(rpz, "%s", emsg.c);
-+                      return false;
-+              }
-+      } while(ck_after(qname, recursed, trig, rpz));
-+
-+      return rpz->st != st_servfail;
-+}
-+
-+
-+/* Check the IPv4 or IPv6 addresses for one NS name. */
-+static bool                           /* false=st_servfail */
-+ck_1nsip(uint8_t* nsname, size_t nsname_size, int family, int qtype,
-+       bool* have_ns, commreply_rpz_t* rpz, struct module_env* env)
-+{
-+      struct ub_packed_rrset_key* akey;
-+
-+      akey = rrset_cache_lookup(env->rrset_cache, nsname, nsname_size,
-+                                qtype, LDNS_RR_CLASS_IN, 0, 0, 0);
-+      if(akey) {
-+              *have_ns = true;
-+
-+              if(!ck_ip_rrset(akey->entry.data, family, LIBRPZ_TRIG_NSIP,
-+                              nsname, rpz)) {
-+                      lock_rw_unlock(&akey->entry.lock);
-+                      return false;
-+              }
-+              lock_rw_unlock(&akey->entry.lock);
-+      }
-+      return true;
-+}
-+
-+
-+static bool                           /* false=st_servfail */
-+ck_qname(uint8_t* qname, size_t qname_len,
-+       bool recursed,                 /* recursion done */
-+       bool wait_ns,                  /* willing to iterate for NS data */
-+       commreply_rpz_t* rpz, struct module_env* env)
-+{
-+      uint8_t* dname;
-+      size_t dname_size;
-+      int cur_lab;
-+      struct ub_packed_rrset_key* nskey;
-+      const struct packed_rrset_data* nsdata;
-+      uint8_t* nsname;
-+      size_t nsname_size;
-+      uint rr_n;
-+      bool have_ns, tried_ns;
-+
-+      if(!ck_dname(qname, qname_len, LIBRPZ_TRIG_QNAME, qname, false, rpz))
-+              return false;
-+
-+      /* Do not waste time looking for NSDNAME and NSIP hits when there
-+       * are no currently relevant triggers. */
-+      if(!librpz->have_ns_trig(rpz->rsp))
-+              return true;
-+
-+      have_ns = false;
-+      tried_ns = false;
-+      dname = qname;
-+      dname_size = qname_len;
-+      for(cur_lab = dname_count_labels(dname) - 2;
-+          cur_lab > rpz->min_ns_dots;
-+          --cur_lab) {
-+              tried_ns = true;
-+              dname_remove_label(&dname, &dname_size);
-+              nskey = rrset_cache_lookup(env->rrset_cache, dname, dname_size,
-+                                         LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN,
-+                                         0, 0, 0);
-+              if(!nskey)
-+                      continue;
-+
-+              nsdata = (const struct packed_rrset_data*)nskey->entry.data;
-+              for(rr_n = 0;
-+                  rr_n < nsdata->count && rpz->st == st_unknown;
-+                  ++rr_n) {
-+                      nsname = nsdata->rr_data[rr_n]+2;
-+                      nsname_size = nsdata->rr_len[rr_n];
-+                      if(nsname_size <= 2)
-+                              continue;
-+                      nsname_size -= 2;
-+                      if(!ck_dname(nsname, nsname_size, LIBRPZ_TRIG_NSDNAME,
-+                                   qname, recursed, rpz))
-+                              return false;
-+                      if(!ck_1nsip(nsname, nsname_size, AF_INET,
-+                                    LDNS_RR_TYPE_A, &have_ns, rpz, env))
-+                              return false;
-+                      if(!ck_1nsip(nsname, nsname_size, AF_INET6,
-+                                    LDNS_RR_TYPE_AAAA, &have_ns, rpz, env))
-+                              return false;
-+              }
-+              lock_rw_unlock(&nskey->entry.lock);
-+      }
-+
-+      /* If we failed to find NS records, then stop building the response
-+       * before a CNAME with this owner name. */
-+      if(!have_ns && tried_ns && (!recursed || wait_ns)) {
-+              rpz->cname_hit.size = qname_len;
-+              RPZ_ASSERT(rpz->cname_hit.size <= sizeof(rpz->cname_hit.d));
-+              memcpy(rpz->cname_hit.d, qname, qname_len);
-+              rpz->result.hit_id = rpz->hit_id;
-+              rpz->st = st_ck_ns;
-+      }
-+      return true;
-+}
-+
-+
-+/*
-+ * Are we ready to rewrite the response?
-+ */
-+static bool                           /* true=send rewritten response */
-+ck_result(uint8_t* qname, bool recursed,
-+        commreply_rpz_t* rpz, const struct comm_point* commpoint)
-+{
-+      librpz_emsg_t emsg;
-+
-+      switch(rpz->st) {
-+      case st_off:
-+      case st_servfail:
-+      case st_rewritten:
-+              return false;
-+      case st_unknown:
-+              break;
-+      case st_iterate:
-+              return false;
-+      case st_ck_ns:
-+              /* An NSDNAME or NSIP check failed for lack of cached data. */
-+              return false;
-+      default:
-+              fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
-+                         rpz->st);
-+      }
-+
-+      /* Wait for a trigger. */
-+      if(rpz->result.policy == LIBRPZ_POLICY_UNDEFINED) {
-+              if(recursed &&
-+                  rpz->result.zpolicy != LIBRPZ_POLICY_UNDEFINED &&
-+                  !librpz->rsp_result(&emsg, &rpz->result, true, rpz->rsp)) {
-+                      log_fail(rpz, "%s", emsg.c);
-+                      return false;
-+              }
-+              if(rpz->result.policy == LIBRPZ_POLICY_UNDEFINED)
-+                      return false;
-+      }
-+
-+      if(rpz->result.policy == LIBRPZ_POLICY_PASSTHRU) {
-+              log_rewrite(qname, rpz->result.policy, "", rpz);
-+              rpz_off(rpz, st_off);
-+              return false;
-+      }
-+
-+      /* The TCP-only policy answers UDP requests with truncated responses. */
-+      if(rpz->result.policy == LIBRPZ_POLICY_TCP_ONLY &&
-+         commpoint->type == comm_tcp) {
-+              rpz_off(rpz, st_off);
-+              return false;
-+      }
-+
-+      return true;
-+}
-+
-+
-+/*
-+ * Convert an RPZ hit to a struct dns_msg
-+ */
-+static void
-+get_result_msg(struct dns_msg** dnsmsg, struct query_info* qinfo,
-+             uint16_t id, uint16_t flags, bool recursed, commreply_rpz_t* rpz,
-+             struct comm_point* commpoint, struct regional* region)
-+{
-+      librpz_rr_t* rr;
-+      librpz_domain_buf_t origin;
-+      struct sldns_buffer* pkt;
-+      uint16_t num_rrs;
-+      librpz_emsg_t emsg;
-+
-+      *dnsmsg = NULL;
-+      if(!ck_result(qinfo->qname, recursed, rpz, commpoint))
-+              return;
-+
-+      rpz->st = st_rewritten;
-+
-+      if(rpz->result.policy == LIBRPZ_POLICY_DROP) {
-+              log_rewrite(qinfo->qname, rpz->result.policy, "", rpz);
-+              /* Make a fake cached message to carry
-+               * sec_status_rpz_drop and be dropped. */
-+              error_encode(commpoint->buffer, LDNS_RCODE_NOERROR,
-+                           qinfo, id, flags, NULL);
-+              pkt2dns_msg(dnsmsg, commpoint->buffer, rpz, region);
-+              (*dnsmsg)->rep->security = sec_status_rpz_drop;
-+              return;
-+      }
-+
-+      /* Create a DNS message of the RPZ data.
-+       * In many cases that message could be sent directly to the DNS client,
-+       * but sometimes iteration must be used to resolve a CNAME.
-+       * This need not be fast, because rewriting responses should be rare.
-+       * Therefore, use the simpler but slower tactic of generating a
-+       * parsed  version of the message. */
-+
-+      flags &= ~BIT_AA;
-+      flags |= BIT_QR | BIT_RA;
-+      rr = NULL;
-+
-+      /* The TCP-only policy answers UDP requests with truncated responses. */
-+      if(rpz->result.policy == LIBRPZ_POLICY_TCP_ONLY) {
-+              flags |= BIT_TC;
-+
-+      } else if(rpz->result.policy == LIBRPZ_POLICY_NXDOMAIN) {
-+              flags |= LDNS_RCODE_NXDOMAIN;
-+
-+      } else if(rpz->result.policy == LIBRPZ_POLICY_CNAME) {
-+              if(!rpz->iterating &&
-+                 qinfo->qtype != LDNS_RR_TYPE_CNAME) {
-+                      /* The new DNS message would be a CNAME and
-+                       * the external request was not for a CNAME.
-+                       * The worker must punt to the iterator so that
-+                       * the iterator can resolve the CNAME. */
-+                      rpz->st = st_iterate;
-+                      return;
-+              }
-+              next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
-+
-+      } else if(rpz->result.policy == LIBRPZ_POLICY_RECORD ||
-+                rpz->result.policy == LIBRPZ_POLICY_NODATA) {
-+              next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
-+              /* Punt to the iterator if the new DNS message would
-+               * be a CNAME that must be resolved. */
-+              if(!rpz->iterating &&
-+                 qinfo->qtype != LDNS_RR_TYPE_CNAME &&
-+                 rr && rr->type == ntohs(LDNS_RR_TYPE_CNAME)) {
-+                      free(rr);
-+                      rpz->st = st_iterate;
-+                      return;
-+              }
-+      }
-+      log_rewrite(qinfo->qname, rpz->result.policy, "", rpz);
-+
-+      /* Make a buffer containing a DNS message with the RPZ data. */
-+      pkt = commpoint->buffer;
-+      sldns_buffer_clear(pkt);
-+      if(sldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE) {
-+              log_fail(rpz, "comm_reply buffer too small for header");
-+              if(rr)
-+                      free(rr);
-+              return;
-+      }
-+
-+      /* Install ID, flags, QDCOUNT=1, ANCOUNT=# of RPZ RRs, NSCOUNT=0,
-+       * and ARCOUNT=1 for the RPZ SOA. */
-+      sldns_buffer_write_u16(pkt, id);
-+      sldns_buffer_write_u16(pkt, flags);
-+      sldns_buffer_write_u16(pkt, 1); /* QDCOUNT */
-+      sldns_buffer_write_u16(pkt, 0); /* ANCOUNT will be set later */
-+      sldns_buffer_write_u16(pkt, 0); /* NSCOUNT */
-+      sldns_buffer_write_u16(pkt, 1); /* ARCOUNT */
-+
-+      /* Install the question with the LDNS_RR_CLASS_RPZ bit to
-+       * to distinguish this supposed cache entry from the real deal. */
-+      sldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len);
-+      sldns_buffer_write_u16(pkt, qinfo->qtype);
-+      sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_IN);
-+
-+      /* Install the RPZ RRs in the answer section */
-+      num_rrs = 0;
-+      while(rr) {
-+              /* Include only the requested RRs. */
-+              if(qinfo->qtype == LDNS_RR_TYPE_ANY ||
-+                 rr->type == htons(qinfo->qtype) ||
-+                 rr->type == htons(LDNS_RR_TYPE_CNAME)) {
-+                      if(!add_rr(pkt, qinfo->qname, qinfo->qname_len,
-+                                 rr, rpz))
-+                              return;
-+
-+                      ++num_rrs;
-+              }
-+              free(rr);
-+
-+              next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
-+      }
-+      /* Finish ANCOUNT. */
-+      if(num_rrs != 0)
-+              sldns_buffer_write_u16_at(pkt, 6, num_rrs);
-+
-+      /* All rewritten responses have an identifying SOA record in the
-+       * additional section. */
-+      if(!librpz->rsp_soa(&emsg, NULL, &rr, &origin,
-+                          &rpz->result, rpz->rsp)) {
-+              log_fail(rpz, "no soa");
-+              return;
-+      }
-+      if(!add_rr(pkt, origin.d, origin.size, rr, rpz))
-+              return;
-+      free(rr);
-+
-+      /* Create a dns_msg representation of the fake incoming message. */
-+      sldns_buffer_flip(pkt);
-+      pkt2dns_msg(dnsmsg, pkt, rpz, region);
-+}
-+
-+
-+/* Check the RRs in the ANSWER section of a reply_info. */
-+static void
-+ck_reply(struct reply_info* reply, uint8_t* qname, bool wait_ns,
-+       commreply_rpz_t* rpz, struct module_env* env)
-+{
-+      struct ub_packed_rrset_key* rrset;
-+      enum sldns_enum_rr_type type;
-+      uint rrset_n;
-+
-+      /* Check the RRs in the ANSWER section. */
-+      rpz->cname_hit.size = 0;
-+      rpz->cname_hit_2nd = false;
-+      for(rrset_n = 0; rrset_n < reply->an_numrrsets; ++rrset_n) {
-+              /* Check all of the RRs before deciding. */
-+              if(rpz->st != st_unknown)
-+                      return;
-+
-+              rrset = reply->rrsets[rrset_n];
-+              if(ntohs(rrset->rk.rrset_class) != LDNS_RR_CLASS_IN)
-+                      continue;
-+              type = ntohs(rrset->rk.type);
-+
-+              if(type == LDNS_RR_TYPE_A) {
-+                      if(!ck_ip_rrset(rrset->entry.data, AF_INET,
-+                                      LIBRPZ_TRIG_IP, qname, rpz))
-+                              break;
-+
-+              } else if(type == LDNS_RR_TYPE_AAAA) {
-+                      if(!ck_ip_rrset(rrset->entry.data, AF_INET6,
-+                                      LIBRPZ_TRIG_IP, qname, rpz))
-+                              break;
-+
-+              } else if(type == LDNS_RR_TYPE_CNAME) {
-+                      /* Check CNAME owners unless we already have a hit. */
-+                      ++rpz->hit_id;
-+                      if(!ck_qname(rrset->rk.dname, rrset->rk.dname_len,
-+                                   true, wait_ns, rpz, env))
-+                              break;
-+
-+                      /* Do not worry about the CNAME if it did not hit,
-+                       * but note the miss so that it can be prepended
-+                       * if we do hit. */
-+                      if(rpz->result.hit_id != rpz->hit_id) {
-+                              rpz->cname_hit_2nd = true;
-+                              continue;
-+                      }
-+
-+                      /* Stop after hitting a CNAME.
-+                       * The iterator must be used to include CNAMEs before
-+                       * the CNAME that hit in the rewritten response. */
-+                      rpz->cname_hit.size = rrset->rk.dname_len;
-+                      RPZ_ASSERT(rpz->cname_hit.size <= sizeof(rpz->cname_hit.d));
-+                      memcpy(rpz->cname_hit.d, rrset->rk.dname,
-+                             rpz->cname_hit.size);
-+                      break;
-+              }
-+      }
-+}
-+
-+
-+static void
-+worker_servfail(struct worker* worker, struct query_info* qinfo,
-+              uint16_t id, uint16_t flags, struct comm_reply* commreply)
-+{
-+      error_encode(commreply->c->buffer, LDNS_RCODE_SERVFAIL,
-+                   qinfo, id, flags, NULL);
-+      regional_free_all(worker->scratchpad);
-+      comm_point_send_reply(commreply);
-+}
-+
-+
-+/* Send an RPZ answer before the iterator has started.
-+ * @return: 1=continue normal unbound processing
-+ *        0=punt to the iterator
-+ *        -1=rewritten response already sent or dropped. */
-+static int
-+worker_send(struct dns_msg* dnsmsg, struct worker* worker,
-+          struct query_info* qinfo, uint16_t id, uint16_t flags,
-+          struct edns_data* edns, struct comm_reply* commreply)
-+{
-+      switch (commreply->rpz->st) {
-+      case st_off:
-+              return 1;
-+      case st_servfail:
-+              worker_servfail(worker, qinfo, id, flags, commreply);
-+              return -1;
-+      case st_unknown:
-+              return 1;
-+      case st_iterate:
-+      case st_ck_ns:
-+              return 0;               /* punt to the iterator */
-+      case st_rewritten:
-+              break;
-+      default:
-+              fatal_exit("impossible RPZ state %d in worker_send()",
-+                         commreply->rpz->st);
-+      }
-+
-+      if(dnsmsg->rep->security == sec_status_rpz_drop) {
-+              regional_free_all(worker->scratchpad);
-+              comm_point_drop_reply(commreply);
-+              return -1;
-+      }
-+
-+      edns->edns_version = EDNS_ADVERTISED_VERSION;
-+      edns->udp_size = EDNS_ADVERTISED_SIZE;
-+      edns->ext_rcode = 0;
-+      edns->bits = 0;                 /* rewritten response cannot verify. */
-+      if(!reply_info_answer_encode(qinfo, dnsmsg->rep,
-+                                   id, flags | BIT_QR,
-+                                   commreply->c->buffer, 0, 1,
-+                                   worker->scratchpad,
-+                                   edns->udp_size, edns, 0, 0)) {
-+              worker_servfail(worker, qinfo, id, flags, commreply);
-+      } else {
-+              regional_free_all(worker->scratchpad);
-+              comm_point_send_reply(commreply);
-+      }
-+      return -1;
-+}
-+
-+
-+/* Set commreply to an RPZ context if the response might be rewritten.
-+ * Try to answer now with a hit allowed before recursion (iteration). */
-+bool                                  /* true=response sent or dropped */
-+rpz_start(struct worker* worker, struct query_info* qinfo,
-+        struct comm_reply* commreply, struct edns_data* edns)
-+{
-+      commreply_rpz_t* rpz;
-+      uint16_t id, flags;
-+      struct dns_msg* dnsmsg;
-+      int family;
-+      const void* addr;
-+      librpz_emsg_t emsg;
-+
-+      /* Quit if rpz not configured. */
-+      if(!worker->daemon->rpz_client)
-+              return false;
-+
-+      /* Rewrite only the Internet class */
-+      if(qinfo->qclass != LDNS_RR_CLASS_IN)
-+              return false;
-+
-+      rpz = commreply->rpz;
-+      RPZ_ASSERT(!rpz);
-+
-+      dnsmsg = NULL;
-+      id = htons(sldns_buffer_read_u16_at(commreply->c->buffer, 0));
-+      flags = sldns_buffer_read_u16_at(commreply->c->buffer, 2);
-+
-+      rpz = malloc(sizeof(*rpz));
-+      if(!rpz) {
-+              librpz->log(LIBRPZ_LOG_ERROR, NULL, "no memory for rpz");
-+              return 0 > worker_send(dnsmsg, worker, qinfo,
-+                                     id, flags, edns, commreply);
-+      }
-+      memset(rpz, 0, sizeof(*rpz));
-+      rpz->st = st_unknown;
-+      commreply->rpz = rpz;
-+
-+      /* Make a new ID for log messages */
-+      rpz->log_id = __sync_add_and_fetch(&log_id, 1);
-+
-+      /* Get access to the librpz data. */
-+      if(!librpz->rsp_create(&emsg, &rpz->rsp, &rpz->min_ns_dots,
-+                            worker->daemon->rpz_client,
-+                            (flags & BIT_RD) != 0,
-+                            (edns->bits & EDNS_DO) != 0)) {
-+              log_fail(rpz, "%s", emsg.c);
-+              return false;
-+      }
-+      /* Quit if benign reasons prevent rewriting. */
-+      if(!rpz->rsp) {
-+              rpz->st = st_off;
-+              librpz->log(LIBRPZ_LOG_TRACE1, rpz, "%s", emsg.c);
-+              return false;
-+      }
-+
-+      /* Check the client IP address.
-+       * Do not use commreply->srctype because it is often 0. */
-+      family = ((struct sockaddr*)&commreply->addr)->sa_family;
-+      switch(family) {
-+      case AF_INET:
-+              addr = &((struct sockaddr_in*)&commreply->addr)->sin_addr;
-+              break;
-+      case AF_INET6:
-+              addr = &((struct sockaddr_in6*)&commreply->addr)->sin6_addr;
-+              break;
-+      default:
-+              /* Maybe the client is on a UNIX domain socket. */
-+              librpz->log(LIBRPZ_LOG_TRACE2, rpz,
-+                          "unknown client address family %d", family);
-+              addr = NULL;
-+              break;
-+      }
-+      /* Loop to ignore disabled zones. */
-+      while(addr) {
-+              if(!push_st(rpz))
-+                      break;
-+              if(!librpz->ck_ip(&emsg, addr, family, LIBRPZ_TRIG_CLIENT_IP,
-+                                rpz->hit_id, true, rpz->rsp)) {
-+                      log_fail(rpz, "%s", emsg.c);
-+                      break;
-+              }
-+              if(!ck_after(qinfo->qname, false, LIBRPZ_TRIG_CLIENT_IP, rpz))
-+                      break;
-+      }
-+      if(rpz->st == st_servfail)
-+              return 0 > worker_send(dnsmsg, worker, qinfo,
-+                                     id, flags, edns, commreply);
-+
-+      /* Check the QNAME and possibly replace a client-IP hit. */
-+      ck_qname(qinfo->qname, qinfo->qname_len, false, true,
-+               rpz, &worker->env);
-+
-+      get_result_msg(&dnsmsg, qinfo, id, flags, false,
-+                     rpz, commreply->c, worker->scratchpad);
-+      return 0 > worker_send(dnsmsg, worker, qinfo,
-+                             id, flags, edns, commreply);
-+}
-+
-+
-+/* Check a cached reply before iteration.
-+ * @return: 1=use cache entry
-+ *        0=deny a cached entry exists in order to punt to the iterator
-+ *        -1=rewritten response already sent or dropped */
-+int
-+rpz_worker_cache(struct worker* worker, struct reply_info* reply,
-+               struct query_info* qinfo, uint16_t id, uint16_t flags,
-+               struct edns_data* edns, struct comm_reply* commreply)
-+{
-+      commreply_rpz_t* rpz;
-+      struct dns_msg* dnsmsg;
-+      st_t new_st;
-+      librpz_rr_t* rr;
-+
-+      dnsmsg = NULL;
-+
-+      rpz = commreply->rpz;
-+      switch(rpz->st) {
-+      case st_off:
-+              return 1;               /* Send the cache entry. */
-+      case st_servfail:
-+              return worker_send(dnsmsg, worker, qinfo, id, flags,
-+                                 edns, commreply);
-+      case st_unknown:
-+              break;
-+      case st_iterate:
-+      case st_ck_ns:
-+              return 0;               /* Punt to the iterator. */
-+      case st_rewritten:
-+      default:
-+              fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
-+                         rpz->st);
-+      }
-+
-+      /* Check the RRs in the ANSWER section. */
-+      if(!push_st(rpz))
-+              return worker_send(dnsmsg, worker, qinfo, id, flags, edns,
-+                                 commreply);
-+
-+      ck_reply(reply, qinfo->qname, true, rpz, &worker->env);
-+      if(!ck_result(qinfo->qname, true, rpz, commreply->c))
-+              return worker_send(dnsmsg, worker, qinfo, id, flags, edns,
-+                                 commreply);
-+
-+      if(rpz->cname_hit.size != 0) {
-+              /* Punt to the iterator if leading CNAMEs must be
-+               * included in the rewritten response. */
-+              rpz->cname_hit.size = 0;
-+              new_st = st_iterate;
-+
-+      } else if(rpz->result.policy == LIBRPZ_POLICY_CNAME) {
-+              /* Punt if the rewritten response is to a CNAME. */
-+              new_st = st_iterate;
-+
-+      } else {
-+              if(rpz->result.policy == LIBRPZ_POLICY_RECORD) {
-+                      next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
-+                      if(rr) {
-+                              /* Punt we are rewriting to a CNAME. */
-+                              if(rr->type == ntohs(LDNS_RR_TYPE_CNAME)) {
-+                                      free(rr);
-+                                      rpz->st = st_iterate;
-+                              } else {
-+                                      free(rr);
-+                              }
-+                      }
-+              }
-+              get_result_msg(&dnsmsg, qinfo, id, flags, true,
-+                             rpz, commreply->c, worker->scratchpad);
-+              new_st = rpz->st;
-+      }
-+
-+      switch(new_st) {
-+      case st_off:
-+      case st_servfail:
-+              break;
-+      case st_unknown:
-+              pop_discard_st(rpz);
-+              break;
-+      case st_iterate:
-+      case st_ck_ns:
-+              if(pop_st(rpz))
-+                      rpz->st = new_st;
-+              break;
-+      case st_rewritten:
-+              pop_discard_st(rpz);
-+              break;
-+      default:
-+              fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
-+                         rpz->st);
-+      }
-+
-+      return worker_send(dnsmsg, worker, qinfo, id, flags, edns, commreply);
-+}
-+
-+
-+/* Check a cache hit or miss for the iterator.
-+ * A cache miss can already have a QNAME hit that was ignored before checking
-+ * the iterator because of "QNAME-WAIT-RECURSE yes".
-+ * Cache hits are treated like responses from authorities. */
-+bool                                  /* false=SERVFAIL */
-+rpz_iter_cache(struct dns_msg** msg, enum response_type* type,
-+             struct module_qstate* qstate, struct iter_qstate* iq)
-+{
-+      struct comm_reply* commreply;
-+      commreply_rpz_t* rpz;
-+      struct dns_msg* dnsmsg;
-+
-+      commreply = &qstate->mesh_info->reply_list->query_reply;
-+      rpz = commreply->rpz;
-+
-+      rpz->iterating = true;
-+
-+      switch(rpz->st) {
-+      case st_off:
-+              iq->rpz_rewritten = 1;  /* RPZ has nothing to say. */
-+              return true;
-+      case st_servfail:
-+              return false;
-+      case st_unknown:
-+              break;
-+      case st_iterate:
-+      case st_ck_ns:
-+              rpz->st = st_unknown;
-+              if(!ck_qname(iq->qchase.qname, iq->qchase.qname_len,
-+                           *msg != NULL, true, rpz, qstate->env))
-+                      return false;
-+              /* If we must recurse regardless and if NSIP/NSDNAME
-+               * checking failed, then delay in the hope that
-+               * recursion will also get NS data. */
-+              if(rpz->st == st_ck_ns)
-+                      return true;
-+              break;
-+      case st_rewritten:
-+      default:
-+              fatal_exit("impossible RPZ state %d in rpz_iter_cache()",
-+                         rpz->st);
-+      }
-+
-+      push_st(rpz);
-+
-+      /* Check the cache hit. */
-+      if(*msg)
-+              ck_reply((*msg)->rep, iq->qchase.qname, true, rpz, qstate->env);
-+
-+      /* The DNS ID does not matter, because the generated dns_msg
-+       * is nominally from an authority and not to the DNS client. */
-+      get_result_msg(&dnsmsg, &iq->qchase, 1, qstate->query_flags, true,
-+                     rpz, commreply->c, qstate->region);
-+
-+      switch(rpz->st) {
-+      case st_off:
-+              iq->rpz_rewritten = 1;  /* RPZ has nothing to say. */
-+              return true;
-+      case st_servfail:
-+              return false;
-+      case st_unknown:
-+              /* RPZ has nothing to say yet.  Maybe there will be a hit
-+               * later in the CNAME chain. */
-+              return pop_discard_st(rpz);
-+      case st_ck_ns:
-+              /* Try to get NS data for a CNAME found by ck_reply() */
-+              *type = RESPONSE_TYPE_CNAME;
-+              return pop_discard_st(rpz);
-+      case st_iterate:
-+      default:
-+              fatal_exit("impossible RPZ state %d in rpz_iter_cache()",
-+                         rpz->st);
-+      case st_rewritten:
-+              break;
-+      }
-+
-+      if(*msg && rpz->cname_hit.size != 0 && rpz->cname_hit_2nd) {
-+              /* We hit a CNAME owner in the cached msg after not hitting one
-+               * or more CNAME owners.  We need to add those leading CNAMEs
-+               * to the prepend list.  Tell the iterator to treat the cached
-+               * message as a RESPONSE_TYPE_CNAME even if it contains answers.
-+               * handle_cname_response() will stop prepending CNAMEs before
-+               * the triggering CNAME.  handle_cname_response() will cause
-+               * a restart to resolve the target of the preceding CNAME,
-+               * which is the same as the hit CNAME owner. */
-+              rpz->st = st_unknown;
-+              *type = RESPONSE_TYPE_CNAME;
-+              return pop_discard_st(rpz);
-+      }
-+
-+      *msg = dnsmsg;
-+      iq->rpz_security = dnsmsg->rep->security;
-+
-+      if(dnsmsg && dnsmsg->rep->an_numrrsets != 0 &&
-+         dnsmsg->rep->rrsets[0]->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
-+              /* The cached msg triggered a rule that rewrites to a
-+               * CNAME that must be resolved.
-+               * We have a replacement dns_msg with that CNAME and also
-+               * an SOA RR in the ADDITIONAL section that the iterator
-+               * will lose as it adds the CNAME to the prepend list.
-+               * Save the SOA RR in iq->rpz_soa. */
-+              iq->rpz_soa = dnsmsg->rep->rrsets[1];
-+              iq->rpz_rewritten = 1;
-+              *type = RESPONSE_TYPE_CNAME;
-+              return true;
-+      }
-+
-+      /* Otherwise we have rewritten to zero or more non-CNAME RRs.
-+       * (DNAMEs are not supported.)
-+       * Tell the iterator to send the rewritten message. */
-+      *type = RESPONSE_TYPE_ANSWER;
-+      iq->rpz_rewritten = 1;
-+      return true;
-+}
-+
-+
-+/* Check a RESPONSE_TYPE_ANSWER response from an authority in the iterator. */
-+rpz_iter_resp_t
-+rpz_iter_resp(struct module_qstate* qstate, struct iter_qstate* iq,
-+            struct dns_msg** resp, bool* is_cname)
-+{
-+      struct comm_reply* commreply;
-+      commreply_rpz_t* rpz;
-+      struct reply_info* rep;
-+
-+      *is_cname = false;
-+
-+      commreply = &qstate->mesh_info->reply_list->query_reply;
-+      rpz = commreply->rpz;
-+      switch(rpz->st) {
-+      case st_off:
-+      case st_servfail:
-+      case st_iterate:
-+      case st_rewritten:
-+      default:
-+              fatal_exit("impossible RPZ state %d in rpz_iter_resp()",
-+                         rpz->st);
-+      case st_ck_ns:
-+      case st_unknown:
-+              break;
-+      }
-+
-+      /* We know !iq->rpz_rewritten and so the response was after a simple
-+       * cache miss when the original QNAME did not trigger a response
-+       * or after a CNAME whose owner name did hit but was then forgotten
-+       * with pop_st().
-+       * In either case, it is necessary to check the QNAME here.
-+       * Checking the QNAME will not lose a better hit. */
-+      rpz->st = st_unknown;
-+      ck_qname(iq->qchase.qname, iq->qchase.qname_len, true, false,
-+               rpz, qstate->env);
-+
-+      /* Check the RRs in the ANSWER section. */
-+      if(!push_st(rpz))
-+              return rpz_iter_resp_fail;
-+      ck_reply(iq->response->rep, iq->qchase.qname, false, rpz, qstate->env);
-+      get_result_msg(resp, &qstate->qinfo, 1, qstate->query_flags, true,
-+                     rpz, commreply->c, qstate->region);
-+      switch(rpz->st) {
-+      case st_off:
-+              iq->rpz_rewritten = 1;  /* Do not come back. */
-+              return rpz_iter_resp_done;
-+      case st_servfail:               /* Send SERVFAIL */
-+              return rpz_iter_resp_fail;
-+      case st_unknown:
-+      case st_ck_ns:
-+              return rpz_iter_resp_done;  /* continue without change */
-+      case st_iterate:
-+      default:
-+              fatal_exit("impossible RPZ state %d in rpz_iter_resp()",
-+                         rpz->st);
-+      case st_rewritten:
-+              /* Tell the iterator to use handle_cname_response() to
-+               * prepend any preceding CNAMEs.
-+               * We have a replacement dns_msg that also has an SOA RR in the
-+               * ADDITIONAL section that the iterator will lose if it is a
-+               * CNAME.  Save that SOA in that case. */
-+              rep = (*resp)->rep;
-+              if(rep->an_numrrsets != 0 &&
-+                 rep->rrsets[0]->rk.type == ntohs(LDNS_RR_TYPE_CNAME)) {
-+                      *is_cname = true;
-+                      iq->rpz_soa = rep->rrsets[1];
-+              }
-+              return rpz_iter_resp_rewrite;
-+      }
-+}
-+
-+
-+/* Tell handle_cname_response() to stop adding to the answer prepend list
-+ * after adding CNAME with a target that hits a QNAME trigger.
-+ * Do not change any RPZ state, but expect the call of handle_cname_response()
-+ * to try to resolve the CNAME and hit the same QNAME trigger and rewrite
-+ * the response. */
-+rpz_cname_t
-+rpz_cname(struct module_qstate* qstate,
-+        uint8_t* oname, size_t oname_size)
-+{
-+      struct mesh_reply* reply_list;
-+      struct comm_reply* commreply;
-+      commreply_rpz_t* rpz;
-+      rpz_cname_t ret;
-+
-+      /* Quit if RPZ is off */
-+      reply_list = qstate->mesh_info->reply_list;
-+      if(!reply_list)
-+              return rpz_cname_prepend;
-+      commreply = &reply_list->query_reply;
-+      rpz = commreply->rpz;
-+
-+      if(!rpz || rpz->st == st_off)
-+              return rpz_cname_prepend;
-+
-+      /* Stop on a 2nd or later CNAME for rpz_iter_resp(). */
-+      if(rpz->cname_hit.size != 0) {
-+              if(!query_dname_compare(rpz->cname_hit.d, oname))
-+                      return rpz_cname_stop;
-+              return rpz_cname_prepend;
-+      }
-+
-+      if(rpz->st != st_unknown)
-+              fatal_exit("impossible RPZ state %d in rpz_cname()", rpz->st);
-+
-+      ret = rpz_cname_prepend;
-+      if(!push_st(rpz))
-+              return rpz_cname_fail;
-+      /* Stop before prepending a CNAME that would preempt a
-+       * rewritten response or before a possible NSDNAME or NSIP trigger. */
-+      ++rpz->hit_id;
-+      ck_qname(oname, oname_size, true, true, rpz, qstate->env);
-+      if(rpz->st != st_unknown)
-+              ret = rpz_cname_stop;
-+      if(!pop_st(rpz))
-+              return rpz_cname_fail;
-+      return ret;
-+}
-+
-+#endif /* ENABLE_FASTRPZ */
-diff --git a/fastrpz/rpz.h b/fastrpz/rpz.h
-new file mode 100644
-index 00000000..5d7e31c5
---- /dev/null
-+++ b/fastrpz/rpz.h
-@@ -0,0 +1,138 @@
-+/*
-+ * fastrpz/rpz.h - interface to the fastrpz response policy zone library
-+ *
-+ * Copyright (c) 2016 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.
-+ */
-+
-+#ifndef UNBOUND_FASTRPZ_RPZ_H
-+#define UNBOUND_FASTRPZ_RPZ_H
-+
-+#ifndef PACKAGE_VERSION
-+/* Ensure that config.h has been included to correctly set ENABLE_FASTRPZ */
-+#include "config.h"
-+#endif
-+
-+#ifdef ENABLE_FASTRPZ
-+
-+#include "librpz.h"
-+
-+#include "daemon/daemon.h"
-+#include "util/config_file.h"
-+
-+struct comm_point;                    /* forward references */
-+struct comm_reply;
-+struct dns_msg;
-+struct edns_data;
-+struct iter_qstate;
-+struct query_info;
-+struct reply_info;
-+enum response_type;                   /* iterator/iter_utils.h */
-+
-+
-+struct commreply_rpz;
-+
-+/**
-+ * Connect to the librpz database.
-+ * @param pclist: future pointer to opaque librpz client data
-+ * @param pclient: future pointer to opaque librpz client data
-+ * @param cfg: parsed unbound configuration
-+ */
-+void rpz_init(librpz_clist_t** pclist, librpz_client_t** pclient,
-+            const struct config_file* cfg);
-+
-+/**
-+ * Disconnect from the librpz database
-+ * @param client: opaque librpz client data
-+ */
-+void rpz_delete(librpz_clist_t** pclist, librpz_client_t** pclient);
-+
-+/**
-+ * Start working on a DNS request and check for client IP address triggers.
-+ * @param worker: the DNS request context
-+ * @param qinfo: the DNS question
-+ * @param[in,out] commreply: the answer
-+ * @param c: where to send the response
-+ * @param[in,out] edns for the DO flag
-+ * @return true if response already sent or dropped
-+ */
-+bool rpz_start(struct worker* worker, struct query_info* qinfo,
-+             struct comm_reply* commreply, struct edns_data* edns);
-+
-+/**
-+ * Release resources held for a DNS request
-+ * @param rspp: pointer to pointer to rpz client context.
-+ */
-+void rpz_end(struct comm_reply* comm_rep);
-+
-+/**
-+ * Check a cached reply for RPZ hits before iteration
-+ * @param worker: the DNS request context
-+ * @param casheresp: cache reply
-+ * @param qinfo: the DNS question
-+ * @param id from the DNS request
-+ * @param flags from the DNS request
-+ * @param[in,out] edns for the DO flag
-+ * @param[in,out] commreply: RPZ state
-+ * @return 1=use cache entry, -1=rewritten response already sent or dropped,
-+ *    0=deny a cached entry exists
-+ */
-+int rpz_worker_cache(struct worker* worker, struct reply_info* cacheresp,
-+                   struct query_info* qinfo, uint16_t id, uint16_t flags,
-+                   struct edns_data* edns, struct comm_reply* commreply);
-+
-+/**
-+ * Check for an existing RPZ CNAME rewrite with "QNAME-WAIT-RECURSE no"
-+ * that needs to be resolved before resolving the external request.
-+ * @param[out] msg: rewritten CNAME response.
-+ * @param qstate: query state.
-+ * @param iq: iterator query state.
-+ * @return false=send SERVFAIL
-+ */
-+bool rpz_iter_cache(struct dns_msg** msg, enum response_type* type,
-+                  struct module_qstate* qstate, struct iter_qstate* iq);
-+
-+/**
-+ * Check a response from an authority in the iterator.
-+ * @param[out] type: of the final response
-+ * @param qstate: query state.
-+ * @param iq: iterator query state.
-+ * @param is_cname: true if the rewritten response is a CNAME
-+ * @return one of rpz_resp_t
-+ */
-+typedef enum {
-+      rpz_iter_resp_fail,             /* Send SERVFAIL. */
-+      rpz_iter_resp_rewrite,          /* We rewrote the response. */
-+      rpz_iter_resp_done,             /* Restart to refetch glue. */
-+} rpz_iter_resp_t;
-+rpz_iter_resp_t rpz_iter_resp(struct module_qstate* qstate,
-+                            struct iter_qstate* iq, struct dns_msg** resp,
-+                            bool* is_cname);
-+
-+/**
-+ * Check a CNAME RR
-+ * @param qstate: query state.
-+ * @param oname: cname owner name
-+ * @param oname_size: length of oname
-+ * @return: one of rpz_cname_t
-+ */
-+typedef enum {
-+      rpz_cname_fail,                 /* send SERVFAIL */
-+      rpz_cname_prepend,              /* prepend CNAME as usual */
-+      rpz_cname_stop,                 /* stop before prepending this CNAME */
-+} rpz_cname_t;
-+rpz_cname_t rpz_cname(struct module_qstate* qstate,
-+                    uint8_t* oname, size_t oname_size);
-+
-+#endif /* ENABLE_FASTRPZ */
-+#endif /* UNBOUND_FASTRPZ_RPZ_H */
-diff --git a/fastrpz/rpz.m4 b/fastrpz/rpz.m4
-new file mode 100644
-index 00000000..21235355
---- /dev/null
-+++ b/fastrpz/rpz.m4
-@@ -0,0 +1,64 @@
-+# fastrpz/rpz.m4
-+
-+# ck_FASTRPZ
-+# --------------------------------------------------------------------------
-+# check for Fastrpz
-+#   --enable-fastrpz      enable Fastrpz response policy zones
-+#   --enable-fastrpz-dl           Fastrpz delayed link [default=have dlopen]
-+#   --with-fastrpz-dir            directory containing librpz.so
-+#
-+# Fastrpz can be compiled into Unbound everywhere with a reasonably
-+# modern C compiler.  It is enabled on systems with dlopen() and librpz.so.
-+
-+AC_DEFUN([ck_FASTRPZ],
-+[
-+  fastrpz_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([fastrpz-dl],
-+    [  --enable-fastrpz-dl      Fastrpz delayed link [[default=$librpz_dl]]],
-+    [enable_librpz_dl="$enableval"],
-+    [enable_librpz_dl="$librpz_dl"])
-+  AC_ARG_WITH([fastrpz-dir],
-+    [  --with-fastrpz-dir       directory containing librpz.so],
-+    [librpz_path="$withval/librpz.so"], [librpz_path="librpz.so"])
-+  AC_DEFINE_UNQUOTED([FASTRPZ_LIBRPZ_PATH], ["$librpz_path"],
-+    [fastrpz librpz.so])
-+  if test "x$enable_librpz_dl" = "xyes"; then
-+    fastrpz_lib_open=2
-+  else
-+    fastrpz_lib_open=1
-+    # Add librpz.so to linked libraries if we are not using dlopen()
-+    AC_SEARCH_LIBS([librpz_client_create], [rpz], [],
-+      [fastrpz_lib_open=0
-+        fastrpz_avail=no])
-+  fi
-+  AC_DEFINE_UNQUOTED([FASTRPZ_LIB_OPEN], [$fastrpz_lib_open],
-+    [0=no fastrpz  1=static link  2=dlopen()])
-+
-+  AC_ARG_ENABLE([fastrpz],
-+    AS_HELP_STRING([--enable-fastrpz],[enable Fastrpz response policy zones]),
-+    [enable_fastrpz=$enableval],[enable_fastrpz=$fastrpz_avail])
-+  if test "x$enable_fastrpz" = xyes; then
-+    AC_DEFINE([ENABLE_FASTRPZ], [1], [Enable fastrpz])
-+    if test "x$fastrpz_lib_open" = "x0"; then
-+      AC_MSG_ERROR([[dlopen and librpz.so needed for fastrpz]])
-+    fi
-+    # used in Makefile.in
-+    AC_SUBST([FASTRPZ_SRC], [fastrpz/rpz.c])
-+    AC_SUBST([FASTRPZ_OBJ], [rpz.lo])
-+  elif test "x$fastrpz_avail" = "x0"; then
-+    AC_MSG_WARN([[dlopen and librpz.so needed for fastrpz]])
-+  fi
-+])
-diff --git a/iterator/iterator.c b/iterator/iterator.c
-index c906c271..55bf2180 100644
---- a/iterator/iterator.c
-+++ b/iterator/iterator.c
-@@ -68,6 +68,9 @@
- #include "sldns/str2wire.h"
- #include "sldns/parseutil.h"
- #include "sldns/sbuffer.h"
-+#ifdef ENABLE_FASTRPZ
-+#include "fastrpz/rpz.h"
-+#endif
- /* in msec */
- int UNKNOWN_SERVER_NICENESS = 376;
-@@ -551,6 +554,23 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
-               if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME &&
-                       query_dname_compare(*mname, r->rk.dname) == 0 &&
-                       !iter_find_rrset_in_prepend_answer(iq, r)) {
-+#ifdef ENABLE_FASTRPZ
-+                      /* Stop adding CNAME rrsets to the prepend list
-+                       * before defining an RPZ hit. */
-+                      if(!iq->rpz_rewritten) {
-+                              switch (rpz_cname(qstate, *mname, *mname_len)) {
-+                              case rpz_cname_fail:
-+                                      /* send SERVFAIL */
-+                                      return 0;
-+                              case rpz_cname_prepend:
-+                                      /* save the CNAME. */
-+                                      break;
-+                              case rpz_cname_stop:
-+                                      /* Pause before adding the CNAME. */
-+                                      goto stop_short;
-+                              }
-+                      }
-+#endif
-                       /* Add this relevant CNAME rrset to the prepend list.*/
-                       if(!iter_add_prepend_answer(qstate, iq, r))
-                               return 0;
-@@ -559,6 +579,9 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
-               /* Other rrsets in the section are ignored. */
-       }
-+#ifdef ENABLE_FASTRPZ
-+stop_short: ;
-+#endif
-       /* add authority rrsets to authority prepend, for wildcarded CNAMEs */
-       for(i=msg->rep->an_numrrsets; i<msg->rep->an_numrrsets +
-               msg->rep->ns_numrrsets; i++) {
-@@ -1195,6 +1218,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
-       uint8_t* delname;
-       size_t delnamelen;
-       struct dns_msg* msg = NULL;
-+      enum response_type type;
-       log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
-       /* check effort */
-@@ -1281,8 +1305,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
-       }
-       if(msg) {
-               /* handle positive cache response */
--              enum response_type type = response_type_from_cache(msg, 
--                      &iq->qchase);
-+              type = response_type_from_cache(msg, &iq->qchase);
-               if(verbosity >= VERB_ALGO) {
-                       log_dns_msg("msg from cache lookup", &msg->qinfo, 
-                               msg->rep);
-@@ -1290,7 +1313,22 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
-                               (int)msg->rep->ttl, 
-                               (int)msg->rep->prefetch_ttl);
-               }
-+#ifdef ENABLE_FASTRPZ
-+      }
-+      /* Check for an RPZ hit in the cached DNS message or an existing
-+       * RPZ CNAME rewrite that can be resolved now after a hit on the QNAME
-+       * or client IP address.  This can involve a creating a fake cache
-+       * hit. It can also involve overriding an RESPONSE_TYPE_ANSWER
-+       * result from response_type_from_cache().  Or it can ignore
-+       * the cached result to refetch glue. */
-+      if(!iq->rpz_rewritten &&
-+         qstate->mesh_info->reply_list &&
-+         qstate->mesh_info->reply_list->query_reply.rpz &&
-+         !rpz_iter_cache(&msg, &type, qstate, iq))
-+              return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
-+      if(msg) {
-+#endif
-               if(type == RESPONSE_TYPE_CNAME) {
-                       uint8_t* sname = 0;
-                       size_t slen = 0;
-@@ -2714,6 +2752,62 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
-                       sock_list_insert(&qstate->reply_origin, 
-                               &qstate->reply->addr, qstate->reply->addrlen, 
-                               qstate->region);
-+#ifdef ENABLE_FASTRPZ
-+              /* Check the response for an RPZ hit. The response has already
-+               * been saved in the cache.  This should have the same effect
-+               * as finding that response in the cache.
-+               * We have already used rpz_iter_cache() at least once. */
-+              if(!iq->rpz_rewritten &&
-+                 qstate->mesh_info->reply_list &&
-+                 qstate->mesh_info->reply_list->query_reply.rpz) {
-+                      struct dns_msg* resp;
-+                      bool is_cname;
-+                      uint8_t* sname;
-+                      size_t slen;
-+
-+                      switch (rpz_iter_resp(qstate, iq, &resp, &is_cname)) {
-+                      case rpz_iter_resp_fail:
-+                              return error_response(qstate, id,
-+                                                    LDNS_RCODE_SERVFAIL);
-+                      case rpz_iter_resp_rewrite:
-+                              /* Prepend any initial CNAMEs from the original
-+                               * response up to a hit. */
-+                              if(!handle_cname_response(qstate, iq,
-+                                                      iq->response,
-+                                                      &sname, &slen))
-+                                      return error_response(qstate, id,
-+                                                      LDNS_RCODE_SERVFAIL);
-+                              if (resp) {
-+                                      iq->response = resp;
-+                                      iq->rpz_security = resp->rep->security;
-+                                      iq->rpz_rewritten = 1;
-+
-+                                      /* Send the rewritten record if it
-+                                       * is not a CNAME. */
-+                                      if(!is_cname)
-+                                          break;
-+
-+                                      /* Prepend the new CNAME
-+                                       * and restart to resolve it. */
-+                                      if(!handle_cname_response(qstate, iq,
-+                                                      resp, &sname, &slen))
-+                                          return error_response(qstate, id,
-+                                                      LDNS_RCODE_SERVFAIL);
-+                              }
-+                              iq->qchase.qname = sname;
-+                              iq->qchase.qname_len = slen;
-+                              iq->dp = NULL;
-+                              iq->refetch_glue = 0;
-+                              iq->query_restart_count++;
-+                              iq->sent_count = 0;
-+                              iq->state = INIT_REQUEST_STATE;
-+                              return 1;
-+
-+                      case rpz_iter_resp_done:
-+                              break;
-+                      }
-+              }
-+#endif
-               if(iq->minimisation_state != DONOT_MINIMISE_STATE
-                       && !(iq->chase_flags & BIT_RD)) {
-                       if(FLAGS_GET_RCODE(iq->response->rep->flags) != 
-@@ -3467,12 +3561,44 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
-                * but only if we did recursion. The nonrecursion referral
-                * from cache does not need to be stored in the msg cache. */
-               if(!qstate->no_cache_store && qstate->query_flags&BIT_RD) {
-+#ifdef ENABLE_FASTRPZ
-+                      /* Do not save RPZ rewritten messages. */
-+                      if(!iq->rpz_rewritten)
-+#endif
-                       iter_dns_store(qstate->env, &qstate->qinfo, 
-                               iq->response->rep, 0, qstate->prefetch_leeway,
-                               iq->dp&&iq->dp->has_parent_side_NS,
-                               qstate->region, qstate->query_flags);
-               }
-       }
-+#ifdef ENABLE_FASTRPZ
-+      if(iq->rpz_rewritten) {
-+              /* Restore RPZ marks on a rewritten response.  The marks
-+               * are lost if the rewrite is to a CNAME. */
-+              iq->response->rep->security = iq->rpz_security;
-+
-+              /* Append the RPZ SOA to rewritten CNAME chains. */
-+              if(iq->rpz_soa) {
-+                      struct ub_packed_rrset_key** sets;
-+                      uint n;
-+
-+                      n = iq->response->rep->rrset_count;
-+                      sets = regional_alloc(qstate->region,
-+                                            (1+n) * sizeof(*sets));
-+                      if(!sets) {
-+                              log_err("append RPZ SOA: out of memory");
-+                              return error_response(qstate, id,
-+                                                    LDNS_RCODE_SERVFAIL);
-+                      }
-+                      memcpy(sets, iq->response->rep->rrsets,
-+                             n * sizeof(struct ub_packed_rrset_key*));
-+                      sets[n] = iq->rpz_soa;
-+                      iq->response->rep->rrsets = sets;
-+                      ++iq->response->rep->rrset_count;
-+                      ++iq->response->rep->ar_numrrsets;
-+              }
-+      }
-+#endif
-       qstate->return_rcode = LDNS_RCODE_NOERROR;
-       qstate->return_msg = iq->response;
-       return 0;
-diff --git a/iterator/iterator.h b/iterator/iterator.h
-index a2f1b570..e1e4a738 100644
---- a/iterator/iterator.h
-+++ b/iterator/iterator.h
-@@ -386,6 +386,16 @@ struct iter_qstate {
-        */
-       int minimise_count;
-+
-+#ifdef ENABLE_FASTRPZ
-+      /** The response has been rewritten by RPZ. */
-+      int rpz_rewritten;
-+      /** RPZ SOA RR for the ADDITIONAL section */
-+      struct ub_packed_rrset_key* rpz_soa;
-+      /** sec_status_rpz_rewritten or sec_status_rpz_drop if rewritten. */
-+      enum sec_status rpz_security;
-+#endif
-+
-       /**
-        * Count number of time-outs. Used to prevent resolving failures when
-        * the QNAME minimisation QTYPE is blocked. */
-diff --git a/services/cache/dns.c b/services/cache/dns.c
-index aa4efec7..5dd3412e 100644
---- a/services/cache/dns.c
-+++ b/services/cache/dns.c
-@@ -945,6 +945,14 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
-       struct regional* region, uint32_t flags)
- {
-       struct reply_info* rep = NULL;
-+
-+#ifdef ENABLE_FASTRPZ
-+      /* Never save RPZ rewritten data. */
-+      if (msgrep->security == sec_status_rpz_drop ||
-+          msgrep->security == sec_status_rpz_rewritten)
-+              return 1;
-+#endif
-+
-       /* alloc, malloc properly (not in region, like msg is) */
-       rep = reply_info_copy(msgrep, env->alloc, NULL);
-       if(!rep)
-diff --git a/services/mesh.c b/services/mesh.c
-index 27f91940..f1bd4e90 100644
---- a/services/mesh.c
-+++ b/services/mesh.c
-@@ -60,6 +60,9 @@
- #include "sldns/wire2str.h"
- #include "services/localzone.h"
- #include "util/data/dname.h"
-+#ifdef ENABLE_FASTRPZ
-+#include "fastrpz/rpz.h"
-+#endif
- #include "respip/respip.h"
- #include "services/listen_dnsport.h"
-@@ -1076,6 +1079,13 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
-       else    secure = 0;
-       if(!rep && rcode == LDNS_RCODE_NOERROR)
-               rcode = LDNS_RCODE_SERVFAIL;
-+#ifdef ENABLE_FASTRPZ
-+      /* Drop the response here for LIBRPZ_POLICY_DROP after iteration. */
-+      if(rep && rep->security == sec_status_rpz_drop) {
-+              log_query_info(VERB_QUERY, "rpz drop", &m->s.qinfo);
-+              secure = 0;
-+      } else
-+#endif
-       /* send the reply */
-       /* We don't reuse the encoded answer if either the previous or current
-        * response has a local alias.  We could compare the alias records
-@@ -1255,6 +1265,7 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
-       key.s.is_valrec = valrec;
-       key.s.qinfo = *qinfo;
-       key.s.query_flags = qflags;
-+      key.reply_list = NULL;
-       /* We are searching for a similar mesh state when we DO want to
-        * aggregate the state. Thus unique is set to NULL. (default when we
-        * desire aggregation).*/
-@@ -1301,6 +1312,10 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
-       if(!r)
-               return 0;
-       r->query_reply = *rep;
-+#ifdef ENABLE_FASTRPZ
-+      /* The new reply structure owns the RPZ state. */
-+      rep->rpz = NULL;
-+#endif
-       r->edns = *edns;
-       if(edns->opt_list) {
-               r->edns.opt_list = edns_opt_copy_region(edns->opt_list,
-diff --git a/util/config_file.c b/util/config_file.c
-index 119b2223..ce43a234 100644
---- a/util/config_file.c
-+++ b/util/config_file.c
-@@ -1434,6 +1434,8 @@ config_delete(struct config_file* cfg)
-       free(cfg->dnstap_socket_path);
-       free(cfg->dnstap_identity);
-       free(cfg->dnstap_version);
-+      if (cfg->rpz_cstr)
-+              free(cfg->rpz_cstr);
-       config_deldblstrlist(cfg->ratelimit_for_domain);
-       config_deldblstrlist(cfg->ratelimit_below_domain);
-       config_delstrlist(cfg->python_script);
-diff --git a/util/config_file.h b/util/config_file.h
-index b3ef930a..56173b80 100644
---- a/util/config_file.h
-+++ b/util/config_file.h
-@@ -494,6 +494,11 @@ struct config_file {
-       /** true to disable DNSSEC lameness check in iterator */
-       int disable_dnssec_lame_check;
-+      /** true to enable RPZ */
-+      int rpz_enable;
-+      /** RPZ configuration */
-+      char* rpz_cstr;
-+
-       /** ratelimit for ip addresses. 0 is off, otherwise qps (unless overridden) */
-       int ip_ratelimit;
-       /** number of slabs for ip_ratelimit cache */
-diff --git a/util/configlexer.lex b/util/configlexer.lex
-index 7a972908..2d03ffc7 100644
---- a/util/configlexer.lex
-+++ b/util/configlexer.lex
-@@ -439,6 +439,10 @@ dnstap-log-forwarder-query-messages{COLON}        {
-               YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) }
- dnstap-log-forwarder-response-messages{COLON} {
-               YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) }
-+rpz{COLON}                    { YDVAR(0, VAR_RPZ) }
-+rpz-enable{COLON}             { YDVAR(1, VAR_RPZ_ENABLE) }
-+rpz-zone{COLON}                       { YDVAR(1, VAR_RPZ_ZONE) }
-+rpz-option{COLON}             { YDVAR(1, VAR_RPZ_OPTION) }
- disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) }
- ip-ratelimit{COLON}           { YDVAR(1, VAR_IP_RATELIMIT) }
- ratelimit{COLON}              { YDVAR(1, VAR_RATELIMIT) }
-diff --git a/util/configparser.y b/util/configparser.y
-index 10227a2f..a519fcc7 100644
---- a/util/configparser.y
-+++ b/util/configparser.y
-@@ -125,6 +125,7 @@ extern struct config_parser_state* cfg_parser;
- %token VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES
- %token VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES
- %token VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES
-+%token VAR_RPZ VAR_RPZ_ENABLE VAR_RPZ_ZONE VAR_RPZ_OPTION
- %token VAR_RESPONSE_IP_TAG VAR_RESPONSE_IP VAR_RESPONSE_IP_DATA
- %token VAR_HARDEN_ALGO_DOWNGRADE VAR_IP_TRANSPARENT
- %token VAR_DISABLE_DNSSEC_LAME_CHECK
-@@ -171,7 +172,7 @@ extern struct config_parser_state* cfg_parser;
- %%
- toplevelvars: /* empty */ | toplevelvars toplevelvar ;
--toplevelvar: serverstart contents_server | stubstart contents_stub |
-+toplevelvar: serverstart contents_server | stubstart contents_stub | rpzstart contents_rpz |
-       forwardstart contents_forward | pythonstart contents_py | 
-       rcstart contents_rc | dtstart contents_dt | viewstart contents_view |
-       dnscstart contents_dnsc | cachedbstart contents_cachedb |
-@@ -2726,6 +2727,50 @@ dt_dnstap_log_forwarder_response_messages: VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MES
-               free($2);
-       }
-       ;
-+rpzstart: VAR_RPZ
-+      {
-+              OUTYY(("\nP(rpz:)\n"));
-+      }
-+      ;
-+contents_rpz: contents_rpz content_rpz
-+      | ;
-+content_rpz: rpz_enable | rpz_zone | rpz_option
-+      ;
-+rpz_enable: VAR_RPZ_ENABLE STRING_ARG
-+      {
-+              OUTYY(("P(rpz_enable:%s)\n", $2));
-+              if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
-+                      yyerror("expected yes or no.");
-+              else cfg_parser->cfg->rpz_enable = (strcmp($2, "yes")==0);
-+              free($2);
-+      }
-+      ;
-+rpz_zone: VAR_RPZ_ZONE STRING_ARG
-+      {
-+              char *new_cstr, *old_cstr;
-+
-+              OUTYY(("P(rpz_zone:%s)\n", $2));
-+              old_cstr = cfg_parser->cfg->rpz_cstr;
-+              (void)asprintf(&new_cstr, "%s\nzone %s", old_cstr?old_cstr:"", $2);
-+              if(!new_cstr)
-+                      yyerror("out of memory");
-+              free(old_cstr);
-+              cfg_parser->cfg->rpz_cstr = new_cstr;
-+      }
-+      ;
-+rpz_option: VAR_RPZ_OPTION STRING_ARG
-+      {
-+              char *new_cstr, *old_cstr;
-+
-+              OUTYY(("P(rpz_option:%s)\n", $2));
-+              old_cstr = cfg_parser->cfg->rpz_cstr;
-+              (void)asprintf(&new_cstr, "%s\n%s", old_cstr ? old_cstr : "", $2);
-+              if(!new_cstr)
-+                      yyerror("out of memory");
-+              free(old_cstr);
-+              cfg_parser->cfg->rpz_cstr = new_cstr;
-+      }
-+      ;
- pythonstart: VAR_PYTHON
-       { 
-               OUTYY(("\nP(python:)\n")); 
-diff --git a/util/data/msgencode.c b/util/data/msgencode.c
-index a51a4b9b..475dfce9 100644
---- a/util/data/msgencode.c
-+++ b/util/data/msgencode.c
-@@ -590,6 +590,35 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
-       return RETVAL_OK;
- }
-+#ifdef ENABLE_FASTRPZ
-+/* Insert the RPZ SOA even with MINIMAL_RESPONSES */
-+static int
-+insert_rpz_soa(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
-+             sldns_buffer* pkt, size_t rrsets_before, time_t timenow,
-+             struct regional* region, struct compress_tree_node** tree,
-+             size_t rr_offset)
-+{
-+      int r;
-+      size_t i, setstart;
-+
-+      *num_rrs = 0;
-+      for(i=0; i<num_rrsets; i++) {
-+              if (rep->rrsets[rrsets_before+i]->rk.type != LDNS_RR_TYPE_SOA)
-+                      continue;
-+              setstart = sldns_buffer_position(pkt);
-+              if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
-+                                        pkt, num_rrs, timenow, region,
-+                                        1, 0, tree, LDNS_SECTION_ADDITIONAL,
-+                                        LDNS_RR_TYPE_ANY, 0, rr_offset))
-+                 != RETVAL_OK) {
-+                      sldns_buffer_set_position(pkt, setstart);
-+                      return r;
-+              }
-+      }
-+      return RETVAL_OK;
-+}
-+
-+#endif
- /** store query section in wireformat buffer, return RETVAL */
- static int
- insert_query(struct query_info* qinfo, struct compress_tree_node** tree, 
-@@ -777,6 +806,19 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
-                       }
-                       sldns_buffer_write_u16_at(buffer, 10, arcount);
-               }
-+#ifdef ENABLE_FASTRPZ
-+      } else if(rep->security == sec_status_rpz_rewritten) {
-+              /* Insert the RPZ SOA for rpz even with MINIMAL_RESPONSES */
-+              r = insert_rpz_soa(rep, rep->ar_numrrsets, &arcount, buffer,
-+                                 rep->an_numrrsets + rep->ns_numrrsets,
-+                                 timenow, region, &tree, rr_offset);
-+              if(r!= RETVAL_OK) {
-+                      if(r != RETVAL_TRUNC)
-+                              return 0;
-+                      /* no need to set TC bit, this is the additional */
-+                      sldns_buffer_write_u16_at(buffer, 10, arcount);
-+              }
-+#endif
-       }
-       sldns_buffer_flip(buffer);
-       return 1;
-diff --git a/util/data/packed_rrset.c b/util/data/packed_rrset.c
-index 7b9d5494..e44b2ce5 100644
---- a/util/data/packed_rrset.c
-+++ b/util/data/packed_rrset.c
-@@ -255,6 +255,10 @@ sec_status_to_string(enum sec_status s)
-       case sec_status_insecure:       return "sec_status_insecure";
-       case sec_status_secure_sentinel_fail:   return "sec_status_secure_sentinel_fail";
-       case sec_status_secure:         return "sec_status_secure";
-+#ifdef ENABLE_FASTRPZ
-+      case sec_status_rpz_rewritten:  return "sec_status_rpz_rewritten";
-+      case sec_status_rpz_drop:       return "sec_status_rpz_drop";
-+#endif
-       }
-       return "unknown_sec_status_value";
- }
-diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h
-index 3a5335dd..20113217 100644
---- a/util/data/packed_rrset.h
-+++ b/util/data/packed_rrset.h
-@@ -193,7 +193,15 @@ enum sec_status {
-       sec_status_secure_sentinel_fail,
-       /** SECURE means that the object (RRset or message) validated 
-        * according to local policy. */
--      sec_status_secure
-+      sec_status_secure,
-+#ifdef ENABLE_FASTRPZ
-+      /** RPZ_REWRITTEN means that the response has been rewritten by
-+       * rpz and so cannot be verified. */
-+      sec_status_rpz_rewritten,
-+      /** RPZ_DROP means that the response has been rewritten by rpz
-+       * as silence. */
-+      sec_status_rpz_drop
-+#endif
- };
- /**
-diff --git a/util/netevent.c b/util/netevent.c
-index 9e2ba92b..06ede4e6 100644
---- a/util/netevent.c
-+++ b/util/netevent.c
-@@ -57,6 +57,9 @@
- #ifdef HAVE_OPENSSL_ERR_H
- #include <openssl/err.h>
- #endif
-+#ifdef ENABLE_FASTRPZ
-+#include "fastrpz/rpz.h"
-+#endif
- /* -------- Start of local definitions -------- */
- /** if CMSG_ALIGN is not defined on this platform, a workaround */
-@@ -590,6 +593,9 @@ comm_point_udp_ancil_callback(int fd, short event, void* arg)
-       struct cmsghdr* cmsg;
- #endif /* S_SPLINT_S */
-+#ifdef ENABLE_FASTRPZ
-+      rep.rpz = NULL;
-+#endif
-       rep.c = (struct comm_point*)arg;
-       log_assert(rep.c->type == comm_udp);
-@@ -679,6 +685,9 @@ comm_point_udp_callback(int fd, short event, void* arg)
-       int i;
-       struct sldns_buffer *buffer;
-+#ifdef ENABLE_FASTRPZ
-+      rep.rpz = NULL;
-+#endif
-       rep.c = (struct comm_point*)arg;
-       log_assert(rep.c->type == comm_udp);
-@@ -722,6 +731,9 @@ comm_point_udp_callback(int fd, short event, void* arg)
-                       (void)comm_point_send_udp_msg(rep.c, buffer,
-                               (struct sockaddr*)&rep.addr, rep.addrlen);
-               }
-+#ifdef ENABLE_FASTRPZ
-+              rpz_end(&rep);
-+#endif
-               if(!rep.c || rep.c->fd != fd) /* commpoint closed to -1 or reused for
-               another UDP port. Note rep.c cannot be reused with TCP fd. */
-                       break;
-@@ -3152,6 +3164,9 @@ comm_point_send_reply(struct comm_reply *repinfo)
-                               repinfo->c->tcp_timeout_msec);
-               }
-       }
-+#ifdef ENABLE_FASTRPZ
-+      rpz_end(repinfo);
-+#endif
- }
- void 
-@@ -3161,6 +3176,9 @@ comm_point_drop_reply(struct comm_reply* repinfo)
-               return;
-       log_assert(repinfo && repinfo->c);
-       log_assert(repinfo->c->type != comm_tcp_accept);
-+#ifdef ENABLE_FASTRPZ
-+      rpz_end(repinfo);
-+#endif
-       if(repinfo->c->type == comm_udp)
-               return;
-       if(repinfo->c->tcp_req_info)
-@@ -3182,6 +3200,9 @@ comm_point_start_listening(struct comm_point* c, int newfd, int msec)
- {
-       verbose(VERB_ALGO, "comm point start listening %d (%d msec)", 
-               c->fd==-1?newfd:c->fd, msec);
-+#ifdef ENABLE_FASTRPZ
-+      rpz_end(&c->repinfo);
-+#endif
-       if(c->type == comm_tcp_accept && !c->tcp_free) {
-               /* no use to start listening no free slots. */
-               return;
-diff --git a/util/netevent.h b/util/netevent.h
-index d80c72b3..0233292f 100644
---- a/util/netevent.h
-+++ b/util/netevent.h
-@@ -120,6 +120,10 @@ struct comm_reply {
-       /** return type 0 (none), 4(IP4), 6(IP6) */
-       int srctype;
-       /* DnsCrypt context */
-+#ifdef ENABLE_FASTRPZ
-+      /** per-request RPZ state */
-+      struct commreply_rpz* rpz;
-+#endif
- #ifdef USE_DNSCRYPT
-       uint8_t client_nonce[crypto_box_HALF_NONCEBYTES];
-       uint8_t nmkey[crypto_box_BEFORENMBYTES];
-diff --git a/validator/validator.c b/validator/validator.c
-index fa8d5419..5628ef0b 100644
---- a/validator/validator.c
-+++ b/validator/validator.c
-@@ -2755,6 +2755,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
-                       default:
-                               /* NSEC proof did not work, try next */
-                               break;
-+#ifdef ENABLE_FASTRPZ
-+                      case sec_status_rpz_rewritten:
-+                      case sec_status_rpz_drop:
-+                              fatal_exit("impossible RPZ sec_status");
-+                              break;
-+#endif
-               }
-               sec = nsec3_prove_nods(qstate->env, ve, 
-@@ -2788,6 +2794,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
-                       default:
-                               /* NSEC3 proof did not work */
-                               break;
-+#ifdef ENABLE_FASTRPZ
-+                      case sec_status_rpz_rewritten:
-+                      case sec_status_rpz_drop:
-+                              fatal_exit("impossible RPZ sec_status");
-+                              break;
-+#endif
-               }
-               /* Apparently, no available NSEC/NSEC3 proved NODATA, so