1Description: based on the included patch contrib/fastrpz.patch
2Author: fastrpz@farsightsecurity.com
3---
4This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
5===================================================================
6RCS file: ./RCS/Makefile.in,v
7retrieving revision 1.1
8Index: unbound-1.7.0~rc1/Makefile.in
9===================================================================
10--- unbound-1.7.0~rc1.orig/Makefile.in
11+++ unbound-1.7.0~rc1/Makefile.in
12@@ -23,6 +23,8 @@ CHECKLOCK_SRC=testcode/checklocks.c
13 CHECKLOCK_OBJ=@CHECKLOCK_OBJ@
14 DNSTAP_SRC=@DNSTAP_SRC@
15 DNSTAP_OBJ=@DNSTAP_OBJ@
16+FASTRPZ_SRC=@FASTRPZ_SRC@
17+FASTRPZ_OBJ=@FASTRPZ_OBJ@
18 DNSCRYPT_SRC=@DNSCRYPT_SRC@
19 DNSCRYPT_OBJ=@DNSCRYPT_OBJ@
20 WITH_PYTHONMODULE=@WITH_PYTHONMODULE@
21@@ -125,7 +127,7 @@ validator/val_sigcrypt.c validator/val_u
22 edns-subnet/edns-subnet.c edns-subnet/subnetmod.c \
23 edns-subnet/addrtree.c edns-subnet/subnet-whitelist.c \
24 cachedb/cachedb.c cachedb/redis.c respip/respip.c $(CHECKLOCK_SRC) \
25-$(DNSTAP_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC)
26+$(DNSTAP_SRC) $(FASTRPZ_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC)
27 COMMON_OBJ_WITHOUT_NETCALL=dns.lo infra.lo rrset.lo dname.lo msgencode.lo \
28 as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
29 iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \
30@@ -137,7 +139,7 @@ slabhash.lo timehist.lo tube.lo winsock_
31 validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \
32 val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo authzone.lo\
33 $(SUBNET_OBJ) $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ) \
34-$(IPSECMOD_OBJ) respip.lo
35+$(FASTRPZ_OBJ) $(IPSECMOD_OBJ) respip.lo
36 COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \
37 outside_network.lo
38 COMMON_OBJ=$(COMMON_OBJ_WITHOUT_UB_EVENT) ub_event.lo
39@@ -400,6 +402,11 @@ dnscrypt.lo dnscrypt.o: $(srcdir)/dnscry
40 	$(srcdir)/util/config_file.h $(srcdir)/util/log.h \
41 	$(srcdir)/util/netevent.h
42
43+# fastrpz
44+rpz.lo rpz.o: $(srcdir)/fastrpz/rpz.c config.h fastrpz/rpz.h fastrpz/librpz.h \
45+	$(srcdir)/util/config_file.h $(srcdir)/daemon/daemon.h \
46+	$(srcdir)/util/log.h
47+
48 # Python Module
49 pythonmod.lo pythonmod.o: $(srcdir)/pythonmod/pythonmod.c config.h \
50 	pythonmod/interface.h \
51Index: unbound-1.7.0~rc1/config.h.in
52===================================================================
53--- unbound-1.7.0~rc1.orig/config.h.in
54+++ unbound-1.7.0~rc1/config.h.in
55@@ -1228,4 +1228,11 @@ void *unbound_stat_realloc_log(void *ptr
56 /** the version of unbound-control that this software implements */
57 #define UNBOUND_CONTROL_VERSION 1
58
59-
60+/* have __attribute__s used in librpz.h */
61+#undef LIBRPZ_HAVE_ATTR
62+/** fastrpz librpz.so */
63+#undef FASTRPZ_LIBRPZ_PATH
64+/** 0=no fastrpz  1=static link  2=dlopen() */
65+#undef FASTRPZ_LIB_OPEN
66+/** turn on fastrpz response policy zones */
67+#undef ENABLE_FASTRPZ
68Index: unbound-1.7.0~rc1/configure.ac
69===================================================================
70--- unbound-1.7.0~rc1.orig/configure.ac
71+++ unbound-1.7.0~rc1/configure.ac
72@@ -6,6 +6,7 @@ sinclude(ax_pthread.m4)
73 sinclude(acx_python.m4)
74 sinclude(ac_pkg_swig.m4)
75 sinclude(dnstap/dnstap.m4)
76+sinclude(fastrpz/rpz.m4)
77 sinclude(dnscrypt/dnscrypt.m4)
78
79 # must be numbers. ac_defun because of later processing
80@@ -1453,6 +1454,9 @@ case "$enable_ipsecmod" in
81 		;;
82 esac
83
84+# check for Fastrpz with fastrpz/rpz.m4
85+ck_FASTRPZ
86+
87 AC_MSG_CHECKING([if ${MAKE:-make} supports $< with implicit rule in scope])
88 # on openBSD, the implicit rule make $< work.
89 # on Solaris, it does not work ($? is changed sources, $^ lists dependencies).
90Index: unbound-1.7.0~rc1/daemon/daemon.c
91===================================================================
92--- unbound-1.7.0~rc1.orig/daemon/daemon.c
93+++ unbound-1.7.0~rc1/daemon/daemon.c
94@@ -90,6 +90,9 @@
95 #include "sldns/keyraw.h"
96 #include "respip/respip.h"
97 #include <signal.h>
98+#ifdef ENABLE_FASTRPZ
99+#include "fastrpz/rpz.h"
100+#endif
101
102 #ifdef HAVE_SYSTEMD
103 #include <systemd/sd-daemon.h>
104@@ -461,6 +464,14 @@ daemon_create_workers(struct daemon* dae
105 		fatal_exit("dnstap enabled in config but not built with dnstap support");
106 #endif
107 	}
108+	if(daemon->cfg->rpz_enable) {
109+#ifdef ENABLE_FASTRPZ
110+		rpz_init(&daemon->rpz_clist, &daemon->rpz_client, daemon->cfg);
111+#else
112+		fatal_exit("fastrpz enabled in config"
113+			   " but not built with fastrpz");
114+#endif
115+	}
116 	for(i=0; i<daemon->num; i++) {
117 		if(!(daemon->workers[i] = worker_create(daemon, i,
118 			shufport+numport*i/daemon->num,
119@@ -710,6 +721,9 @@ daemon_cleanup(struct daemon* daemon)
120 #ifdef USE_DNSCRYPT
121 	dnsc_delete(daemon->dnscenv);
122 #endif
123+#ifdef ENABLE_FASTRPZ
124+	rpz_delete(&daemon->rpz_clist, &daemon->rpz_client);
125+#endif
126 	daemon->cfg = NULL;
127 }
128
129Index: unbound-1.7.0~rc1/daemon/daemon.h
130===================================================================
131--- unbound-1.7.0~rc1.orig/daemon/daemon.h
132+++ unbound-1.7.0~rc1/daemon/daemon.h
133@@ -134,6 +134,11 @@ struct daemon {
134 	/** the dnscrypt environment */
135 	struct dnsc_env* dnscenv;
136 #endif
137+#ifdef ENABLE_FASTRPZ
138+	/** global opaque rpz handles */
139+	struct librpz_clist *rpz_clist;
140+	struct librpz_client *rpz_client;
141+#endif
142 };
143
144 /**
145Index: unbound-1.7.0~rc1/daemon/worker.c
146===================================================================
147--- unbound-1.7.0~rc1.orig/daemon/worker.c
148+++ unbound-1.7.0~rc1/daemon/worker.c
149@@ -74,6 +74,9 @@
150 #include "libunbound/context.h"
151 #include "libunbound/libworker.h"
152 #include "sldns/sbuffer.h"
153+#ifdef ENABLE_FASTRPZ
154+#include "fastrpz/rpz.h"
155+#endif
156 #include "sldns/wire2str.h"
157 #include "util/shm_side/shm_main.h"
158 #include "dnscrypt/dnscrypt.h"
159@@ -527,8 +530,27 @@ answer_norec_from_cache(struct worker* w
160 			/* not secure */
161 			secure = 0;
162 			break;
163+#ifdef ENABLE_FASTRPZ
164+		case sec_status_rpz_rewritten:
165+		case sec_status_rpz_drop:
166+			fatal_exit("impossible cached RPZ sec_status");
167+			break;
168+#endif
169 		}
170 	}
171+#ifdef ENABLE_FASTRPZ
172+	if(repinfo->rpz) {
173+		/* Scan the cached answer for RPZ hits.
174+		 * ret=1 use cache entry
175+		 * ret=-1 rewritten response already sent or dropped
176+		 * ret=0 deny a cached entry exists
177+		 */
178+		int ret = rpz_worker_cache(worker, msg->rep, qinfo,
179+					   id, flags, edns, repinfo);
180+		if(ret != 1)
181+			return ret;
182+	}
183+#endif
184 	/* return this delegation from the cache */
185 	edns->edns_version = EDNS_ADVERTISED_VERSION;
186 	edns->udp_size = EDNS_ADVERTISED_SIZE;
187@@ -689,6 +711,23 @@ answer_from_cache(struct worker* worker,
188 			secure = 0;
189 		}
190 	} else	secure = 0;
191+#ifdef ENABLE_FASTRPZ
192+	if(repinfo->rpz) {
193+		/* Scan the cached answer for RPZ hits.
194+		 * ret=1 use cache entry
195+		 * ret=-1 rewritten response already sent or dropped
196+		 * ret=0 deny a cached entry exists
197+		 */
198+		int ret = rpz_worker_cache(worker, rep, qinfo, id, flags, edns,
199+					   repinfo);
200+		if(ret != 1) {
201+			rrset_array_unlock_touch(worker->env.rrset_cache,
202+						 worker->scratchpad, rep->ref,
203+						 rep->rrset_count);
204+			return ret;
205+		}
206+        }
207+#endif
208
209 	edns->edns_version = EDNS_ADVERTISED_VERSION;
210 	edns->udp_size = EDNS_ADVERTISED_SIZE;
211@@ -1291,6 +1330,15 @@ worker_handle_request(struct comm_point*
212 		log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
213 			&repinfo->addr, repinfo->addrlen);
214 		goto send_reply;
215+#ifdef ENABLE_FASTRPZ
216+	} else {
217+		/* Start to rewrite for response policy zones.
218+		 * This can hit a qname trigger and be done. */
219+		if(rpz_start(worker, &qinfo, repinfo, &edns)) {
220+			regional_free_all(worker->scratchpad);
221+			return 0;
222+		}
223+#endif
224 	}
225
226 	/* If we've found a local alias, replace the qname with the alias
227@@ -1339,12 +1387,21 @@ lookup_cache:
228 		h = query_info_hash(lookup_qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
229 		if((e=slabhash_lookup(worker->env.msg_cache, h, lookup_qinfo, 0))) {
230 			/* answer from cache - we have acquired a readlock on it */
231-			if(answer_from_cache(worker, &qinfo,
232+			ret = answer_from_cache(worker, &qinfo,
233 				cinfo, &need_drop, &alias_rrset, &partial_rep,
234 				(struct reply_info*)e->data,
235 				*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
236 				sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
237-				&edns)) {
238+				&edns);
239+#ifdef ENABLE_FASTRPZ
240+			if(ret < 0) {
241+				/* RPZ already dropped or sent a response. */
242+				lock_rw_unlock(&e->lock);
243+				regional_free_all(worker->scratchpad);
244+				return 0;
245+			}
246+#endif
247+			if(ret) {
248 				/* prefetch it if the prefetch TTL expired.
249 				 * Note that if there is more than one pass
250 				 * its qname must be that used for cache
251@@ -1398,11 +1455,19 @@ lookup_cache:
252 			lock_rw_unlock(&e->lock);
253 		}
254 		if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
255-			if(answer_norec_from_cache(worker, &qinfo,
256+			ret = answer_norec_from_cache(worker, &qinfo,
257 				*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
258 				sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
259-				&edns)) {
260+				&edns);
261+			if(ret) {
262 				regional_free_all(worker->scratchpad);
263+#ifdef ENABLE_FASTRPZ
264+				if(ret < 0) {
265+					/* RPZ already dropped
266+					 * or sent a response. */
267+					return 0;
268+				}
269+#endif
270 				goto send_reply;
271 			}
272 			verbose(VERB_ALGO, "answer norec from cache -- "
273Index: unbound-1.7.0~rc1/doc/unbound.conf.5.in
274===================================================================
275--- unbound-1.7.0~rc1.orig/doc/unbound.conf.5.in
276+++ unbound-1.7.0~rc1/doc/unbound.conf.5.in
277@@ -1581,6 +1581,81 @@ It must be /96 or shorter.  The default
278 .B dns64\-synthall: \fI<yes or no>\fR
279 Debug option, default no.  If enabled, synthesize all AAAA records
280 despite the presence of actual AAAA records.
281+.SS "Response Policy Zone Rewriting"
282+.LP
283+Response policy zone rewriting is controlled with the
284+.B rpz
285+clause.
286+It must contain a
287+.B rpz\-enable:
288+option, and one or more
289+.B rpz\-zone:
290+options.
291+It will usually also contain
292+.B rpz\-option:
293+clauses with general rewriting options or specifying dnsrpzd parameters.
294+Beneath the surface, the text in
295+.B rpz\-zone: \fI<"domain">\fR
296+is converted to \fI"zone domain\\n"\fR and added to the configuration string
297+given to
298+\fIlibrpz\fR(3).
299+The text in
300+.B rpz-option \fI<"text">\fR
301+is also added to that configuration string.
302+.LP
303+If using chroot, then the chroot directory must contain the \fIdnsrpzd\fR(3)
304+command and the shared libraries that it uses.
305+Those can be found with the \fIldd\fR(1) command.
306+.LP
307+Resolver zone and rewriting options and response policy zone triggers and
308+actions are described in \fIlibrpz\fR(3).
309+The separate control file that specifies the policy zones maintained by
310+the dnsrpzd daemon is described in \fIdnsrpzd\fR(8).
311+.LP
312+Many installations need a local whitelist that exempts local
313+domains from rewriting.
314+Whitelist records can be in zones transferred by dnsrpzd from
315+authorities or in a local zone file.
316+.TP
317+.B rpz-enable: \fI<yes or no>
318+enables Fastrpz.
319+If not enabled, the other options in the
320+.B rpz:
321+clause are ignored.
322+.TP
323+.B rpz-zone: \fI<"zone and options">
324+specifies a policy zone and optional per-zone rewriting parameters.
325+.TP
326+.B rpz-option: \fI<"option">
327+specifies general Fastrpz options.
328+.LP
329+Fastrpz is available only on POSIX compliant UNIX-like systems with the
330+\fImmap\fR(2) system call.
331+.LP
332+Fastrpz in Unbound differs from rpz and fastrpz in BIND by
333+.RS 3
334+.HP 4
335+RPZ-CLIENT-IP triggers can only be used in the first policy zone
336+specified with
337+.B rpz-zone:
338+.HP
339+Policy zone rewriting is disabled by the DO bit in DNS requests
340+even when no DNSSEC signatures are supplied by authorities.
341+.HP
342+Unbound local zones are not subject to rpz rewriting.
343+.HP
344+Like Fastrpz with BIND but unlike classic BIND rpz,
345+the ADDITIONAL sections of rewritten responses contain the SOA record from
346+the policy zone used to rewrite the response.
347+.RE
348+.P
349+.nf
350+# example Fastrpz settings for use with chroot on Freebsd
351+rpz:
352+    rpz-zone: "rpz.example.org"
353+    rpz-zone: "other.rpz.example.org ip-as-ns yes"
354+    rpz-option: "dnsrpzd ./dnsrpzd"
355+.fi
356 .SS "DNSCrypt Options"
357 .LP
358 The
359Index: unbound-1.7.0~rc1/fastrpz/librpz.h
360===================================================================
361--- /dev/null
362+++ unbound-1.7.0~rc1/fastrpz/librpz.h
363@@ -0,0 +1,957 @@
364+/*
365+ * Define the interface from a DNS resolver to the Response Policy Zone
366+ * library, librpz.
367+ *
368+ * This file should be included only the interface functions between the
369+ * resolver and librpz to avoid name space pollution.
370+ *
371+ * Copyright (c) 2016-2017 Farsight Security, Inc.
372+ *
373+ * Licensed under the Apache License, Version 2.0 (the "License");
374+ * you may not use this file except in compliance with the License.
375+ * You may obtain a copy of the License at
376+ *	http://www.apache.org/licenses/LICENSE-2.0
377+ *
378+ * Unless required by applicable law or agreed to in writing, software
379+ * distributed under the License is distributed on an "AS IS" BASIS,
380+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
381+ * See the License for the specific language governing permissions and
382+ * limitations under the License.
383+ *
384+ * Fastrpz version 1.2.10
385+ */
386+
387+#ifndef LIBRPZ_H
388+#define LIBRPZ_H
389+
390+#include <arpa/nameser.h>
391+#include <netinet/in.h>
392+#include <stdarg.h>
393+#include <stdbool.h>
394+#include <stdio.h>
395+#include <sys/types.h>
396+
397+
398+/*
399+ * Allow either ordinary or dlopen() linking.
400+ */
401+#ifdef LIBRPZ_INTERNAL
402+#define LIBDEF(t,s) extern t s;
403+#define LIBDEF_F(f) LIBDEF(librpz_##f##_t, librpz_##f)
404+#else
405+#define LIBDEF(t,s)
406+#define LIBDEF_F(f)
407+#endif
408+
409+/*
410+ * Response Policy Zone triggers.
411+ *	Comparisons of trigger precedences require
412+ *	LIBRPZ_TRIG_CLIENT_IP < LIBRPZ_TRIG_QNAME < LIBRPZ_TRIG_IP
413+ *	    < LIBRPZ_TRIG_NSDNAME < LIBRPZ_TRIG_NSIP}
414+ */
415+typedef enum {
416+	LIBRPZ_TRIG_BAD		=0,
417+	LIBRPZ_TRIG_CLIENT_IP	=1,
418+	LIBRPZ_TRIG_QNAME	=2,
419+	LIBRPZ_TRIG_IP		=3,
420+	LIBRPZ_TRIG_NSDNAME	=4,
421+	LIBRPZ_TRIG_NSIP	=5
422+} librpz_trig_t;
423+#define LIBRPZ_TRIG_SIZE	3	/* sizeof librpz_trig_t in bits */
424+typedef uint8_t		librpz_tbit_t;  /* one bit for each of the TRIGS_NUM
425+					 * trigger types */
426+
427+
428+/*
429+ * Response Policy Zone Actions or policies
430+ */
431+typedef enum {
432+	LIBRPZ_POLICY_UNDEFINED	=0,	/* an empty entry or no decision yet */
433+	LIBRPZ_POLICY_DELETED	=1,	/* placeholder for a deleted policy */
434+
435+	LIBRPZ_POLICY_PASSTHRU	=2,	/* 'passthru': do not rewrite */
436+	LIBRPZ_POLICY_DROP	=3,	/* 'drop': do not respond */
437+	LIBRPZ_POLICY_TCP_ONLY	=4,	/* 'tcp-only': answer UDP with TC=1 */
438+	LIBRPZ_POLICY_NXDOMAIN	=5,	/* 'nxdomain': answer with NXDOMAIN */
439+	LIBRPZ_POLICY_NODATA	=6,	/* 'nodata': answer with ANCOUNT=0 */
440+	LIBRPZ_POLICY_RECORD	=7,	/* rewrite with the policy's RR */
441+
442+	/* only in client configurations to override the zone */
443+	LIBRPZ_POLICY_GIVEN,		/* 'given': what policy record says */
444+	LIBRPZ_POLICY_DISABLED,		/* at most log */
445+	LIBRPZ_POLICY_CNAME,		/* answer with 'cname x' */
446+} librpz_policy_t;
447+#define LIBRPZ_POLICY_BITS	4
448+
449+/*
450+ * Special policies that appear as targets of CNAMEs
451+ * NXDOMAIN is signaled by a CNAME with a "." target.
452+ * NODATA is signaled by a CNAME with a "*." target.
453+ */
454+#define LIBRPZ_RPZ_PREFIX	"rpz-"
455+#define LIBRPZ_RPZ_PASSTHRU	LIBRPZ_RPZ_PREFIX"passthru"
456+#define LIBRPZ_RPZ_DROP		LIBRPZ_RPZ_PREFIX"drop"
457+#define LIBRPZ_RPZ_TCP_ONLY	LIBRPZ_RPZ_PREFIX"tcp-only"
458+
459+
460+typedef	uint16_t    librpz_dznum_t;	/* dnsrpzd zone # in [0,DZNUM_MAX] */
461+typedef	uint8_t	    librpz_cznum_t;	/* client zone # in [0,CZNUM_MAX] */
462+
463+
464+/*
465+ * CIDR block
466+ */
467+typedef struct librpz_prefix {
468+	union {
469+		struct in_addr	in;
470+		struct in6_addr	in6;
471+	} addr;
472+	uint8_t		    family;
473+	uint8_t		    len;
474+} librpz_prefix_t;
475+
476+/*
477+ * A domain
478+ */
479+typedef uint8_t	librpz_dsize_t;
480+typedef struct librpz_domain {
481+	librpz_dsize_t	    size;	/* of only .d */
482+	uint8_t		    d[0];	/* variable length wire format */
483+} librpz_domain_t;
484+
485+/*
486+ * A maximal domain buffer
487+ */
488+typedef struct librpz_domain_buf {
489+	librpz_dsize_t	    size;
490+	uint8_t		    d[NS_MAXCDNAME];
491+} librpz_domain_buf_t;
492+
493+/*
494+ * A resource record without the owner name.
495+ * C compilers say that sizeof(librpz_rr_t)=12 instead of 10.
496+ */
497+typedef struct {
498+	uint16_t	    type;	/* network byte order */
499+	uint16_t	    class;	/* network byte order */
500+	uint32_t	    ttl;	/* network byte order */
501+	uint16_t	    rdlength;	/* network byte order */
502+	uint8_t		    rdata[0];	/* variable length */
503+} librpz_rr_t;
504+
505+/*
506+ * The database file might be mapped with different starting addresses
507+ * by concurrent clients (resolvers), and so all pointers are offsets.
508+ */
509+typedef uint32_t	librpz_idx_t;
510+#define LIBRPZ_IDX_NULL	0
511+#define LIBRPZ_IDX_MIN	1
512+#define LIBRPZ_IDX_BAD  ((librpz_idx_t)-1)
513+/**
514+ * Partial decoded results of a set of RPZ queries for a single DNS response
515+ * or interation through the mapped file.
516+ */
517+typedef int16_t librpz_result_id_t;
518+typedef struct librpz_result {
519+	librpz_idx_t	    next_rr;
520+	librpz_result_id_t  hit_id;		/* trigger ID from resolver */
521+	librpz_policy_t	    zpolicy;	/* policy from zone */
522+	librpz_policy_t	    policy;	/* adjusted by client configuration */
523+	librpz_dznum_t	    dznum;	/* dnsrpzd zone number */
524+	librpz_cznum_t	    cznum;	/* librpz client zone number */
525+	librpz_trig_t	    trig:LIBRPZ_TRIG_SIZE;
526+	bool		    log:1;	/* log rewrite given librpz_log_level */
527+} librpz_result_t;
528+
529+
530+/**
531+ * librpz trace or log levels.
532+ */
533+typedef enum {
534+	LIBRPZ_LOG_FATAL    =0,		/* always print fatal errors */
535+	LIBRPZ_LOG_ERROR    =1,		/* errors have this level */
536+	LIBRPZ_LOG_TRACE1   =2,		/* big events such as dnsrpzd starts */
537+	LIBRPZ_LOG_TRACE2   =3,		/* smaller dnsrpzd zone transfers */
538+	LIBRPZ_LOG_TRACE3   =4,		/* librpz hits */
539+	LIBRPZ_LOG_TRACE4   =5,		/* librpz lookups */
540+	LIBRPZ_LOG_INVALID   =999,
541+} librpz_log_level_t;
542+typedef librpz_log_level_t (librpz_log_level_val_t)(librpz_log_level_t level);
543+LIBDEF_F(log_level_val)
544+
545+/**
546+ * Logging function that can be supplied by the resolver.
547+ * @param level is one of librpz_log_level_t
548+ * @param ctx is for use by the resolver's logging system.
549+ *	NULL mean a context-free message.
550+ */
551+typedef void(librpz_log_fnc_t)(librpz_log_level_t level, void *ctx,
552+			       const char *buf);
553+
554+/**
555+ * Point librpz logging functions to the resolver's choice.
556+ */
557+typedef void (librpz_set_log_t)(librpz_log_fnc_t *new_log, const char *prog_nm);
558+LIBDEF_F(set_log)
559+
560+
561+/**
562+ * librpz error messages are put in these buffers.
563+ * Use a structure intead of naked char* to let the compiler check the length.
564+ * A function defined with "foo(char buf[120])" can be called with
565+ * "char sbuf[2]; foo(sbuf)" and suffer a buffer overrun.
566+ */
567+typedef struct {
568+	char	c[120];
569+} librpz_emsg_t;
570+
571+
572+#ifdef LIBRPZ_HAVE_ATTR
573+#define LIBRPZ_UNUSED	__attribute__((unused))
574+#define LIBRPZ_PF(f,l)	__attribute__((format(printf,f,l)))
575+#define	LIBRPZ_NORET	__attribute__((__noreturn__))
576+#else
577+#define LIBRPZ_UNUSED
578+#define LIBRPZ_PF(f,l)
579+#define	LIBRPZ_NORET
580+#endif
581+
582+#ifdef HAVE_BUILTIN_EXPECT
583+#define LIBRPZ_LIKELY(c) __builtin_expect(!!(c), 1)
584+#define LIBRPZ_UNLIKELY(c) __builtin_expect(!!(c), 0)
585+#else
586+#define LIBRPZ_LIKELY(c) (c)
587+#define LIBRPZ_UNLIKELY(c) (c)
588+#endif
589+
590+typedef bool (librpz_parse_log_opt_t)(librpz_emsg_t *emsg, const char *arg);
591+LIBDEF_F(parse_log_opt)
592+
593+typedef void (librpz_vpemsg_t)(librpz_emsg_t *emsg,
594+			       const char *p, va_list args);
595+LIBDEF_F(vpemsg)
596+typedef void (librpz_pemsg_t)(librpz_emsg_t *emsg,
597+			      const char *p, ...) LIBRPZ_PF(2,3);
598+LIBDEF_F(pemsg)
599+
600+typedef void (librpz_vlog_t)(librpz_log_level_t level, void *ctx,
601+			     const char *p, va_list args);
602+LIBDEF_F(vlog)
603+typedef void (librpz_log_t)(librpz_log_level_t level, void *ctx,
604+			    const char *p, ...) LIBRPZ_PF(3,4);
605+LIBDEF_F(log)
606+
607+typedef void (librpz_fatal_t)(int ex_code,
608+			      const char *p, ...) LIBRPZ_PF(2,3);
609+extern void librpz_fatal(int ex_code,
610+			 const char *p, ...) LIBRPZ_PF(2,3) LIBRPZ_NORET;
611+
612+typedef void (librpz_rpz_assert_t)(const char *file, unsigned line,
613+				   const char *p, ...) LIBRPZ_PF(3,4);
614+extern void librpz_rpz_assert(const char *file, unsigned line,
615+			      const char *p, ...) LIBRPZ_PF(3,4) LIBRPZ_NORET;
616+
617+typedef void (librpz_rpz_vassert_t)(const char *file, uint line,
618+				    const char *p, va_list args);
619+extern void librpz_rpz_vassert(const char *file, uint line,
620+			       const char *p, va_list args) LIBRPZ_NORET;
621+
622+
623+/*
624+ * As far as clients are concerned, all relative pointers or indexes in a
625+ * version of the mapped file except trie node parent pointers remain valid
626+ * forever.  A client must release a version so that it can be garbage
627+ * collected by the file system.  When dnsrpzd needs to expand the file,
628+ * it copies the old file to a new, larger file.  Clients can continue
629+ * using the old file.
630+ *
631+ * Versions can also appear in a single file.  Old nodes and trie values
632+ * within the file are not destroyed until all clients using the version
633+ * that contained the old values release the version.
634+ *
635+ * A client is marked as using version by connecting to the deamon.  It is
636+ * marked as using all subsequent versions.  A client releases all versions
637+ * by closing the connection or a range of versions by updating is slot
638+ * in the shared memory version table.
639+ *
640+ * As far as clients are concerned, there are the following possible librpz
641+ * failures:
642+ *	- malloc() or other fatal internal librpz problems indicated by
643+ *	    a failing return from a librpz function
644+ *	    All operations will fail until client handle is destroyed and
645+ *	    recreated with librpz_client_detach() and librpz_client_create().
646+ *	- corrupt database detected by librpz code, corrupt database detected
647+ *	    by dnsrpzd, or disconnection from the daemon.
648+ *	    Current operations will fail.
649+ *
650+ * Clients assume that the file has already been unlinked before
651+ *	the corrupt flag is set so that they do not race with the server
652+ *	over the corruption of a single file.  A client that finds the
653+ *	corrupt set knows that dnsrpzd has already crashed with
654+ *	abort() and is restarting.  The client can re-connect to dnsrpzd
655+ *	and retransmit its configuration, backing off as usual if anything
656+ *	goes wrong.
657+ *
658+ * Searchs of the database by a client do not need locks against dnsrpzd or
659+ *	other clients, but a lock is used to protect changes to the connection
660+ *	by competing threads in the client.  The client provides fuctions
661+ *	to serialize the conncurrent use of any single client handle.
662+ *	Functions that do nothing are appropriate for applications that are
663+ *	not "threaded" or that do not share client handles among threads.
664+ *	Otherwise, functions must be provided to librpz_clientcreate().
665+ *	Something like the following works with pthreads:
666+ *
667+ * static void
668+ * lock(void *mutex) { assert(pthread_mutex_lock(mutex) == 0); }
669+ *
670+ * static void
671+ * unlock(void *mutex) { assert(pthread_mutex_unlock(mutex) == 0); }
672+ *
673+ * static void
674+ * mutex_destroy(void *mutex) { assert(pthread_mutex_destroy(mutex) == 0); }
675+ *
676+ *
677+ *
678+ * At every instant, all of the data and pointers in the mapped file are valid.
679+ *	Changes to trie node or other data are always made so that it and
680+ *	all pointers in and to it remain valid for a time.  Old versions are
681+ *	eventually discarded.
682+ *
683+ * Dnsrpzd periodically defines a new version by setting asside all changes
684+ *	made since the previous version was defined.  Subsequent changes
685+ *	made (only!) by dnsrpzd will be part of the next version.
686+ *
687+ * To discard an old version, dnsrpzd must know that all clients have stopped
688+ *	using that version.  Clients do that by using part of the mapped file
689+ *	to tell dnsrpzd the oldest version that each client is using.
690+ *	Dnsrpzd assigns each connecting client an entry in the cversions array
691+ *	in the mapped file.  The client puts version numbers into that entry
692+ *	to signal to dnsrpzd which versions that can be discarded.
693+ *	Dnsrpzd is free, as far as that client is concerned, to discard all
694+ *	numerically smaller versions.  A client can disclaim all versions with
695+ *	the version number VERSIONS_ALL or 0.
696+ *
697+ * The race between a client changing its entry and dnsrpzd discarding a
698+ *	version is resolved by allowing dnsrpzd to discard all versions
699+ *	smaller or equal to the client's version number.  If dnsrpzd is in
700+ *	the midst of discarding or about to discard version N when the
701+ *	client asserts N, no harm is done.  The client depends only on
702+ *	the consistency of version N+1.
703+ *
704+ * This version mechanism depends in part on not being exercised too frequently
705+ *	Version numbers are 32 bits long and dnsrpzd creates new versions
706+ *	at most once every 30 seconds.
707+ */
708+
709+
710+/*
711+ * Lock functions for concurrent use of a single librpz_client_t client handle.
712+ */
713+typedef void(librpz_mutex_t)(void *mutex);
714+
715+/*
716+ * List of connections to dnsrpzd daemons.
717+ */
718+typedef struct librpz_clist librpz_clist_t;
719+
720+/*
721+ * Client's handle on dnsrpzd.
722+ */
723+typedef struct librpz_client librpz_client_t;
724+
725+/**
726+ * Create the list of connections to the dnsrpzd daemon.
727+ * @param[out] emsg: error message
728+ * @param lock: start exclusive access to the client handle
729+ * @param unlock: end exclusive access to the client handle
730+ * @param mutex_destroy: release the lock
731+ * @param mutex: pointer to the lock for the client handle
732+ * @param log_ctx: NULL or resolver's context log messages
733+ */
734+typedef librpz_clist_t *(librpz_clist_create_t)(librpz_emsg_t *emsg,
735+						librpz_mutex_t *lock,
736+						librpz_mutex_t *unlock,
737+						librpz_mutex_t *mutex_destroy,
738+						void *mutex, void *log_ctx);
739+LIBDEF_F(clist_create)
740+
741+
742+/**
743+ * Release the list of dnsrpzd connections.
744+ */
745+typedef void (librpz_clist_detach_t)(librpz_clist_t **clistp);
746+LIBDEF_F(clist_detach)
747+
748+/**
749+ * Create a librpz client handle.
750+ * @param[out] emsg: error message
751+ * @param: list of dnsrpzd connections
752+ * @param cstr: string of configuration settings separated by ';' or '\n'
753+ * @param use_expired: true to not ignore expired zones
754+ * @return client handle or NULL if the handle could not be created
755+ */
756+typedef librpz_client_t *(librpz_client_create_t)(librpz_emsg_t *emsg,
757+						  librpz_clist_t *clist,
758+						  const char *cstr,
759+						  bool use_expired);
760+LIBDEF_F(client_create)
761+
762+/**
763+ * Start (if necessary) dnsrpzd and connect to it.
764+ * @param[out] emsg: error message
765+ * @param client handle
766+ * @param optional: true if it is ok if starting the daemon is not allowed
767+ */
768+typedef bool (librpz_connect_t)(librpz_emsg_t *emsg, librpz_client_t *client,
769+				bool optional);
770+LIBDEF_F(connect)
771+
772+/**
773+ * Start to destroy a librpz client handle.
774+ * It will not be destroyed until the last set of RPZ queries represented
775+ * by a librpz_rsp_t ends.
776+ * @param client handle to be released
777+ * @return false on error
778+ */
779+typedef void (librpz_client_detach_t)(librpz_client_t **clientp);
780+LIBDEF_F(client_detach)
781+
782+/**
783+ * State for a set of RPZ queries for a single DNS response
784+ * or for listing the database.
785+ */
786+typedef struct librpz_rsp librpz_rsp_t;
787+
788+/**
789+ * Start a set of RPZ queries for a single DNS response.
790+ * @param[out] emsg: error message for false return or *rspp=NULL
791+ * @param[out] rspp created context or NULL
792+ * @param[out] min_ns_dotsp: NULL or pointer to configured MIN-NS-DOTS value
793+ * @param client state
794+ * @param have_rd: RD=1 in the DNS request
795+ * @param have_do: DO=1 in the DNS request
796+ * @return false on error
797+ */
798+typedef bool (librpz_rsp_create_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp,
799+				   int *min_ns_dotsp, librpz_client_t *client,
800+				   bool have_rd, bool have_do);
801+LIBDEF_F(rsp_create)
802+
803+/**
804+ * Finish RPZ work for a DNS response.
805+ */
806+typedef void (librpz_rsp_detach_t)(librpz_rsp_t **rspp);
807+LIBDEF_F(rsp_detach)
808+
809+/**
810+ * Get the final, accumulated result of a set of RPZ queries.
811+ * Yield LIBRPZ_POLICY_UNDEFINED if
812+ *  - there were no hits,
813+ *  - there was a dispositive hit, be we have not recursed and are required
814+ *	to recurse so that evil DNS authories will not know we are using RPZ
815+ *  - we have a hit and have recursed, but later data such as NSIP could
816+ *	override
817+ * @param[out] emsg
818+ * @param[out] result describes the hit
819+ *	or result->policy=LIBRPZ_POLICY_UNDEFINED without a hit
820+ * @param[out] result: current policy rewrite values
821+ * @param recursed: recursion has now been done even if it was not done
822+ *	when the hit was found
823+ * @param[in,out] rsp state from librpz_itr_start()
824+ * @return false on error
825+ */
826+typedef bool (librpz_rsp_result_t)(librpz_emsg_t *emsg, librpz_result_t *result,
827+				   bool recursed, const librpz_rsp_t *rsp);
828+LIBDEF_F(rsp_result)
829+
830+/**
831+ * Might looking for a trigger be worthwhile?
832+ * @param trig: look for this type of trigger
833+ * @param ipv6: true if trig is LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP,
834+ *	or LIBRPZ_TRIG_NSIP and the IP address is IPv6
835+ * @return: true if looking could be worthwhile
836+ */
837+typedef bool (librpz_have_trig_t)(librpz_trig_t trig, bool ipv6,
838+				  const librpz_rsp_t *rsp);
839+LIBDEF_F(have_trig)
840+
841+/**
842+ * Might looking for NSDNAME and NSIP triggers be worthwhile?
843+ * @return: true if looking could be worthwhile
844+ */
845+typedef bool (librpz_have_ns_trig_t)(const librpz_rsp_t *rsp);
846+LIBDEF_F(have_ns_trig)
847+
848+/**
849+ * Convert the found client IP trie key to a CIDR block
850+ * @param[out] emsg
851+ * @param[out] prefix trigger
852+ * @param[in,out] rsp state from librpz_itr_start()
853+ * @return false on error
854+ */
855+typedef bool (librpz_rsp_clientip_prefix_t)(librpz_emsg_t *emsg,
856+					    librpz_prefix_t *prefix,
857+					    librpz_rsp_t *rsp);
858+LIBDEF_F(rsp_clientip_prefix)
859+
860+/**
861+ * Compute the owner name of the found or result trie key, usually to log it.
862+ * An IP address key might be returned as 8.0.0.0.127.rpz-client-ip.
863+ * example.com. might be a qname trigger.  example.com.rpz-nsdname. could
864+ * be an NSDNAME trigger.
865+ * @param[out] emsg
866+ * @param[out] owner domain
867+ * @param[in,out] rsp state from librpz_itr_start()
868+ * @return false on error
869+ */
870+typedef bool (librpz_rsp_domain_t)(librpz_emsg_t *emsg,
871+				   librpz_domain_buf_t *owner,
872+				   librpz_rsp_t *rsp);
873+LIBDEF_F(rsp_domain)
874+
875+/**
876+ * Get the next RR of the LIBRPZ_POLICY_RECORD result after an initial use of
877+ * librpz_rsp_result() or librpz_itr_node() or after a previous use of
878+ * librpz_rsp_rr().  The RR is in uncompressed wire format including type,
879+ * class, ttl and length in network byte order.
880+ * @param[out] emsg
881+ * @param[out] typep: optional host byte order record type or ns_t_invalid (0)
882+ * @param[out] classp: class such as ns_c_in
883+ * @param[out] ttlp: TTL
884+ * @param[out] rrp: optionall malloc() buffer containting the next RR or
885+ *	NULL after the last RR
886+ * @param[out] result: current policy rewrite values
887+ * @param qname: used construct a wildcard CNAME
888+ * @param qname_size
889+ * @param[in,out] rsp state from librpz_itr_start()
890+ * @return false on error
891+ */
892+typedef bool (librpz_rsp_rr_t)(librpz_emsg_t *emsg, uint16_t *typep,
893+			       uint16_t *classp, uint32_t *ttlp,
894+			       librpz_rr_t **rrp, librpz_result_t *result,
895+			       const uint8_t *qname, size_t qname_size,
896+			       librpz_rsp_t *rsp);
897+LIBDEF_F(rsp_rr)
898+
899+/**
900+ * Get the next RR of the LIBRPZ_POLICY_RECORD result.
901+ * @param[out] emsg
902+ * @param[out] ttlp: TTL
903+ * @param[out] rrp: malloc() buffer with SOA RR without owner name
904+ * @param[out] result: current policy rewrite values
905+ * @param[out] origin: SOA owner name
906+ * @param[out] origin_size
907+ * @param[in,out] rsp state from librpz_itr_start()
908+ * @return false on error
909+ */
910+typedef bool (librpz_rsp_soa_t)(librpz_emsg_t *emsg, uint32_t *ttlp,
911+				librpz_rr_t **rrp, librpz_domain_buf_t *origin,
912+				librpz_result_t *result, librpz_rsp_t *rsp);
913+LIBDEF_F(rsp_soa)
914+
915+/**
916+ * Get the SOA serial number for a policy zone to compare with a known value
917+ * to check whether a zone tranfer is complete.
918+ */
919+typedef bool (librpz_soa_serial_t)(librpz_emsg_t *emsg, uint32_t *serialp,
920+				   const char *domain_nm, librpz_rsp_t *rsp);
921+LIBDEF_F(soa_serial)
922+
923+/**
924+ * Save the current policy checking state.
925+ * @param[out] emsg
926+ * @param[in,out] rsp state from librpz_itr_start()
927+ * @return false on error
928+ */
929+typedef bool (librpz_rsp_push_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
930+LIBDEF_F(rsp_push)
931+#define LIBRPZ_RSP_STACK_DEPTH	3
932+
933+/**
934+ * Restore the previous policy checking state.
935+ * @param[out] emsg
936+ * @param[out] result: NULL or restored policy rewrite values
937+ * @param[in,out] rsp state from librpz_itr_start()
938+ * @return false on error
939+ */
940+typedef bool (librpz_rsp_pop_t)(librpz_emsg_t *emsg, librpz_result_t *result,
941+				librpz_rsp_t *rsp);
942+LIBDEF_F(rsp_pop)
943+
944+/**
945+ * Discard the most recently save policy checking state.
946+ * @param[out] emsg
947+ * @param[out] result: NULL or restored policy rewrite values
948+ * @return false on error
949+ */
950+typedef bool (librpz_rsp_pop_discard_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
951+LIBDEF_F(rsp_pop_discard)
952+
953+/**
954+ * Disable a zone.
955+ * @param[out] emsg
956+ * @param znum
957+ * @param[in,out] rsp state from librpz_itr_start()
958+ * @return false on error
959+ */
960+typedef bool (librpz_rsp_forget_zone_t)(librpz_emsg_t *emsg,
961+					librpz_cznum_t znum, librpz_rsp_t *rsp);
962+LIBDEF_F(rsp_forget_zone)
963+
964+/**
965+ * Apply RPZ to an IP address.
966+ * @param[out] emsg
967+ * @param addr: address to check
968+ * @param ipv6: true for 16 byte IPv6 instead of 4 byte IPv4
969+ * @param trig LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP, or LIBRPZ_TRIG_NSIP
970+ * @param hit_id: caller chosen
971+ * @param recursed: recursion has been done
972+ * @param[in,out] rsp state from librpz_itr_start()
973+ * @return false on error
974+ */
975+typedef bool (librpz_ck_ip_t)(librpz_emsg_t *emsg,
976+			      const void *addr, uint family,
977+			      librpz_trig_t trig, librpz_result_id_t hit_id,
978+			      bool recursed, librpz_rsp_t *rsp);
979+LIBDEF_F(ck_ip)
980+
981+/**
982+ * Apply RPZ to a wire-format domain.
983+ * @param[out] emsg
984+ * @param domain in wire format
985+ * @param domain_size
986+ * @param trig LIBRPZ_TRIG_QNAME or LIBRPZ_TRIG_NSDNAME
987+ * @param hit_id: caller chosen
988+ * @param recursed: recursion has been done
989+ * @param[in,out] rsp state from librpz_itr_start()
990+ * @return false on error
991+ */
992+typedef bool (librpz_ck_domain_t)(librpz_emsg_t *emsg,
993+				  const uint8_t *domain, size_t domain_size,
994+				  librpz_trig_t trig, librpz_result_id_t hit_id,
995+				  bool recursed, librpz_rsp_t *rsp);
996+LIBDEF_F(ck_domain)
997+
998+/**
999+ * Ask dnsrpzd to refresh a zone.
1000+ * @param[out] emsg error message
1001+ * @param librpz_domain_t domain to refresh
1002+ * @param client context
1003+ * @return false after error
1004+ */
1005+typedef bool (librpz_zone_refresh_t)(librpz_emsg_t *emsg, const char *domain,
1006+				       librpz_rsp_t *rsp);
1007+LIBDEF_F(zone_refresh)
1008+
1009+/**
1010+ * Get a string describing the the databasse
1011+ * @param license: include the license
1012+ * @param cfiles: include the configuration file names
1013+ * @param listens: include the local notify IP addresses
1014+ * @param[out] emsg error message if the result is null
1015+ * @param client context
1016+ * @return malloc'ed string or NULL after error
1017+ */
1018+typedef char *(librpz_db_info_t)(librpz_emsg_t *emsg,
1019+				 bool license, bool cfiles, bool listens,
1020+				 librpz_rsp_t *rsp);
1021+LIBDEF_F(db_info)
1022+
1023+/**
1024+ * Start a context for listing the nodes and/or zones in the mapped file
1025+ * @param[out] emsg: error message for false return or *rspp=NULL
1026+ * @param[out[ rspp created context or NULL
1027+ * @param client context
1028+ * @return false after error
1029+ */
1030+typedef bool (librpz_itr_start_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp,
1031+				  librpz_client_t *client);
1032+LIBDEF_F(itr_start)
1033+
1034+/**
1035+ * Get mapped file memory allocation statistics.
1036+ * @param[out] emsg: error message
1037+ * @param rsp state from librpz_itr_start()
1038+ * @return malloc'ed string or NULL after error
1039+ */
1040+typedef char *(librpz_mf_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
1041+LIBDEF_F(mf_stats)
1042+
1043+/**
1044+ * Get versions currently used by clients.
1045+ * @param[out] emsg: error message
1046+ * @param[in,out] rsp: state from librpz_itr_start()
1047+ * @return malloc'ed string or NULL after error
1048+ */
1049+typedef char *(librpz_vers_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
1050+LIBDEF_F(vers_stats)
1051+
1052+/**
1053+ * Allocate a string describing the next zone or "" after the last zone.
1054+ * @param[out] emsg
1055+ * @param all_zones to list all instead of only requested zones
1056+ * @param[in,out] rsp state from librpz_rsp_start()
1057+ * @return malloc'ed string or NULL after error
1058+ */
1059+typedef char *(librpz_itr_zone_t)(librpz_emsg_t *emsg, bool all_zones,
1060+				  librpz_rsp_t *rsp);
1061+LIBDEF_F(itr_zone)
1062+
1063+/**
1064+ * Describe the next trie node while dumping the database.
1065+ * @param[out] emsg
1066+ * @param[out] result describes node
1067+ *	or result->policy=LIBRPZ_POLICY_UNDEFINED after the last node.
1068+ * @param all_zones to list all instead of only requested zones
1069+ * @param[in,out] rsp state from librpz_itr_start()
1070+ * @return: false on error
1071+ */
1072+typedef bool (librpz_itr_node_t)(librpz_emsg_t *emsg, librpz_result_t *result,
1073+				 bool all_zones, librpz_rsp_t *rsp);
1074+LIBDEF_F(itr_node)
1075+
1076+/**
1077+ * RPZ policy to string with a backup buffer of POLICY2STR_SIZE size
1078+ */
1079+typedef const char *(librpz_policy2str_t)(librpz_policy_t policy,
1080+					  char *buf, size_t buf_size);
1081+#define POLICY2STR_SIZE sizeof("policy xxxxxx")
1082+LIBDEF_F(policy2str)
1083+
1084+/**
1085+ * Trigger type to string.
1086+ */
1087+typedef const char *(librpz_trig2str_t)(librpz_trig_t trig);
1088+LIBDEF_F(trig2str)
1089+
1090+/**
1091+ * Convert a number of seconds to a zone file duration string
1092+ */
1093+typedef const char *(librpz_secs2str_t)(time_t secs,
1094+					char *buf, size_t buf_size);
1095+#define SECS2STR_SIZE sizeof("1234567w7d24h59m59s")
1096+LIBDEF_F(secs2str)
1097+
1098+/**
1099+ * Parse a duration with 's', 'm', 'h', 'd', and 'w' units.
1100+ */
1101+typedef bool (librpz_str2secs_t)(librpz_emsg_t *emsg, time_t *val,
1102+				 const char *str0);
1103+LIBDEF_F(str2secs)
1104+
1105+/**
1106+ * Translate selected rtypes to strings
1107+ */
1108+typedef const char *(librpz_rtype2str_t)(uint type, char *buf, size_t buf_size);
1109+#define RTYPE2STR_SIZE sizeof("type xxxxx")
1110+LIBDEF_F(rtype2str)
1111+
1112+/**
1113+ * Local version of ns_name_ntop() for portability.
1114+ */
1115+typedef int (librpz_domain_ntop_t)(const u_char *src, char *dst, size_t dstsiz);
1116+LIBDEF_F(domain_ntop)
1117+
1118+/**
1119+ * Local version of ns_name_pton().
1120+ */
1121+typedef int (librpz_domain_pton2_t)(const char *src, u_char *dst, size_t dstsiz,
1122+				    size_t *dstlen, bool lower);
1123+LIBDEF_F(domain_pton2)
1124+
1125+typedef union socku socku_t;
1126+typedef socku_t *(librpz_mk_inet_su_t)(socku_t *su, const struct in_addr *addrp,
1127+				     in_port_t port);
1128+LIBDEF_F(mk_inet_su)
1129+
1130+typedef socku_t *(librpz_mk_inet6_su_t)(socku_t *su, const
1131+					struct in6_addr *addrp,
1132+					uint32_t scope_id, in_port_t port);
1133+LIBDEF_F(mk_inet6_su)
1134+
1135+typedef bool (librpz_str2su_t)(socku_t *sup, const char *str);
1136+LIBDEF_F(str2su)
1137+
1138+typedef char *(librpz_su2str_t)(char *str, size_t str_len, const socku_t *su);
1139+LIBDEF_F(su2str)
1140+#define SU2STR_SIZE (INET6_ADDRSTRLEN+1+6+1)
1141+
1142+
1143+/**
1144+ * default path to dnsrpzd
1145+ */
1146+const char *librpz_dnsrpzd_path;
1147+
1148+
1149+#undef LIBDEF
1150+
1151+/*
1152+ * This is the dlopen() interface to librpz.
1153+ */
1154+typedef const struct {
1155+	const char			*dnsrpzd_path;
1156+	const char			*version;
1157+	librpz_parse_log_opt_t		*parse_log_opt;
1158+	librpz_log_level_val_t		*log_level_val;
1159+	librpz_set_log_t		*set_log;
1160+	librpz_vpemsg_t			*vpemsg;
1161+	librpz_pemsg_t			*pemsg;
1162+	librpz_vlog_t			*vlog;
1163+	librpz_log_t			*log;
1164+	librpz_fatal_t			*fatal LIBRPZ_NORET;
1165+	librpz_rpz_assert_t		*rpz_assert LIBRPZ_NORET;
1166+	librpz_rpz_vassert_t		*rpz_vassert LIBRPZ_NORET;
1167+	librpz_clist_create_t		*clist_create;
1168+	librpz_clist_detach_t		*clist_detach;
1169+	librpz_client_create_t		*client_create;
1170+	librpz_connect_t		*connect;
1171+	librpz_client_detach_t		*client_detach;
1172+	librpz_rsp_create_t		*rsp_create;
1173+	librpz_rsp_detach_t		*rsp_detach;
1174+	librpz_rsp_result_t		*rsp_result;
1175+	librpz_have_trig_t		*have_trig;
1176+	librpz_have_ns_trig_t		*have_ns_trig;
1177+	librpz_rsp_clientip_prefix_t	*rsp_clientip_prefix;
1178+	librpz_rsp_domain_t		*rsp_domain;
1179+	librpz_rsp_rr_t			*rsp_rr;
1180+	librpz_rsp_soa_t		*rsp_soa;
1181+	librpz_soa_serial_t		*soa_serial;
1182+	librpz_rsp_push_t		*rsp_push;
1183+	librpz_rsp_pop_t		*rsp_pop;
1184+	librpz_rsp_pop_discard_t	*rsp_pop_discard;
1185+	librpz_rsp_forget_zone_t	*rsp_forget_zone;
1186+	librpz_ck_ip_t			*ck_ip;
1187+	librpz_ck_domain_t		*ck_domain;
1188+	librpz_zone_refresh_t		*zone_refresh;
1189+	librpz_db_info_t		*db_info;
1190+	librpz_itr_start_t		*itr_start;
1191+	librpz_mf_stats_t		*mf_stats;
1192+	librpz_vers_stats_t		*vers_stats;
1193+	librpz_itr_zone_t		*itr_zone;
1194+	librpz_itr_node_t		*itr_node;
1195+	librpz_policy2str_t		*policy2str;
1196+	librpz_trig2str_t		*trig2str;
1197+	librpz_secs2str_t		*secs2str;
1198+	librpz_str2secs_t		*str2secs;
1199+	librpz_rtype2str_t		*rtype2str;
1200+	librpz_domain_ntop_t		*domain_ntop;
1201+	librpz_domain_pton2_t		*domain_pton2;
1202+	librpz_mk_inet_su_t		*mk_inet_su;
1203+	librpz_mk_inet6_su_t		*mk_inet6_su;
1204+	librpz_str2su_t			*str2su;
1205+	librpz_su2str_t			*su2str;
1206+} librpz_0_t;
1207+extern librpz_0_t librpz_def_0;
1208+
1209+/*
1210+ * Future versions can be upward compatible by defining LIBRPZ_DEF as
1211+ * librpz_X_t.
1212+ */
1213+#define LIBRPZ_DEF	librpz_def_0
1214+#define LIBRPZ_DEF_STR	"librpz_def_0"
1215+
1216+typedef librpz_0_t librpz_t;
1217+extern librpz_t *librpz;
1218+
1219+
1220+#if LIBRPZ_LIB_OPEN == 2
1221+#include <dlfcn.h>
1222+
1223+/**
1224+ * link-load librpz
1225+ * @param[out] emsg: error message
1226+ * @param[in,out] dl_handle: NULL or pointer to new dlopen handle
1227+ * @param[in] path: librpz.so path
1228+ * @return address of interface structure or NULL on failure
1229+ */
1230+static inline librpz_t *
1231+librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path)
1232+{
1233+	void *handle;
1234+	librpz_t *new_librpz;
1235+
1236+	emsg->c[0] = '\0';
1237+
1238+	/*
1239+	 * Close a previously opened handle on librpz.so.
1240+	 */
1241+	if (dl_handle != NULL && *dl_handle != NULL) {
1242+		if (dlclose(*dl_handle) != 0) {
1243+			snprintf(emsg->c, sizeof(librpz_emsg_t),
1244+				 "dlopen(NULL): %s", dlerror());
1245+			return (NULL);
1246+		}
1247+		*dl_handle = NULL;
1248+	}
1249+
1250+	/*
1251+	 * First try the main executable of the process in case it was
1252+	 * linked to librpz.
1253+	 * Do not worry if we cannot search the main executable of the process.
1254+	 */
1255+	handle = dlopen(NULL, RTLD_NOW | RTLD_LOCAL);
1256+	if (handle != NULL) {
1257+		new_librpz = dlsym(handle, LIBRPZ_DEF_STR);
1258+		if (new_librpz != NULL) {
1259+			if (dl_handle != NULL)
1260+				*dl_handle = handle;
1261+			return (new_librpz);
1262+		}
1263+		if (dlclose(handle) != 0) {
1264+			snprintf(emsg->c, sizeof(librpz_emsg_t),
1265+				 "dlsym(NULL, "LIBRPZ_DEF_STR"): %s",
1266+				 dlerror());
1267+			return (NULL);
1268+		}
1269+	}
1270+
1271+	if (path == NULL || path[0] == '\0') {
1272+		snprintf(emsg->c, sizeof(librpz_emsg_t),
1273+			 "librpz not linked and no dlopen() path provided");
1274+		return (NULL);
1275+	}
1276+
1277+	handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
1278+	if (handle == NULL) {
1279+		snprintf(emsg->c, sizeof(librpz_emsg_t), "dlopen(%s): %s",
1280+			 path, dlerror());
1281+		return (NULL);
1282+	}
1283+	new_librpz = dlsym(handle, LIBRPZ_DEF_STR);
1284+	if (new_librpz != NULL) {
1285+		if (dl_handle != NULL)
1286+			*dl_handle = handle;
1287+		return (new_librpz);
1288+	}
1289+	snprintf(emsg->c, sizeof(librpz_emsg_t),
1290+		 "dlsym(%s, "LIBRPZ_DEF_STR"): %s",
1291+		 path, dlerror());
1292+	dlclose(handle);
1293+	return (NULL);
1294+}
1295+
1296+#elif defined(LIBRPZ_LIB_OPEN)
1297+
1298+/*
1299+ * Statically link to the librpz.so DSO on systems without dlopen()
1300+ */
1301+static inline librpz_t *
1302+librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path)
1303+{
1304+	(void)(path);
1305+
1306+	if (dl_handle != NULL)
1307+		*dl_handle = NULL;
1308+
1309+#if LIBRPZ_LIB_OPEN == 1
1310+	emsg->c[0] = '\0';
1311+	return (&LIBRPZ_DEF);
1312+#else
1313+	snprintf(emsg->c, sizeof(librpz_emsg_t),
1314+		 "librpz not available via ./configure");
1315+	return (NULL);
1316+#endif /* LIBRPZ_LIB_OPEN */
1317+}
1318+#endif /* LIBRPZ_LIB_OPEN */
1319+
1320+#endif /* LIBRPZ_H */
1321Index: unbound-1.7.0~rc1/fastrpz/rpz.c
1322===================================================================
1323--- /dev/null
1324+++ unbound-1.7.0~rc1/fastrpz/rpz.c
1325@@ -0,0 +1,1357 @@
1326+/*
1327+ * fastrpz/rpz.c - interface to the fastrpz response policy zone library
1328+ *
1329+ * Optimize no-rewrite cases for speed but optimize rewriting for
1330+ * simplicity and size.
1331+ */
1332+
1333+#include "config.h"
1334+
1335+#ifdef ENABLE_FASTRPZ
1336+#include "daemon/daemon.h"
1337+#define LIBRPZ_LIB_OPEN FASTRPZ_LIB_OPEN
1338+#include "fastrpz/rpz.h"
1339+#include "daemon/worker.h"
1340+#include "iterator/iter_delegpt.h"
1341+#include "iterator/iter_utils.h"
1342+#include "iterator/iterator.h"
1343+#include "util/data/dname.h"
1344+#include "util/data/msgencode.h"
1345+#include "util/data/msgparse.h"
1346+#include "util/data/msgreply.h"
1347+#include "util/log.h"
1348+#include "util/netevent.h"
1349+#include "util/net_help.h"
1350+#include "util/regional.h"
1351+#include "util/storage/slabhash.h"
1352+#include "services/cache/dns.h"
1353+#include "services/cache/rrset.h"
1354+#include "services/mesh.h"
1355+#include "sldns/sbuffer.h"
1356+#include "sldns/rrdef.h"
1357+
1358+
1359+typedef enum state {
1360+	/* No more rewriting */
1361+	st_off = 1,
1362+	/* Send SERVFAIL */
1363+	st_servfail,
1364+	/* No dispositive hit yet */
1365+	st_unknown,
1366+	/* Let the iterator resolve a CNAME or get a delegation point. */
1367+	st_iterate,
1368+	/* Let the iterator resolve NS to check NSIP or NSDNAME triggers. */
1369+	st_ck_ns,
1370+	/* We have an answer */
1371+	st_rewritten,
1372+} st_t;
1373+
1374+
1375+/* RPZ state pointed to by struct comm_reply */
1376+typedef struct commreply_rpz {
1377+	/* librpz state */
1378+	librpz_rsp_t*	rsp;
1379+	/* ID for log messages */
1380+	int		log_id;
1381+
1382+	/* from configuration */
1383+	int		min_ns_dots;
1384+
1385+	/* Running in the iterator */
1386+	bool		iterating;
1387+
1388+	/* current and previous state and librpz result */
1389+	st_t		st;
1390+	st_t		saved_st[LIBRPZ_RSP_STACK_DEPTH-1];
1391+	librpz_result_t result;
1392+
1393+	/* Stop adding CNAMEs to the prepend list before this owner name. */
1394+	librpz_domain_buf_t cname_hit;
1395+	/* It is not the first CNAME */
1396+	bool		cname_hit_2nd;
1397+	librpz_result_id_t hit_id;
1398+} commreply_rpz_t;
1399+
1400+
1401+/* Generate an ID for log messages. */
1402+static int log_id;
1403+
1404+librpz_t *librpz;
1405+
1406+
1407+static void LIBRPZ_NORET
1408+rpz_assert(const char *s)
1409+{
1410+	fatal_exit("%s", s);
1411+	exit(1);
1412+}
1413+#define RPZ_ASSERT(c) ((c) ? (void)0 : rpz_assert(#c), (void)0)
1414+
1415+/*
1416+ * librpz client handle locking
1417+ */
1418+static void
1419+lock_destroy(void* mutex)
1420+{
1421+	lock_basic_destroy(mutex);
1422+	free(mutex);
1423+}
1424+
1425+static void
1426+lock(void* mutex)
1427+{
1428+	lock_basic_lock(mutex);
1429+}
1430+
1431+static void
1432+unlock(void* mutex)
1433+{
1434+	lock_basic_unlock(mutex);
1435+}
1436+
1437+
1438+static void
1439+log_fnc(librpz_log_level_t level, void* ATTR_UNUSED(ctx), const char* buf)
1440+{
1441+	char label_buf[sizeof("rpz ")+8];
1442+
1443+	/* Setting librpz_log_level overrides the unbound "verbose" level. */
1444+	if(level > LIBRPZ_LOG_TRACE1 &&
1445+	   level <= librpz->log_level_val(LIBRPZ_LOG_INVALID))
1446+		level = LIBRPZ_LOG_TRACE1;
1447+
1448+	switch(level) {
1449+	case LIBRPZ_LOG_FATAL:
1450+	case LIBRPZ_LOG_ERROR:		/* errors */
1451+	default:
1452+		log_err("rpz: %s", buf);
1453+		break;
1454+
1455+	case LIBRPZ_LOG_TRACE1:		/* big events such as dnsrpzd starts */
1456+		verbose(VERB_OPS, "rpz: %s", buf);
1457+		break;
1458+
1459+	case LIBRPZ_LOG_TRACE2:		/* smaller dnsrpzd zone transfers */
1460+		verbose(VERB_DETAIL, "rpz: %s", buf);
1461+		break;
1462+
1463+	case LIBRPZ_LOG_TRACE3:		/* librpz hits */
1464+		verbose(VERB_QUERY, "rpz: %s", buf);
1465+		break;
1466+
1467+	case LIBRPZ_LOG_TRACE4:		/* librpz lookups */
1468+		verbose(VERB_CLIENT, "rpz: %s", buf);
1469+		break;
1470+	}
1471+}
1472+
1473+
1474+/* Release the librpz version. */
1475+static void
1476+rpz_off(commreply_rpz_t* rpz, st_t st)
1477+{
1478+	if(!rpz)
1479+		return;
1480+	rpz->st = st;
1481+	librpz->rsp_detach(&rpz->rsp);
1482+}
1483+
1484+
1485+static void LIBRPZ_PF(2,3)
1486+log_fail(commreply_rpz_t* rpz, const char* p, ...)
1487+{
1488+	va_list args;
1489+
1490+	if(rpz->st == st_servfail)
1491+		return;
1492+
1493+	va_start(args, p);
1494+	librpz->vlog(LIBRPZ_LOG_ERROR, rpz, p, args);
1495+	va_end(args);
1496+	if(!rpz)
1497+		return;
1498+	rpz_off(rpz, st_servfail);
1499+}
1500+
1501+
1502+/* Announce a rewrite. */
1503+static void
1504+log_rewrite(uint8_t* qname, librpz_policy_t policy, const char* msg,
1505+	    commreply_rpz_t* rpz)
1506+{
1507+	char policy_buf[POLICY2STR_SIZE];
1508+	char qname_nm[LDNS_MAX_DOMAINLEN+1];
1509+	librpz_domain_buf_t tdomain;
1510+	char tdomain_nm[LDNS_MAX_DOMAINLEN+1];
1511+	librpz_emsg_t emsg;
1512+
1513+	if(rpz->st == st_servfail || !rpz->result.log)
1514+		return;
1515+	if(librpz->log_level_val(LIBRPZ_LOG_INVALID) < LIBRPZ_LOG_TRACE1)
1516+		return;
1517+
1518+	dname_str(qname, qname_nm);
1519+
1520+	if(!librpz->rsp_domain(&emsg, &tdomain, rpz->rsp)) {
1521+		librpz->log(LIBRPZ_LOG_ERROR, rpz, "%s", emsg.c);
1522+		return;
1523+	}
1524+	dname_str(tdomain.d, tdomain_nm);
1525+
1526+	librpz->log(LIBRPZ_LOG_TRACE3, rpz, "%srewriting %s via %s %s to %s",
1527+		    msg, qname_nm, tdomain_nm,
1528+		    librpz->trig2str(rpz->result.trig),
1529+		    librpz->policy2str(policy, policy_buf,
1530+				       sizeof(policy_buf)));
1531+}
1532+
1533+
1534+/* Connect to and start dnsrpzd if necessary for the unbound daemon.
1535+ *	Require "rpz-conf: path" to specify the rpz configuration file.
1536+ *	The unbound server directory name is the default rpz working
1537+ *	    directory.  If unbound uses chroot, then the dnsrpzd working
1538+ *	    directory must be in the chroot tree.
1539+ *	The database and socket are closed and re-opened.
1540+ */
1541+void
1542+rpz_init(librpz_clist_t** pclist, librpz_client_t** pclient,
1543+	 const struct config_file* cfg)
1544+{
1545+	lock_basic_type* mutex;
1546+	librpz_emsg_t emsg;
1547+
1548+	if(!librpz) {
1549+		librpz = librpz_lib_open(&emsg, NULL, FASTRPZ_LIBRPZ_PATH);
1550+		if(!librpz)
1551+			fatal_exit("rpz: %s", emsg.c);
1552+	}
1553+
1554+	librpz->set_log(&log_fnc, NULL);
1555+
1556+	if(!cfg->rpz_cstr)
1557+		fatal_exit("rpz: rpz-zone: not set");
1558+
1559+	librpz->client_detach(pclient);
1560+	librpz->clist_detach(pclist);
1561+
1562+	mutex = malloc(sizeof(*mutex));
1563+	if(!mutex)
1564+		fatal_exit("rpz: no memory for lock");
1565+	lock_basic_init(mutex);
1566+
1567+	*pclist = librpz->clist_create(&emsg, &lock, &unlock, &lock_destroy,
1568+				       mutex, NULL);
1569+	if(!pclist)
1570+		fatal_exit("rpz: %s", emsg.c);
1571+
1572+	*pclient = librpz->client_create(&emsg, *pclist, cfg->rpz_cstr, false);
1573+	if(!*pclient)
1574+		fatal_exit("rpz: %s", emsg.c);
1575+
1576+	if(!librpz->connect(&emsg, *pclient, true))
1577+		fatal_exit("rpz: %s", emsg.c);
1578+
1579+	verbose(VERB_OPS, "rpz: librpz version %s", librpz->version);
1580+}
1581+
1582+
1583+/* Stop using librpz on behalf of a worker thread. */
1584+void
1585+rpz_delete(librpz_clist_t** pclist, librpz_client_t** pclient)
1586+{
1587+	if(librpz) {
1588+		librpz->client_detach(pclient);
1589+		librpz->clist_detach(pclist);
1590+	}
1591+}
1592+
1593+
1594+/* Release the librpz resources held for a DNS client request. */
1595+void
1596+rpz_end(struct comm_reply* commreply)
1597+{
1598+	if(!commreply->rpz)
1599+		return;
1600+	rpz_off(commreply->rpz, commreply->rpz->st);
1601+	free(commreply->rpz);
1602+	commreply->rpz = NULL;
1603+}
1604+
1605+
1606+static bool
1607+push_st(commreply_rpz_t* rpz)
1608+{
1609+	librpz_emsg_t emsg;
1610+
1611+	if(rpz->st == st_off || rpz->st == st_servfail) {
1612+		librpz->log(LIBRPZ_LOG_ERROR, rpz,
1613+			    "state %d in push_st()", rpz->st);
1614+		return false;
1615+	}
1616+	if(!librpz->rsp_push(&emsg, rpz->rsp))
1617+		log_fail(rpz, "%s", emsg.c);
1618+	memmove(&rpz->saved_st[1], &rpz->saved_st[0],
1619+		sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0]));
1620+	rpz->saved_st[0] = rpz->st;
1621+	return rpz->st != st_servfail;
1622+}
1623+
1624+
1625+static bool
1626+pop_st(commreply_rpz_t* rpz)
1627+{
1628+	librpz_emsg_t emsg;
1629+
1630+	if(rpz->rsp && !librpz->rsp_pop(&emsg, &rpz->result, rpz->rsp))
1631+		log_fail(rpz, "%s", emsg.c);
1632+	if(rpz->st != st_servfail)
1633+		rpz->st = rpz->saved_st[0];
1634+	memmove(&rpz->saved_st[0], &rpz->saved_st[1],
1635+		sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0]));
1636+	return rpz->st != st_servfail;
1637+}
1638+
1639+static bool
1640+pop_discard_st(commreply_rpz_t* rpz)
1641+{
1642+	librpz_emsg_t emsg;
1643+
1644+	if(rpz->rsp && !librpz->rsp_pop_discard(&emsg, rpz->rsp))
1645+		log_fail(rpz, "%s", emsg.c);
1646+	memmove(&rpz->saved_st[0], &rpz->saved_st[1],
1647+		sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0]));
1648+	return rpz->st != st_servfail;
1649+}
1650+
1651+/* Check a rewrite attempt for errors and a disabled zone. */
1652+static bool				/* true=repeat the check */
1653+ck_after(uint8_t* qname, bool recursed, librpz_trig_t trig,
1654+	 commreply_rpz_t* rpz)
1655+{
1656+	librpz_emsg_t emsg;
1657+
1658+	if(rpz->st == st_servfail)
1659+		return false;
1660+
1661+	if(!librpz->rsp_result(&emsg, &rpz->result, recursed, rpz->rsp)) {
1662+		log_fail(rpz, "%s", emsg.c);
1663+		return false;
1664+	}
1665+
1666+	if(rpz->result.policy == LIBRPZ_POLICY_DISABLED) {
1667+		/* Log the hit on the disabled zone, do not try the zone again,
1668+		 * and restore the state from before the check to forget the hit
1669+		 * before trying again. */
1670+		log_rewrite(qname, rpz->result.zpolicy, "disabled ", rpz);
1671+		if(!librpz->rsp_forget_zone(&emsg, rpz->result.cznum, rpz->rsp))
1672+			log_fail(rpz, "%s", emsg.c);
1673+		return pop_st(rpz);
1674+	}
1675+
1676+	/* Complain about and forget client-IP address hit that is not
1677+	 * dispositive.  Client-IP triggers have the highest priority
1678+	 * within a policy zone, but can be overridden by any hit in a policy
1679+	 * earlier in the client's (resolver's) list of zones, including
1680+	 * policies that cannot be hit until after recursion. If we allowed
1681+	 * client-IP triggers in secondary zones, then than two DNS requests
1682+	 * that differ only in DNS client-IP addresses could properly
1683+	 * have differing results.  The Unbound iterator treats identical
1684+	 * DNS requests the same regardless of DNS client-IP address.
1685+	 * struct query_info would need to be modified to have an optional
1686+	 * librpz_prefix_t containing the prefix of the client-IP address hit
1687+	 * from librpz->rsp_clientip_prefix().  Adding to struct query_info
1688+	 * would require finding and changing the many and obscure places
1689+	 * including the Unbound tests to memset(0) the struct query_info
1690+	 * that they create. */
1691+	if(trig == LIBRPZ_TRIG_CLIENT_IP) {
1692+		if(rpz->result.cznum != 0) {
1693+			log_rewrite(qname, rpz->result.policy,
1694+				     "ignore secondary ", rpz);
1695+			if(!pop_st(rpz))
1696+				log_fail(rpz, "%s", emsg.c);
1697+			return (false);
1698+		}
1699+	}
1700+
1701+	/* Forget the state from before the check and keep the new state
1702+	 * if we do not have a hit on a disabled policy zone. */
1703+	pop_discard_st(rpz);
1704+	return false;
1705+}
1706+
1707+
1708+/* Get the next RR from the policy record. */
1709+static bool
1710+next_rr(librpz_rr_t** rrp, const uint8_t* qname, size_t qname_len,
1711+	commreply_rpz_t* rpz)
1712+{
1713+	librpz_emsg_t emsg;
1714+
1715+	if(!librpz->rsp_rr(&emsg, NULL, NULL, NULL, rrp, &rpz->result,
1716+			   qname, qname_len, rpz->rsp)) {
1717+		log_fail(rpz, "%s", emsg.c);
1718+		*rrp = NULL;
1719+		return false;
1720+	}
1721+	return true;
1722+}
1723+
1724+
1725+static bool				/* false=fatal error to be logged */
1726+add_rr(struct sldns_buffer* pkt, const uint8_t* owner, size_t owner_len,
1727+       librpz_rr_t* rr, commreply_rpz_t* rpz)
1728+{
1729+	size_t rdlength;
1730+
1731+	rdlength = ntohs(rr->rdlength);
1732+
1733+	if(!sldns_buffer_available(pkt, owner_len + 10 + rdlength)) {
1734+		log_fail(rpz, "comm_reply buffer exhausted");
1735+		free(rr);
1736+		return false;
1737+	}
1738+	sldns_buffer_write(pkt, owner, owner_len);
1739+	/* sizeof(librpz_rr_t)=12 instead of 10 */
1740+	sldns_buffer_write(pkt, rr, 10 + rdlength);
1741+	return true;
1742+}
1743+
1744+
1745+/* Convert a fake incoming DNS message to an Unbound struct dns_msg */
1746+static void
1747+pkt2dns_msg(struct dns_msg** dnsmsg, struct sldns_buffer* pkt,
1748+	    commreply_rpz_t* rpz, struct regional* region)
1749+{
1750+	struct msg_parse* msgparse;
1751+
1752+	msgparse = regional_alloc(region, sizeof(*msgparse));
1753+	if(!msgparse) {
1754+		log_fail(rpz, "out of memory for msgparse");
1755+		*dnsmsg = NULL;
1756+		return;
1757+	}
1758+	memset(msgparse, 0, sizeof(*msgparse));
1759+	if(parse_packet(pkt, msgparse, region) != LDNS_RCODE_NOERROR) {
1760+		log_fail(rpz, "packet parse error");
1761+		*dnsmsg = NULL;
1762+		return;
1763+	}
1764+	*dnsmsg = dns_alloc_msg(pkt, msgparse, region);
1765+	if(!*dnsmsg) {
1766+		log_fail(rpz, "dns_alloc_msg() failed");
1767+		*dnsmsg = NULL;
1768+		return;
1769+	}
1770+	(*dnsmsg)->rep->security = sec_status_rpz_rewritten;
1771+}
1772+
1773+
1774+static bool				/* false=SERVFAIL */
1775+ck_ip_rrset(const void* vdata, int family, librpz_trig_t trig,
1776+	    uint8_t* qname, commreply_rpz_t* rpz)
1777+{
1778+	const struct packed_rrset_data* data;
1779+	uint rr_n;
1780+	size_t len;
1781+	librpz_emsg_t emsg;
1782+
1783+	data = vdata;
1784+
1785+	/* Loop to ignore disabled zones. */
1786+	do {
1787+		if(!push_st(rpz))
1788+			return false;
1789+		for(rr_n = 0; rr_n < data->count; ++rr_n) {
1790+			len = data->rr_len[rr_n];
1791+			/* Skip bogus including negative placeholding rdata. */
1792+			if((family == AF_INET &&
1793+			    len != sizeof(struct in_addr)+2) ||
1794+			   (family == AF_INET6 &&
1795+			    len != sizeof(struct in6_addr)+2))
1796+				continue;
1797+			if(!librpz->ck_ip(&emsg, data->rr_data[rr_n]+2,
1798+					  family, trig, rpz->hit_id, true,
1799+					  rpz->rsp)) {
1800+				log_fail(rpz, "%s", emsg.c);
1801+				return false;
1802+			}
1803+		}
1804+	} while(ck_after(qname, true, trig, rpz));
1805+	return rpz->st != st_servfail;
1806+}
1807+
1808+
1809+static bool				/* false=SERVFAIL */
1810+ck_dname(uint8_t* dname, size_t dname_size, librpz_trig_t trig,
1811+	 uint8_t* qname, bool recursed, commreply_rpz_t* rpz)
1812+{
1813+	librpz_emsg_t emsg;
1814+
1815+	/* Refuse to check the root. */
1816+	if(dname_is_root(dname))
1817+		return rpz->st != st_servfail;
1818+
1819+	/* Loop to ignore disabled zones. */
1820+	do {
1821+		if(!push_st(rpz))
1822+			return false;
1823+		if(!librpz->ck_domain(&emsg, dname, dname_size, trig,
1824+				      rpz->hit_id, recursed, rpz->rsp)) {
1825+			log_fail(rpz, "%s", emsg.c);
1826+			return false;
1827+		}
1828+	} while(ck_after(qname, recursed, trig, rpz));
1829+
1830+	return rpz->st != st_servfail;
1831+}
1832+
1833+
1834+/* Check the IPv4 or IPv6 addresses for one NS name. */
1835+static bool				/* false=st_servfail */
1836+ck_1nsip(uint8_t* nsname, size_t nsname_size, int family, int qtype,
1837+	 bool* have_ns, commreply_rpz_t* rpz, struct module_env* env)
1838+{
1839+	struct ub_packed_rrset_key* akey;
1840+
1841+	akey = rrset_cache_lookup(env->rrset_cache, nsname, nsname_size,
1842+				  qtype, LDNS_RR_CLASS_IN, 0, 0, 0);
1843+	if(akey) {
1844+		*have_ns = true;
1845+
1846+		if(!ck_ip_rrset(akey->entry.data, family, LIBRPZ_TRIG_NSIP,
1847+				nsname, rpz)) {
1848+			lock_rw_unlock(&akey->entry.lock);
1849+			return false;
1850+		}
1851+		lock_rw_unlock(&akey->entry.lock);
1852+	}
1853+	return true;
1854+}
1855+
1856+
1857+static bool				/* false=st_servfail */
1858+ck_qname(uint8_t* qname, size_t qname_len,
1859+	 bool recursed,			/* recursion done */
1860+	 bool wait_ns,			/* willing to iterate for NS data */
1861+	 commreply_rpz_t* rpz, struct module_env* env)
1862+{
1863+	uint8_t* dname;
1864+	size_t dname_size;
1865+	int cur_lab;
1866+	struct ub_packed_rrset_key* nskey;
1867+	const struct packed_rrset_data* nsdata;
1868+	uint8_t* nsname;
1869+	size_t nsname_size;
1870+	uint rr_n;
1871+	bool have_ns, tried_ns;
1872+
1873+	if(!ck_dname(qname, qname_len, LIBRPZ_TRIG_QNAME, qname, false, rpz))
1874+		return false;
1875+
1876+	/* Do not waste time looking for NSDNAME and NSIP hits when there
1877+	 * are no currently relevant triggers. */
1878+	if(!librpz->have_ns_trig(rpz->rsp))
1879+		return true;
1880+
1881+	have_ns = false;
1882+	tried_ns = false;
1883+	dname = qname;
1884+	dname_size = qname_len;
1885+	for(cur_lab = dname_count_labels(dname) - 2;
1886+	    cur_lab > rpz->min_ns_dots;
1887+	    --cur_lab) {
1888+		tried_ns = true;
1889+		dname_remove_label(&dname, &dname_size);
1890+		nskey = rrset_cache_lookup(env->rrset_cache, dname, dname_size,
1891+					   LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN,
1892+					   0, 0, 0);
1893+		if(!nskey)
1894+			continue;
1895+
1896+		nsdata = (const struct packed_rrset_data*)nskey->entry.data;
1897+		for(rr_n = 0;
1898+		    rr_n < nsdata->count && rpz->st == st_unknown;
1899+		    ++rr_n) {
1900+			nsname = nsdata->rr_data[rr_n]+2;
1901+			nsname_size = nsdata->rr_len[rr_n];
1902+			if(nsname_size <= 2)
1903+				continue;
1904+			nsname_size -= 2;
1905+			if(!ck_dname(nsname, nsname_size, LIBRPZ_TRIG_NSDNAME,
1906+				     qname, recursed, rpz))
1907+				return false;
1908+			if(!ck_1nsip(nsname, nsname_size, AF_INET,
1909+				      LDNS_RR_TYPE_A, &have_ns, rpz, env))
1910+				return false;
1911+			if(!ck_1nsip(nsname, nsname_size, AF_INET6,
1912+				      LDNS_RR_TYPE_AAAA, &have_ns, rpz, env))
1913+				return false;
1914+		}
1915+		lock_rw_unlock(&nskey->entry.lock);
1916+	}
1917+
1918+	/* If we failed to find NS records, then stop building the response
1919+	 * before a CNAME with this owner name. */
1920+	if(!have_ns && tried_ns && (!recursed || wait_ns)) {
1921+		rpz->cname_hit.size = qname_len;
1922+		RPZ_ASSERT(rpz->cname_hit.size <= sizeof(rpz->cname_hit.d));
1923+		memcpy(rpz->cname_hit.d, qname, qname_len);
1924+		rpz->result.hit_id = rpz->hit_id;
1925+		rpz->st = st_ck_ns;
1926+	}
1927+	return true;
1928+}
1929+
1930+
1931+/*
1932+ * Are we ready to rewrite the response?
1933+ */
1934+static bool				/* true=send rewritten response */
1935+ck_result(uint8_t* qname, bool recursed,
1936+	  commreply_rpz_t* rpz, const struct comm_point* commpoint)
1937+{
1938+	librpz_emsg_t emsg;
1939+
1940+	switch(rpz->st) {
1941+	case st_off:
1942+	case st_servfail:
1943+	case st_rewritten:
1944+		return false;
1945+	case st_unknown:
1946+		break;
1947+	case st_iterate:
1948+		return false;
1949+	case st_ck_ns:
1950+		/* An NSDNAME or NSIP check failed for lack of cached data. */
1951+		return false;
1952+#pragma clang diagnostic push
1953+#pragma clang diagnostic ignored "-Wunreachable-code"
1954+	default:
1955+		fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
1956+			   rpz->st);
1957+#pragma clang diagnostic pop
1958+	}
1959+
1960+	/* Wait for a trigger. */
1961+	if(rpz->result.policy == LIBRPZ_POLICY_UNDEFINED) {
1962+		if(recursed &&
1963+		    rpz->result.zpolicy != LIBRPZ_POLICY_UNDEFINED &&
1964+		    !librpz->rsp_result(&emsg, &rpz->result, true, rpz->rsp)) {
1965+			log_fail(rpz, "%s", emsg.c);
1966+			return false;
1967+		}
1968+		if(rpz->result.policy == LIBRPZ_POLICY_UNDEFINED)
1969+			return false;
1970+	}
1971+
1972+	if(rpz->result.policy == LIBRPZ_POLICY_PASSTHRU) {
1973+		log_rewrite(qname, rpz->result.policy, "", rpz);
1974+		rpz_off(rpz, st_off);
1975+		return false;
1976+	}
1977+
1978+	/* The TCP-only policy answers UDP requests with truncated responses. */
1979+	if(rpz->result.policy == LIBRPZ_POLICY_TCP_ONLY &&
1980+	   commpoint->type == comm_tcp) {
1981+		rpz_off(rpz, st_off);
1982+		return false;
1983+	}
1984+
1985+	return true;
1986+}
1987+
1988+
1989+/*
1990+ * Convert an RPZ hit to a struct dns_msg
1991+ */
1992+static void
1993+get_result_msg(struct dns_msg** dnsmsg, struct query_info* qinfo,
1994+	       uint16_t id, uint16_t flags, bool recursed, commreply_rpz_t* rpz,
1995+	       struct comm_point* commpoint, struct regional* region)
1996+{
1997+	librpz_rr_t* rr;
1998+	librpz_domain_buf_t origin;
1999+	struct sldns_buffer* pkt;
2000+	uint16_t num_rrs;
2001+	librpz_emsg_t emsg;
2002+
2003+	*dnsmsg = NULL;
2004+	if(!ck_result(qinfo->qname, recursed, rpz, commpoint))
2005+		return;
2006+
2007+	rpz->st = st_rewritten;
2008+
2009+	if(rpz->result.policy == LIBRPZ_POLICY_DROP) {
2010+		log_rewrite(qinfo->qname, rpz->result.policy, "", rpz);
2011+		/* Make a fake cached message to carry
2012+		 * sec_status_rpz_drop and be dropped. */
2013+		error_encode(commpoint->buffer, LDNS_RCODE_NOERROR,
2014+			     qinfo, id, flags, NULL);
2015+		pkt2dns_msg(dnsmsg, commpoint->buffer, rpz, region);
2016+		(*dnsmsg)->rep->security = sec_status_rpz_drop;
2017+		return;
2018+	}
2019+
2020+	/* Create a DNS message of the RPZ data.
2021+	 * In many cases that message could be sent directly to the DNS client,
2022+	 * but sometimes iteration must be used to resolve a CNAME.
2023+	 * This need not be fast, because rewriting responses should be rare.
2024+	 * Therefore, use the simpler but slower tactic of generating a
2025+	 * parsed  version of the message. */
2026+
2027+	flags &= ~BIT_AA;
2028+	flags |= BIT_QR | BIT_RA;
2029+	rr = NULL;
2030+
2031+	/* The TCP-only policy answers UDP requests with truncated responses. */
2032+	if(rpz->result.policy == LIBRPZ_POLICY_TCP_ONLY) {
2033+		flags |= BIT_TC;
2034+
2035+	} else if(rpz->result.policy == LIBRPZ_POLICY_NXDOMAIN) {
2036+		flags |= LDNS_RCODE_NXDOMAIN;
2037+
2038+	} else if(rpz->result.policy == LIBRPZ_POLICY_CNAME) {
2039+		if(!rpz->iterating &&
2040+		   qinfo->qtype != LDNS_RR_TYPE_CNAME) {
2041+			/* The new DNS message would be a CNAME and
2042+			 * the external request was not for a CNAME.
2043+			 * The worker must punt to the iterator so that
2044+			 * the iterator can resolve the CNAME. */
2045+			rpz->st = st_iterate;
2046+			return;
2047+		}
2048+		next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
2049+
2050+	} else if(rpz->result.policy == LIBRPZ_POLICY_RECORD ||
2051+		  rpz->result.policy == LIBRPZ_POLICY_NODATA) {
2052+		next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
2053+		/* Punt to the iterator if the new DNS message would
2054+		 * be a CNAME that must be resolved. */
2055+		if(!rpz->iterating &&
2056+		   qinfo->qtype != LDNS_RR_TYPE_CNAME &&
2057+		   rr && rr->type == ntohs(LDNS_RR_TYPE_CNAME)) {
2058+			free(rr);
2059+			rpz->st = st_iterate;
2060+			return;
2061+		}
2062+	}
2063+	log_rewrite(qinfo->qname, rpz->result.policy, "", rpz);
2064+
2065+	/* Make a buffer containing a DNS message with the RPZ data. */
2066+	pkt = commpoint->buffer;
2067+	sldns_buffer_clear(pkt);
2068+	if(sldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE) {
2069+		log_fail(rpz, "comm_reply buffer too small for header");
2070+		if(rr)
2071+			free(rr);
2072+		return;
2073+	}
2074+
2075+	/* Install ID, flags, QDCOUNT=1, ANCOUNT=# of RPZ RRs, NSCOUNT=0,
2076+	 * and ARCOUNT=1 for the RPZ SOA. */
2077+	sldns_buffer_write_u16(pkt, id);
2078+	sldns_buffer_write_u16(pkt, flags);
2079+	sldns_buffer_write_u16(pkt, 1);	/* QDCOUNT */
2080+	sldns_buffer_write_u16(pkt, 0);	/* ANCOUNT will be set later */
2081+	sldns_buffer_write_u16(pkt, 0);	/* NSCOUNT */
2082+	sldns_buffer_write_u16(pkt, 1);	/* ARCOUNT */
2083+
2084+	/* Install the question with the LDNS_RR_CLASS_RPZ bit to
2085+	 * to distinguish this supposed cache entry from the real deal. */
2086+	sldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len);
2087+	sldns_buffer_write_u16(pkt, qinfo->qtype);
2088+	sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_IN);
2089+
2090+	/* Install the RPZ RRs in the answer section */
2091+	num_rrs = 0;
2092+	while(rr) {
2093+		/* Include only the requested RRs. */
2094+		if(qinfo->qtype == LDNS_RR_TYPE_ANY ||
2095+		   rr->type == htons(qinfo->qtype) ||
2096+		   rr->type == htons(LDNS_RR_TYPE_CNAME)) {
2097+			if(!add_rr(pkt, qinfo->qname, qinfo->qname_len,
2098+				   rr, rpz))
2099+				return;
2100+
2101+			++num_rrs;
2102+		}
2103+		free(rr);
2104+
2105+		next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
2106+	}
2107+	/* Finish ANCOUNT. */
2108+	if(num_rrs != 0)
2109+		sldns_buffer_write_u16_at(pkt, 6, num_rrs);
2110+
2111+	/* All rewritten responses have an identifying SOA record in the
2112+	 * additional section. */
2113+	if(!librpz->rsp_soa(&emsg, NULL, &rr, &origin,
2114+			    &rpz->result, rpz->rsp)) {
2115+		log_fail(rpz, "no soa");
2116+		return;
2117+	}
2118+	if(!add_rr(pkt, origin.d, origin.size, rr, rpz))
2119+		return;
2120+	free(rr);
2121+
2122+	/* Create a dns_msg representation of the fake incoming message. */
2123+	sldns_buffer_flip(pkt);
2124+	pkt2dns_msg(dnsmsg, pkt, rpz, region);
2125+}
2126+
2127+
2128+/* Check the RRs in the ANSWER section of a reply_info. */
2129+static void
2130+ck_reply(struct reply_info* reply, uint8_t* qname, bool wait_ns,
2131+	 commreply_rpz_t* rpz, struct module_env* env)
2132+{
2133+	struct ub_packed_rrset_key* rrset;
2134+	enum sldns_enum_rr_type type;
2135+	uint rrset_n;
2136+
2137+	/* Check the RRs in the ANSWER section. */
2138+	rpz->cname_hit.size = 0;
2139+	rpz->cname_hit_2nd = false;
2140+	for(rrset_n = 0; rrset_n < reply->an_numrrsets; ++rrset_n) {
2141+		/* Check all of the RRs before deciding. */
2142+		if(rpz->st != st_unknown)
2143+			return;
2144+
2145+		rrset = reply->rrsets[rrset_n];
2146+		if(ntohs(rrset->rk.rrset_class) != LDNS_RR_CLASS_IN)
2147+			continue;
2148+		type = ntohs(rrset->rk.type);
2149+
2150+		if(type == LDNS_RR_TYPE_A) {
2151+			if(!ck_ip_rrset(rrset->entry.data, AF_INET,
2152+					LIBRPZ_TRIG_IP, qname, rpz))
2153+				break;
2154+
2155+		} else if(type == LDNS_RR_TYPE_AAAA) {
2156+			if(!ck_ip_rrset(rrset->entry.data, AF_INET6,
2157+					LIBRPZ_TRIG_IP, qname, rpz))
2158+				break;
2159+
2160+		} else if(type == LDNS_RR_TYPE_CNAME) {
2161+			/* Check CNAME owners unless we already have a hit. */
2162+			++rpz->hit_id;
2163+			if(!ck_qname(rrset->rk.dname, rrset->rk.dname_len,
2164+				     true, wait_ns, rpz, env))
2165+				break;
2166+
2167+			/* Do not worry about the CNAME if it did not hit,
2168+			 * but note the miss so that it can be prepended
2169+			 * if we do hit. */
2170+			if(rpz->result.hit_id != rpz->hit_id) {
2171+				rpz->cname_hit_2nd = true;
2172+				continue;
2173+			}
2174+
2175+			/* Stop after hitting a CNAME.
2176+			 * The iterator must be used to include CNAMEs before
2177+			 * the CNAME that hit in the rewritten response. */
2178+			rpz->cname_hit.size = rrset->rk.dname_len;
2179+			RPZ_ASSERT(rpz->cname_hit.size <= sizeof(rpz->cname_hit.d));
2180+			memcpy(rpz->cname_hit.d, rrset->rk.dname,
2181+			       rpz->cname_hit.size);
2182+			break;
2183+		}
2184+	}
2185+}
2186+
2187+
2188+static void
2189+worker_servfail(struct worker* worker, struct query_info* qinfo,
2190+		uint16_t id, uint16_t flags, struct comm_reply* commreply)
2191+{
2192+	error_encode(commreply->c->buffer, LDNS_RCODE_SERVFAIL,
2193+		     qinfo, id, flags, NULL);
2194+	regional_free_all(worker->scratchpad);
2195+	comm_point_send_reply(commreply);
2196+}
2197+
2198+
2199+/* Send an RPZ answer before the iterator has started.
2200+ * @return: 1=continue normal unbound processing
2201+ *	    0=punt to the iterator
2202+ *	    -1=rewritten response already sent or dropped. */
2203+static int
2204+worker_send(struct dns_msg* dnsmsg, struct worker* worker,
2205+	    struct query_info* qinfo, uint16_t id, uint16_t flags,
2206+	    struct edns_data* edns, struct comm_reply* commreply)
2207+{
2208+	switch (commreply->rpz->st) {
2209+	case st_off:
2210+		return 1;
2211+	case st_servfail:
2212+		worker_servfail(worker, qinfo, id, flags, commreply);
2213+		return -1;
2214+	case st_unknown:
2215+		return 1;
2216+	case st_iterate:
2217+	case st_ck_ns:
2218+		return 0;		/* punt to the iterator */
2219+	case st_rewritten:
2220+		break;
2221+	default:
2222+		fatal_exit("impossible RPZ state %d in worker_send()",
2223+			   commreply->rpz->st);
2224+	}
2225+
2226+	if(dnsmsg->rep->security == sec_status_rpz_drop) {
2227+		regional_free_all(worker->scratchpad);
2228+		comm_point_drop_reply(commreply);
2229+		return -1;
2230+	}
2231+
2232+	edns->edns_version = EDNS_ADVERTISED_VERSION;
2233+	edns->udp_size = EDNS_ADVERTISED_SIZE;
2234+	edns->ext_rcode = 0;
2235+	edns->bits = 0;			/* rewritten response cannot verify. */
2236+	if(!reply_info_answer_encode(qinfo, dnsmsg->rep,
2237+				     id, flags | BIT_QR,
2238+				     commreply->c->buffer, 0, 1,
2239+				     worker->scratchpad,
2240+				     edns->udp_size, edns, 0, 0)) {
2241+		worker_servfail(worker, qinfo, id, flags, commreply);
2242+	} else {
2243+		regional_free_all(worker->scratchpad);
2244+		comm_point_send_reply(commreply);
2245+	}
2246+	return -1;
2247+}
2248+
2249+
2250+/* Set commreply to an RPZ context if the response might be rewritten.
2251+ * Try to answer now with a hit allowed before recursion (iteration). */
2252+bool					/* true=response sent or dropped */
2253+rpz_start(struct worker* worker, struct query_info* qinfo,
2254+	  struct comm_reply* commreply, struct edns_data* edns)
2255+{
2256+	commreply_rpz_t* rpz;
2257+	uint16_t id, flags;
2258+	struct dns_msg* dnsmsg;
2259+	int family;
2260+	const void* addr;
2261+	librpz_emsg_t emsg;
2262+
2263+	/* Quit if rpz not configured. */
2264+	if(!worker->daemon->rpz_client)
2265+		return false;
2266+
2267+	/* Rewrite only the Internet class */
2268+	if(qinfo->qclass != LDNS_RR_CLASS_IN)
2269+		return false;
2270+
2271+	rpz = commreply->rpz;
2272+	RPZ_ASSERT(!rpz);
2273+
2274+	dnsmsg = NULL;
2275+	id = htons(sldns_buffer_read_u16_at(commreply->c->buffer, 0));
2276+	flags = sldns_buffer_read_u16_at(commreply->c->buffer, 2);
2277+
2278+	rpz = malloc(sizeof(*rpz));
2279+	if(!rpz) {
2280+		librpz->log(LIBRPZ_LOG_ERROR, NULL, "no memory for rpz");
2281+		return 0 > worker_send(dnsmsg, worker, qinfo,
2282+				       id, flags, edns, commreply);
2283+	}
2284+	memset(rpz, 0, sizeof(*rpz));
2285+	rpz->st = st_unknown;
2286+	commreply->rpz = rpz;
2287+
2288+	/* Make a new ID for log messages */
2289+	rpz->log_id = __sync_add_and_fetch(&log_id, 1);
2290+
2291+	/* Get access to the librpz data. */
2292+	if(!librpz->rsp_create(&emsg, &rpz->rsp, &rpz->min_ns_dots,
2293+			      worker->daemon->rpz_client,
2294+			      (flags & BIT_RD) != 0,
2295+			      (edns->bits & EDNS_DO) != 0)) {
2296+		log_fail(rpz, "%s", emsg.c);
2297+		return false;
2298+	}
2299+	/* Quit if benign reasons prevent rewriting. */
2300+	if(!rpz->rsp) {
2301+		rpz->st = st_off;
2302+		librpz->log(LIBRPZ_LOG_TRACE1, rpz, "%s", emsg.c);
2303+		return false;
2304+	}
2305+
2306+	/* Check the client IP address.
2307+	 * Do not use commreply->srctype because it is often 0. */
2308+	family = ((struct sockaddr*)&commreply->addr)->sa_family;
2309+	switch(family) {
2310+	case AF_INET:
2311+		addr = &((struct sockaddr_in*)&commreply->addr)->sin_addr;
2312+		break;
2313+	case AF_INET6:
2314+		addr = &((struct sockaddr_in6*)&commreply->addr)->sin6_addr;
2315+		break;
2316+	default:
2317+		/* Maybe the client is on a UNIX domain socket. */
2318+		librpz->log(LIBRPZ_LOG_TRACE2, rpz,
2319+			    "unknown client address family %d", family);
2320+		addr = NULL;
2321+		break;
2322+	}
2323+	/* Loop to ignore disabled zones. */
2324+	while(addr) {
2325+		if(!push_st(rpz))
2326+			break;
2327+		if(!librpz->ck_ip(&emsg, addr, family, LIBRPZ_TRIG_CLIENT_IP,
2328+				  rpz->hit_id, true, rpz->rsp)) {
2329+			log_fail(rpz, "%s", emsg.c);
2330+			break;
2331+		}
2332+		if(!ck_after(qinfo->qname, false, LIBRPZ_TRIG_CLIENT_IP, rpz))
2333+			break;
2334+	}
2335+	if(rpz->st == st_servfail)
2336+		return 0 > worker_send(dnsmsg, worker, qinfo,
2337+				       id, flags, edns, commreply);
2338+
2339+	/* Check the QNAME and possibly replace a client-IP hit. */
2340+	ck_qname(qinfo->qname, qinfo->qname_len, false, true,
2341+		 rpz, &worker->env);
2342+
2343+	get_result_msg(&dnsmsg, qinfo, id, flags, false,
2344+		       rpz, commreply->c, worker->scratchpad);
2345+	return 0 > worker_send(dnsmsg, worker, qinfo,
2346+			       id, flags, edns, commreply);
2347+}
2348+
2349+
2350+/* Check a cached reply before iteration.
2351+ * @return: 1=use cache entry
2352+ *	    0=deny a cached entry exists in order to punt to the iterator
2353+ *	    -1=rewritten response already sent or dropped */
2354+int
2355+rpz_worker_cache(struct worker* worker, struct reply_info* reply,
2356+		 struct query_info* qinfo, uint16_t id, uint16_t flags,
2357+		 struct edns_data* edns, struct comm_reply* commreply)
2358+{
2359+	commreply_rpz_t* rpz;
2360+	struct dns_msg* dnsmsg;
2361+	st_t new_st;
2362+	librpz_rr_t* rr;
2363+
2364+	dnsmsg = NULL;
2365+
2366+	rpz = commreply->rpz;
2367+	switch(rpz->st) {
2368+	case st_off:
2369+		return 1;		/* Send the cache entry. */
2370+	case st_servfail:
2371+		return worker_send(dnsmsg, worker, qinfo, id, flags,
2372+				   edns, commreply);
2373+	case st_unknown:
2374+		break;
2375+	case st_iterate:
2376+	case st_ck_ns:
2377+		return 0;		/* Punt to the iterator. */
2378+	case st_rewritten:
2379+	default:
2380+		fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
2381+			   rpz->st);
2382+	}
2383+
2384+	/* Check the RRs in the ANSWER section. */
2385+	if(!push_st(rpz))
2386+		return worker_send(dnsmsg, worker, qinfo, id, flags, edns,
2387+				   commreply);
2388+
2389+	ck_reply(reply, qinfo->qname, true, rpz, &worker->env);
2390+	if(!ck_result(qinfo->qname, true, rpz, commreply->c))
2391+		return worker_send(dnsmsg, worker, qinfo, id, flags, edns,
2392+				   commreply);
2393+
2394+	if(rpz->cname_hit.size != 0) {
2395+		/* Punt to the iterator if leading CNAMEs must be
2396+		 * included in the rewritten response. */
2397+		rpz->cname_hit.size = 0;
2398+		new_st = st_iterate;
2399+
2400+	} else if(rpz->result.policy == LIBRPZ_POLICY_CNAME) {
2401+		/* Punt if the rewritten response is to a CNAME. */
2402+		new_st = st_iterate;
2403+
2404+	} else {
2405+		if(rpz->result.policy == LIBRPZ_POLICY_RECORD) {
2406+			next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
2407+			if(rr) {
2408+				/* Punt we are rewriting to a CNAME. */
2409+				if(rr->type == ntohs(LDNS_RR_TYPE_CNAME)) {
2410+					free(rr);
2411+					rpz->st = st_iterate;
2412+				} else {
2413+					free(rr);
2414+				}
2415+			}
2416+		}
2417+		get_result_msg(&dnsmsg, qinfo, id, flags, true,
2418+			       rpz, commreply->c, worker->scratchpad);
2419+		new_st = rpz->st;
2420+	}
2421+
2422+	switch(new_st) {
2423+	case st_off:
2424+	case st_servfail:
2425+		break;
2426+	case st_unknown:
2427+		pop_discard_st(rpz);
2428+		break;
2429+	case st_iterate:
2430+	case st_ck_ns:
2431+		if(pop_st(rpz))
2432+			rpz->st = new_st;
2433+		break;
2434+	case st_rewritten:
2435+		pop_discard_st(rpz);
2436+		break;
2437+	default:
2438+		fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
2439+			   rpz->st);
2440+	}
2441+
2442+	return worker_send(dnsmsg, worker, qinfo, id, flags, edns, commreply);
2443+}
2444+
2445+
2446+/* Check a cache hit or miss for the iterator.
2447+ * A cache miss can already have a QNAME hit that was ignored before checking
2448+ * the iterator because of "QNAME-WAIT-RECURSE yes".
2449+ * Cache hits are treated like responses from authorities. */
2450+bool					/* false=SERVFAIL */
2451+rpz_iter_cache(struct dns_msg** msg, enum response_type* type,
2452+	       struct module_qstate* qstate, struct iter_qstate* iq)
2453+{
2454+	struct comm_reply* commreply;
2455+	commreply_rpz_t* rpz;
2456+	struct dns_msg* dnsmsg;
2457+
2458+	commreply = &qstate->mesh_info->reply_list->query_reply;
2459+	rpz = commreply->rpz;
2460+
2461+	rpz->iterating = true;
2462+
2463+	switch(rpz->st) {
2464+	case st_off:
2465+		iq->rpz_rewritten = 1;	/* RPZ has nothing to say. */
2466+		return true;
2467+	case st_servfail:
2468+		return false;
2469+	case st_unknown:
2470+		break;
2471+	case st_iterate:
2472+	case st_ck_ns:
2473+		rpz->st = st_unknown;
2474+		if(!ck_qname(iq->qchase.qname, iq->qchase.qname_len,
2475+			     *msg != NULL, true, rpz, qstate->env))
2476+			return false;
2477+		/* If we must recurse regardless and if NSIP/NSDNAME
2478+		 * checking failed, then delay in the hope that
2479+		 * recursion will also get NS data. */
2480+		if(rpz->st == st_ck_ns)
2481+			return true;
2482+		break;
2483+	case st_rewritten:
2484+	default:
2485+		fatal_exit("impossible RPZ state %d in rpz_iter_cache()",
2486+			   rpz->st);
2487+	}
2488+
2489+	push_st(rpz);
2490+
2491+	/* Check the cache hit. */
2492+	if(*msg)
2493+		ck_reply((*msg)->rep, iq->qchase.qname, true, rpz, qstate->env);
2494+
2495+	/* The DNS ID does not matter, because the generated dns_msg
2496+	 * is nominally from an authority and not to the DNS client. */
2497+	get_result_msg(&dnsmsg, &iq->qchase, 1, qstate->query_flags, true,
2498+		       rpz, commreply->c, qstate->region);
2499+
2500+	switch(rpz->st) {
2501+	case st_off:
2502+		iq->rpz_rewritten = 1;	/* RPZ has nothing to say. */
2503+		return true;
2504+	case st_servfail:
2505+		return false;
2506+	case st_unknown:
2507+		/* RPZ has nothing to say yet.  Maybe there will be a hit
2508+		 * later in the CNAME chain. */
2509+		return pop_discard_st(rpz);
2510+	case st_ck_ns:
2511+		/* Try to get NS data for a CNAME found by ck_reply() */
2512+		*type = RESPONSE_TYPE_CNAME;
2513+		return pop_discard_st(rpz);
2514+	case st_iterate:
2515+	default:
2516+		fatal_exit("impossible RPZ state %d in rpz_iter_cache()",
2517+			   rpz->st);
2518+	case st_rewritten:
2519+		break;
2520+	}
2521+
2522+	if(*msg && rpz->cname_hit.size != 0 && rpz->cname_hit_2nd) {
2523+		/* We hit a CNAME owner in the cached msg after not hitting one
2524+		 * or more CNAME owners.  We need to add those leading CNAMEs
2525+		 * to the prepend list.  Tell the iterator to treat the cached
2526+		 * message as a RESPONSE_TYPE_CNAME even if it contains answers.
2527+		 * handle_cname_response() will stop prepending CNAMEs before
2528+		 * the triggering CNAME.  handle_cname_response() will cause
2529+		 * a restart to resolve the target of the preceding CNAME,
2530+		 * which is the same as the hit CNAME owner. */
2531+		rpz->st = st_unknown;
2532+		*type = RESPONSE_TYPE_CNAME;
2533+		return pop_discard_st(rpz);
2534+	}
2535+
2536+	*msg = dnsmsg;
2537+	iq->rpz_security = dnsmsg->rep->security;
2538+
2539+	if(dnsmsg && dnsmsg->rep->an_numrrsets != 0 &&
2540+	   dnsmsg->rep->rrsets[0]->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
2541+		/* The cached msg triggered a rule that rewrites to a
2542+		 * CNAME that must be resolved.
2543+		 * We have a replacement dns_msg with that CNAME and also
2544+		 * an SOA RR in the ADDITIONAL section that the iterator
2545+		 * will lose as it adds the CNAME to the prepend list.
2546+		 * Save the SOA RR in iq->rpz_soa. */
2547+		iq->rpz_soa = dnsmsg->rep->rrsets[1];
2548+		iq->rpz_rewritten = 1;
2549+		*type = RESPONSE_TYPE_CNAME;
2550+		return true;
2551+	}
2552+
2553+	/* Otherwise we have rewritten to zero or more non-CNAME RRs.
2554+	 * (DNAMEs are not supported.)
2555+	 * Tell the iterator to send the rewritten message. */
2556+	*type = RESPONSE_TYPE_ANSWER;
2557+	iq->rpz_rewritten = 1;
2558+	return true;
2559+}
2560+
2561+
2562+/* Check a RESPONSE_TYPE_ANSWER response from an authority in the iterator. */
2563+rpz_iter_resp_t
2564+rpz_iter_resp(struct module_qstate* qstate, struct iter_qstate* iq,
2565+	      struct dns_msg** resp, bool* is_cname)
2566+{
2567+	struct comm_reply* commreply;
2568+	commreply_rpz_t* rpz;
2569+	struct reply_info* rep;
2570+
2571+	*is_cname = false;
2572+
2573+	commreply = &qstate->mesh_info->reply_list->query_reply;
2574+	rpz = commreply->rpz;
2575+	switch(rpz->st) {
2576+	case st_off:
2577+	case st_servfail:
2578+	case st_iterate:
2579+	case st_rewritten:
2580+	default:
2581+		fatal_exit("impossible RPZ state %d in rpz_iter_resp()",
2582+			   rpz->st);
2583+	case st_ck_ns:
2584+	case st_unknown:
2585+		break;
2586+	}
2587+
2588+	/* We know !iq->rpz_rewritten and so the response was after a simple
2589+	 * cache miss when the original QNAME did not trigger a response
2590+	 * or after a CNAME whose owner name did hit but was then forgotten
2591+	 * with pop_st().
2592+	 * In either case, it is necessary to check the QNAME here.
2593+	 * Checking the QNAME will not lose a better hit. */
2594+	rpz->st = st_unknown;
2595+	ck_qname(iq->qchase.qname, iq->qchase.qname_len, true, false,
2596+		 rpz, qstate->env);
2597+
2598+	/* Check the RRs in the ANSWER section. */
2599+	if(!push_st(rpz))
2600+		return rpz_iter_resp_fail;
2601+	ck_reply(iq->response->rep, iq->qchase.qname, false, rpz, qstate->env);
2602+	get_result_msg(resp, &qstate->qinfo, 1, qstate->query_flags, true,
2603+		       rpz, commreply->c, qstate->region);
2604+	switch(rpz->st) {
2605+	case st_off:
2606+		iq->rpz_rewritten = 1;	/* Do not come back. */
2607+		return rpz_iter_resp_done;
2608+	case st_servfail:		/* Send SERVFAIL */
2609+		return rpz_iter_resp_fail;
2610+	case st_unknown:
2611+	case st_ck_ns:
2612+		return rpz_iter_resp_done;  /* continue without change */
2613+	case st_iterate:
2614+	default:
2615+		fatal_exit("impossible RPZ state %d in rpz_iter_resp()",
2616+			   rpz->st);
2617+	case st_rewritten:
2618+		/* Tell the iterator to use handle_cname_response() to
2619+		 * prepend any preceding CNAMEs.
2620+		 * We have a replacement dns_msg that also has an SOA RR in the
2621+		 * ADDITIONAL section that the iterator will lose if it is a
2622+		 * CNAME.  Save that SOA in that case. */
2623+		rep = (*resp)->rep;
2624+		if(rep->an_numrrsets != 0 &&
2625+		   rep->rrsets[0]->rk.type == ntohs(LDNS_RR_TYPE_CNAME)) {
2626+			*is_cname = true;
2627+			iq->rpz_soa = rep->rrsets[1];
2628+		}
2629+		return rpz_iter_resp_rewrite;
2630+	}
2631+}
2632+
2633+
2634+/* Tell handle_cname_response() to stop adding to the answer prepend list
2635+ * after adding CNAME with a target that hits a QNAME trigger.
2636+ * Do not change any RPZ state, but expect the call of handle_cname_response()
2637+ * to try to resolve the CNAME and hit the same QNAME trigger and rewrite
2638+ * the response. */
2639+rpz_cname_t
2640+rpz_cname(struct module_qstate* qstate,
2641+	  uint8_t* oname, size_t oname_size)
2642+{
2643+	struct mesh_reply* reply_list;
2644+	struct comm_reply* commreply;
2645+	commreply_rpz_t* rpz;
2646+	rpz_cname_t ret;
2647+
2648+	/* Quit if RPZ is off */
2649+	reply_list = qstate->mesh_info->reply_list;
2650+	if(!reply_list)
2651+		return rpz_cname_prepend;
2652+	commreply = &reply_list->query_reply;
2653+	rpz = commreply->rpz;
2654+
2655+	if(!rpz || rpz->st == st_off)
2656+		return rpz_cname_prepend;
2657+
2658+	/* Stop on a 2nd or later CNAME for rpz_iter_resp(). */
2659+	if(rpz->cname_hit.size != 0) {
2660+		if(!query_dname_compare(rpz->cname_hit.d, oname))
2661+			return rpz_cname_stop;
2662+		return rpz_cname_prepend;
2663+	}
2664+
2665+	if(rpz->st != st_unknown)
2666+		fatal_exit("impossible RPZ state %d in rpz_cname()", rpz->st);
2667+
2668+	ret = rpz_cname_prepend;
2669+	if(!push_st(rpz))
2670+		return rpz_cname_fail;
2671+	/* Stop before prepending a CNAME that would preempt a
2672+	 * rewritten response or before a possible NSDNAME or NSIP trigger. */
2673+	++rpz->hit_id;
2674+	ck_qname(oname, oname_size, true, true, rpz, qstate->env);
2675+	if(rpz->st != st_unknown)
2676+		ret = rpz_cname_stop;
2677+	if(!pop_st(rpz))
2678+		return rpz_cname_fail;
2679+	return ret;
2680+}
2681+
2682+#endif /* ENABLE_FASTRPZ */
2683Index: unbound-1.7.0~rc1/fastrpz/rpz.h
2684===================================================================
2685--- /dev/null
2686+++ unbound-1.7.0~rc1/fastrpz/rpz.h
2687@@ -0,0 +1,138 @@
2688+/*
2689+ * fastrpz/rpz.h - interface to the fastrpz response policy zone library
2690+ *
2691+ * Copyright (c) 2016 Farsight Security, Inc.
2692+ *
2693+ * Licensed under the Apache License, Version 2.0 (the "License");
2694+ * you may not use this file except in compliance with the License.
2695+ * You may obtain a copy of the License at
2696+ *	http://www.apache.org/licenses/LICENSE-2.0
2697+ *
2698+ * Unless required by applicable law or agreed to in writing, software
2699+ * distributed under the License is distributed on an "AS IS" BASIS,
2700+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2701+ * See the License for the specific language governing permissions and
2702+ * limitations under the License.
2703+ */
2704+
2705+#ifndef UNBOUND_FASTRPZ_RPZ_H
2706+#define UNBOUND_FASTRPZ_RPZ_H
2707+
2708+#ifndef PACKAGE_VERSION
2709+/* Ensure that config.h has been included to correctly set ENABLE_FASTRPZ */
2710+#include "config.h"
2711+#endif
2712+
2713+#ifdef ENABLE_FASTRPZ
2714+
2715+#include "librpz.h"
2716+
2717+#include "daemon/daemon.h"
2718+#include "util/config_file.h"
2719+
2720+struct comm_point;			/* forward references */
2721+struct comm_reply;
2722+struct dns_msg;
2723+struct edns_data;
2724+struct iter_qstate;
2725+struct query_info;
2726+struct reply_info;
2727+enum response_type;			/* iterator/iter_utils.h */
2728+
2729+
2730+struct commreply_rpz;
2731+
2732+/**
2733+ * Connect to the librpz database.
2734+ * @param pclist: future pointer to opaque librpz client data
2735+ * @param pclient: future pointer to opaque librpz client data
2736+ * @param cfg: parsed unbound configuration
2737+ */
2738+void rpz_init(librpz_clist_t** pclist, librpz_client_t** pclient,
2739+	      const struct config_file* cfg);
2740+
2741+/**
2742+ * Disconnect from the librpz database
2743+ * @param client: opaque librpz client data
2744+ */
2745+void rpz_delete(librpz_clist_t** pclist, librpz_client_t** pclient);
2746+
2747+/**
2748+ * Start working on a DNS request and check for client IP address triggers.
2749+ * @param worker: the DNS request context
2750+ * @param qinfo: the DNS question
2751+ * @param[in,out] commreply: the answer
2752+ * @param c: where to send the response
2753+ * @param[in,out] edns for the DO flag
2754+ * @return true if response already sent or dropped
2755+ */
2756+bool rpz_start(struct worker* worker, struct query_info* qinfo,
2757+	       struct comm_reply* commreply, struct edns_data* edns);
2758+
2759+/**
2760+ * Release resources held for a DNS request
2761+ * @param rspp: pointer to pointer to rpz client context.
2762+ */
2763+void rpz_end(struct comm_reply* comm_rep);
2764+
2765+/**
2766+ * Check a cached reply for RPZ hits before iteration
2767+ * @param worker: the DNS request context
2768+ * @param casheresp: cache reply
2769+ * @param qinfo: the DNS question
2770+ * @param id from the DNS request
2771+ * @param flags from the DNS request
2772+ * @param[in,out] edns for the DO flag
2773+ * @param[in,out] commreply: RPZ state
2774+ * @return 1=use cache entry, -1=rewritten response already sent or dropped,
2775+ *	0=deny a cached entry exists
2776+ */
2777+int rpz_worker_cache(struct worker* worker, struct reply_info* cacheresp,
2778+		     struct query_info* qinfo, uint16_t id, uint16_t flags,
2779+		     struct edns_data* edns, struct comm_reply* commreply);
2780+
2781+/**
2782+ * Check for an existing RPZ CNAME rewrite with "QNAME-WAIT-RECURSE no"
2783+ * that needs to be resolved before resolving the external request.
2784+ * @param[out] msg: rewritten CNAME response.
2785+ * @param qstate: query state.
2786+ * @param iq: iterator query state.
2787+ * @return false=send SERVFAIL
2788+ */
2789+bool rpz_iter_cache(struct dns_msg** msg, enum response_type* type,
2790+		    struct module_qstate* qstate, struct iter_qstate* iq);
2791+
2792+/**
2793+ * Check a response from an authority in the iterator.
2794+ * @param[out] type: of the final response
2795+ * @param qstate: query state.
2796+ * @param iq: iterator query state.
2797+ * @param is_cname: true if the rewritten response is a CNAME
2798+ * @return one of rpz_resp_t
2799+ */
2800+typedef enum {
2801+	rpz_iter_resp_fail,		/* Send SERVFAIL. */
2802+	rpz_iter_resp_rewrite,		/* We rewrote the response. */
2803+	rpz_iter_resp_done,		/* Restart to refetch glue. */
2804+} rpz_iter_resp_t;
2805+rpz_iter_resp_t rpz_iter_resp(struct module_qstate* qstate,
2806+			      struct iter_qstate* iq, struct dns_msg** resp,
2807+			      bool* is_cname);
2808+
2809+/**
2810+ * Check a CNAME RR
2811+ * @param qstate: query state.
2812+ * @param oname: cname owner name
2813+ * @param oname_size: length of oname
2814+ * @return: one of rpz_cname_t
2815+ */
2816+typedef enum {
2817+	rpz_cname_fail,			/* send SERVFAIL */
2818+	rpz_cname_prepend,		/* prepend CNAME as usual */
2819+	rpz_cname_stop,			/* stop before prepending this CNAME */
2820+} rpz_cname_t;
2821+rpz_cname_t rpz_cname(struct module_qstate* qstate,
2822+		      uint8_t* oname, size_t oname_size);
2823+
2824+#endif /* ENABLE_FASTRPZ */
2825+#endif /* UNBOUND_FASTRPZ_RPZ_H */
2826Index: unbound-1.7.0~rc1/fastrpz/rpz.m4
2827===================================================================
2828--- /dev/null
2829+++ unbound-1.7.0~rc1/fastrpz/rpz.m4
2830@@ -0,0 +1,64 @@
2831+# fastrpz/rpz.m4
2832+
2833+# ck_FASTRPZ
2834+# --------------------------------------------------------------------------
2835+# check for Fastrpz
2836+#   --enable-fastrpz	    enable Fastrpz response policy zones
2837+#   --enable-fastrpz-dl	    Fastrpz delayed link [default=have dlopen]
2838+#   --with-fastrpz-dir	    directory containing librpz.so
2839+#
2840+# Fastrpz can be compiled into Unbound everywhere with a reasonably
2841+# modern C compiler.  It is enabled on systems with dlopen() and librpz.so.
2842+
2843+AC_DEFUN([ck_FASTRPZ],
2844+[
2845+  fastrpz_avail=yes
2846+  AC_MSG_CHECKING([for librpz __attribute__s])
2847+    AC_TRY_COMPILE(,[
2848+	extern void f(char *p __attribute__((unused)), ...)
2849+	__attribute__((format(printf,1,2))) __attribute__((__noreturn__));],
2850+      librpz_have_attr=yes
2851+        AC_DEFINE([LIBRPZ_HAVE_ATTR], 1, [have __attribute__s used in librpz.h])
2852+        AC_MSG_RESULT([yes]),
2853+      librpz_have_attr=no
2854+        AC_MSG_RESULT([no]))
2855+
2856+  AC_SEARCH_LIBS(dlopen, dl)
2857+  librpz_dl=yes
2858+  AC_CHECK_FUNCS(dlopen dlclose dlsym,,librpz_dl=no)
2859+  AC_ARG_ENABLE([fastrpz-dl],
2860+    [  --enable-fastrpz-dl	  Fastrpz delayed link [[default=$librpz_dl]]],
2861+    [enable_librpz_dl="$enableval"],
2862+    [enable_librpz_dl="$librpz_dl"])
2863+  AC_ARG_WITH([fastrpz-dir],
2864+    [  --with-fastrpz-dir	  directory containing librpz.so],
2865+    [librpz_path="$withval/librpz.so"], [librpz_path="librpz.so"])
2866+  AC_DEFINE_UNQUOTED([FASTRPZ_LIBRPZ_PATH], ["$librpz_path"],
2867+    [fastrpz librpz.so])
2868+  if test "x$enable_librpz_dl" = "xyes"; then
2869+    fastrpz_lib_open=2
2870+  else
2871+    fastrpz_lib_open=1
2872+    # Add librpz.so to linked libraries if we are not using dlopen()
2873+    AC_SEARCH_LIBS([librpz_client_create], [rpz], [],
2874+      [fastrpz_lib_open=0
2875+        fastrpz_avail=no])
2876+  fi
2877+  AC_DEFINE_UNQUOTED([FASTRPZ_LIB_OPEN], [$fastrpz_lib_open],
2878+    [0=no fastrpz  1=static link  2=dlopen()])
2879+
2880+  AC_ARG_ENABLE([fastrpz],
2881+    AS_HELP_STRING([--enable-fastrpz],[enable Fastrpz response policy zones]),
2882+    [enable_fastrpz=$enableval],[enable_fastrpz=$fastrpz_avail])
2883+  if test "x$enable_fastrpz" = xyes; then
2884+    AC_DEFINE([ENABLE_FASTRPZ], [1], [Enable fastrpz])
2885+    if test "x$fastrpz_lib_open" = "x0"; then
2886+      AC_MSG_ERROR([[dlopen and librpz.so needed for fastrpz]])
2887+    fi
2888+    # used in Makefile.in
2889+    AC_SUBST([FASTRPZ_SRC], [fastrpz/rpz.c])
2890+    AC_SUBST([FASTRPZ_OBJ], [rpz.lo])
2891+  elif test "x$fastrpz_avail" = "x0"; then
2892+    AC_MSG_WARN([[dlopen and librpz.so needed for fastrpz]])
2893+  fi
2894+])
2895Index: unbound-1.7.0~rc1/iterator/iterator.c
2896===================================================================
2897--- unbound-1.7.0~rc1.orig/iterator/iterator.c
2898+++ unbound-1.7.0~rc1/iterator/iterator.c
2899@@ -68,6 +68,9 @@
2900 #include "sldns/str2wire.h"
2901 #include "sldns/parseutil.h"
2902 #include "sldns/sbuffer.h"
2903+#ifdef ENABLE_FASTRPZ
2904+#include "fastrpz/rpz.h"
2905+#endif
2906
2907 int
2908 iter_init(struct module_env* env, int id)
2909@@ -511,6 +514,23 @@ handle_cname_response(struct module_qsta
2910 		if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME &&
2911 			query_dname_compare(*mname, r->rk.dname) == 0 &&
2912 			!iter_find_rrset_in_prepend_answer(iq, r)) {
2913+#ifdef ENABLE_FASTRPZ
2914+			/* Stop adding CNAME rrsets to the prepend list
2915+			 * before defining an RPZ hit. */
2916+			if(!iq->rpz_rewritten) {
2917+				switch (rpz_cname(qstate, *mname, *mname_len)) {
2918+				case rpz_cname_fail:
2919+					/* send SERVFAIL */
2920+					return 0;
2921+				case rpz_cname_prepend:
2922+					/* save the CNAME. */
2923+					break;
2924+				case rpz_cname_stop:
2925+					/* Pause before adding the CNAME. */
2926+					goto stop_short;
2927+				}
2928+			}
2929+#endif
2930 			/* Add this relevant CNAME rrset to the prepend list.*/
2931 			if(!iter_add_prepend_answer(qstate, iq, r))
2932 				return 0;
2933@@ -519,6 +539,9 @@ handle_cname_response(struct module_qsta
2934
2935 		/* Other rrsets in the section are ignored. */
2936 	}
2937+#ifdef ENABLE_FASTRPZ
2938+stop_short: ;
2939+#endif
2940 	/* add authority rrsets to authority prepend, for wildcarded CNAMEs */
2941 	for(i=msg->rep->an_numrrsets; i<msg->rep->an_numrrsets +
2942 		msg->rep->ns_numrrsets; i++) {
2943@@ -1148,6 +1171,7 @@ processInitRequest(struct module_qstate*
2944 	uint8_t* delname;
2945 	size_t delnamelen;
2946 	struct dns_msg* msg = NULL;
2947+	enum response_type type;
2948
2949 	log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
2950 	/* check effort */
2951@@ -1223,8 +1247,7 @@ processInitRequest(struct module_qstate*
2952 	}
2953 	if(msg) {
2954 		/* handle positive cache response */
2955-		enum response_type type = response_type_from_cache(msg,
2956-			&iq->qchase);
2957+		type = response_type_from_cache(msg, &iq->qchase);
2958 		if(verbosity >= VERB_ALGO) {
2959 			log_dns_msg("msg from cache lookup", &msg->qinfo,
2960 				msg->rep);
2961@@ -1232,7 +1255,22 @@ processInitRequest(struct module_qstate*
2962 				(int)msg->rep->ttl,
2963 				(int)msg->rep->prefetch_ttl);
2964 		}
2965+#ifdef ENABLE_FASTRPZ
2966+	}
2967+	/* Check for an RPZ hit in the cached DNS message or an existing
2968+	 * RPZ CNAME rewrite that can be resolved now after a hit on the QNAME
2969+	 * or client IP address.  This can involve a creating a fake cache
2970+	 * hit. It can also involve overriding an RESPONSE_TYPE_ANSWER
2971+	 * result from response_type_from_cache().  Or it can ignore
2972+	 * the cached result to refetch glue. */
2973+	if(!iq->rpz_rewritten &&
2974+	   qstate->mesh_info->reply_list &&
2975+	   qstate->mesh_info->reply_list->query_reply.rpz &&
2976+	   !rpz_iter_cache(&msg, &type, qstate, iq))
2977+		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
2978
2979+	if(msg) {
2980+#endif
2981 		if(type == RESPONSE_TYPE_CNAME) {
2982 			uint8_t* sname = 0;
2983 			size_t slen = 0;
2984@@ -2552,6 +2590,62 @@ processQueryResponse(struct module_qstat
2985 			sock_list_insert(&qstate->reply_origin,
2986 				&qstate->reply->addr, qstate->reply->addrlen,
2987 				qstate->region);
2988+#ifdef ENABLE_FASTRPZ
2989+		/* Check the response for an RPZ hit. The response has already
2990+		 * been saved in the cache.  This should have the same effect
2991+		 * as finding that response in the cache.
2992+		 * We have already used rpz_iter_cache() at least once. */
2993+		if(!iq->rpz_rewritten &&
2994+		   qstate->mesh_info->reply_list &&
2995+		   qstate->mesh_info->reply_list->query_reply.rpz) {
2996+			struct dns_msg* resp;
2997+			bool is_cname;
2998+			uint8_t* sname;
2999+			size_t slen;
3000+
3001+			switch (rpz_iter_resp(qstate, iq, &resp, &is_cname)) {
3002+			case rpz_iter_resp_fail:
3003+				return error_response(qstate, id,
3004+						      LDNS_RCODE_SERVFAIL);
3005+			case rpz_iter_resp_rewrite:
3006+				/* Prepend any initial CNAMEs from the original
3007+				 * response up to a hit. */
3008+				if(!handle_cname_response(qstate, iq,
3009+							iq->response,
3010+							&sname, &slen))
3011+					return error_response(qstate, id,
3012+							LDNS_RCODE_SERVFAIL);
3013+				if (resp) {
3014+					iq->response = resp;
3015+					iq->rpz_security = resp->rep->security;
3016+					iq->rpz_rewritten = 1;
3017+
3018+					/* Send the rewritten record if it
3019+					 * is not a CNAME. */
3020+					if(!is_cname)
3021+					    break;
3022+
3023+					/* Prepend the new CNAME
3024+					 * and restart to resolve it. */
3025+					if(!handle_cname_response(qstate, iq,
3026+							resp, &sname, &slen))
3027+					    return error_response(qstate, id,
3028+							LDNS_RCODE_SERVFAIL);
3029+				}
3030+				iq->qchase.qname = sname;
3031+				iq->qchase.qname_len = slen;
3032+				iq->dp = NULL;
3033+				iq->refetch_glue = 0;
3034+				iq->query_restart_count++;
3035+				iq->sent_count = 0;
3036+				iq->state = INIT_REQUEST_STATE;
3037+				return 1;
3038+
3039+			case rpz_iter_resp_done:
3040+				break;
3041+			}
3042+		}
3043+#endif
3044 		if(iq->minimisation_state != DONOT_MINIMISE_STATE) {
3045 			if(FLAGS_GET_RCODE(iq->response->rep->flags) !=
3046 				LDNS_RCODE_NOERROR) {
3047@@ -3273,12 +3367,44 @@ processFinished(struct module_qstate* qs
3048 		 * but only if we did recursion. The nonrecursion referral
3049 		 * from cache does not need to be stored in the msg cache. */
3050 		if(!qstate->no_cache_store && qstate->query_flags&BIT_RD) {
3051+#ifdef ENABLE_FASTRPZ
3052+			/* Do not save RPZ rewritten messages. */
3053+			if(!iq->rpz_rewritten)
3054+#endif
3055 			iter_dns_store(qstate->env, &qstate->qinfo,
3056 				iq->response->rep, 0, qstate->prefetch_leeway,
3057 				iq->dp&&iq->dp->has_parent_side_NS,
3058 				qstate->region, qstate->query_flags);
3059 		}
3060 	}
3061+#ifdef ENABLE_FASTRPZ
3062+	if(iq->rpz_rewritten) {
3063+		/* Restore RPZ marks on a rewritten response.  The marks
3064+		 * are lost if the rewrite is to a CNAME. */
3065+		iq->response->rep->security = iq->rpz_security;
3066+
3067+		/* Append the RPZ SOA to rewritten CNAME chains. */
3068+		if(iq->rpz_soa) {
3069+			struct ub_packed_rrset_key** sets;
3070+			uint n;
3071+
3072+			n = iq->response->rep->rrset_count;
3073+			sets = regional_alloc(qstate->region,
3074+					      (1+n) * sizeof(*sets));
3075+			if(!sets) {
3076+				log_err("append RPZ SOA: out of memory");
3077+				return error_response(qstate, id,
3078+						      LDNS_RCODE_SERVFAIL);
3079+			}
3080+			memcpy(sets, iq->response->rep->rrsets,
3081+			       n * sizeof(struct ub_packed_rrset_key*));
3082+			sets[n] = iq->rpz_soa;
3083+			iq->response->rep->rrsets = sets;
3084+			++iq->response->rep->rrset_count;
3085+			++iq->response->rep->ar_numrrsets;
3086+		}
3087+	}
3088+#endif
3089 	qstate->return_rcode = LDNS_RCODE_NOERROR;
3090 	qstate->return_msg = iq->response;
3091 	return 0;
3092Index: unbound-1.7.0~rc1/iterator/iterator.h
3093===================================================================
3094--- unbound-1.7.0~rc1.orig/iterator/iterator.h
3095+++ unbound-1.7.0~rc1/iterator/iterator.h
3096@@ -383,6 +383,16 @@ struct iter_qstate {
3097 	 */
3098 	int minimise_count;
3099
3100+
3101+#ifdef ENABLE_FASTRPZ
3102+	/** The response has been rewritten by RPZ. */
3103+	int rpz_rewritten;
3104+	/** RPZ SOA RR for the ADDITIONAL section */
3105+	struct ub_packed_rrset_key* rpz_soa;
3106+	/** sec_status_rpz_rewritten or sec_status_rpz_drop if rewritten. */
3107+	enum sec_status rpz_security;
3108+#endif
3109+
3110 	/**
3111 	 * Count number of time-outs. Used to prevent resolving failures when
3112 	 * the QNAME minimisation QTYPE is blocked. */
3113Index: unbound-1.7.0~rc1/services/cache/dns.c
3114===================================================================
3115--- unbound-1.7.0~rc1.orig/services/cache/dns.c
3116+++ unbound-1.7.0~rc1/services/cache/dns.c
3117@@ -876,6 +876,14 @@ dns_cache_store(struct module_env* env,
3118 	struct regional* region, uint32_t flags)
3119 {
3120 	struct reply_info* rep = NULL;
3121+
3122+#ifdef ENABLE_FASTRPZ
3123+	/* Never save RPZ rewritten data. */
3124+	if (msgrep->security == sec_status_rpz_drop ||
3125+	    msgrep->security == sec_status_rpz_rewritten)
3126+		return 1;
3127+#endif
3128+
3129 	/* alloc, malloc properly (not in region, like msg is) */
3130 	rep = reply_info_copy(msgrep, env->alloc, NULL);
3131 	if(!rep)
3132Index: unbound-1.7.0~rc1/services/mesh.c
3133===================================================================
3134--- unbound-1.7.0~rc1.orig/services/mesh.c
3135+++ unbound-1.7.0~rc1/services/mesh.c
3136@@ -59,6 +59,9 @@
3137 #include "sldns/wire2str.h"
3138 #include "services/localzone.h"
3139 #include "util/data/dname.h"
3140+#ifdef ENABLE_FASTRPZ
3141+#include "fastrpz/rpz.h"
3142+#endif
3143 #include "respip/respip.h"
3144
3145 /** subtract timers and the values do not overflow or become negative */
3146@@ -1050,6 +1053,13 @@ mesh_send_reply(struct mesh_state* m, in
3147 	else	secure = 0;
3148 	if(!rep && rcode == LDNS_RCODE_NOERROR)
3149 		rcode = LDNS_RCODE_SERVFAIL;
3150+#ifdef ENABLE_FASTRPZ
3151+	/* Drop the response here for LIBRPZ_POLICY_DROP after iteration. */
3152+	if(rep && rep->security == sec_status_rpz_drop) {
3153+		log_query_info(VERB_QUERY, "rpz drop", &m->s.qinfo);
3154+		secure = 0;
3155+	} else
3156+#endif
3157 	/* send the reply */
3158 	/* We don't reuse the encoded answer if either the previous or current
3159 	 * response has a local alias.  We could compare the alias records
3160@@ -1199,6 +1209,7 @@ struct mesh_state* mesh_area_find(struct
3161 	key.s.is_valrec = valrec;
3162 	key.s.qinfo = *qinfo;
3163 	key.s.query_flags = qflags;
3164+	key.reply_list = NULL;
3165 	/* We are searching for a similar mesh state when we DO want to
3166 	 * aggregate the state. Thus unique is set to NULL. (default when we
3167 	 * desire aggregation).*/
3168@@ -1245,6 +1256,10 @@ int mesh_state_add_reply(struct mesh_sta
3169 	if(!r)
3170 		return 0;
3171 	r->query_reply = *rep;
3172+#ifdef ENABLE_FASTRPZ
3173+	/* The new reply structure owns the RPZ state. */
3174+	rep->rpz = NULL;
3175+#endif
3176 	r->edns = *edns;
3177 	if(edns->opt_list) {
3178 		r->edns.opt_list = edns_opt_copy_region(edns->opt_list,
3179Index: unbound-1.7.0~rc1/util/config_file.c
3180===================================================================
3181--- unbound-1.7.0~rc1.orig/util/config_file.c
3182+++ unbound-1.7.0~rc1/util/config_file.c
3183@@ -1323,6 +1323,8 @@ config_delete(struct config_file* cfg)
3184 	free(cfg->dnstap_socket_path);
3185 	free(cfg->dnstap_identity);
3186 	free(cfg->dnstap_version);
3187+	if (cfg->rpz_cstr)
3188+		free(cfg->rpz_cstr);
3189 	config_deldblstrlist(cfg->ratelimit_for_domain);
3190 	config_deldblstrlist(cfg->ratelimit_below_domain);
3191 #ifdef USE_IPSECMOD
3192Index: unbound-1.7.0~rc1/util/config_file.h
3193===================================================================
3194--- unbound-1.7.0~rc1.orig/util/config_file.h
3195+++ unbound-1.7.0~rc1/util/config_file.h
3196@@ -431,6 +431,11 @@ struct config_file {
3197 	/** true to disable DNSSEC lameness check in iterator */
3198 	int disable_dnssec_lame_check;
3199
3200+	/** true to enable RPZ */
3201+	int rpz_enable;
3202+	/** RPZ configuration */
3203+	char* rpz_cstr;
3204+
3205 	/** ratelimit for ip addresses. 0 is off, otherwise qps (unless overridden) */
3206 	int ip_ratelimit;
3207 	/** number of slabs for ip_ratelimit cache */
3208Index: unbound-1.7.0~rc1/util/configlexer.lex
3209===================================================================
3210--- unbound-1.7.0~rc1.orig/util/configlexer.lex
3211+++ unbound-1.7.0~rc1/util/configlexer.lex
3212@@ -412,6 +412,10 @@ dnstap-log-forwarder-query-messages{COLO
3213 		YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) }
3214 dnstap-log-forwarder-response-messages{COLON}	{
3215 		YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) }
3216+rpz{COLON}			{ YDVAR(0, VAR_RPZ) }
3217+rpz-enable{COLON}		{ YDVAR(1, VAR_RPZ_ENABLE) }
3218+rpz-zone{COLON}			{ YDVAR(1, VAR_RPZ_ZONE) }
3219+rpz-option{COLON}		{ YDVAR(1, VAR_RPZ_OPTION) }
3220 disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) }
3221 ip-ratelimit{COLON}		{ YDVAR(1, VAR_IP_RATELIMIT) }
3222 ratelimit{COLON}		{ YDVAR(1, VAR_RATELIMIT) }
3223Index: unbound-1.7.0~rc1/util/configparser.y
3224===================================================================
3225--- unbound-1.7.0~rc1.orig/util/configparser.y
3226+++ unbound-1.7.0~rc1/util/configparser.y
3227@@ -124,6 +124,7 @@ extern struct config_parser_state* cfg_p
3228 %token VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES
3229 %token VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES
3230 %token VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES
3231+%token VAR_RPZ VAR_RPZ_ENABLE VAR_RPZ_ZONE VAR_RPZ_OPTION
3232 %token VAR_RESPONSE_IP_TAG VAR_RESPONSE_IP VAR_RESPONSE_IP_DATA
3233 %token VAR_HARDEN_ALGO_DOWNGRADE VAR_IP_TRANSPARENT
3234 %token VAR_DISABLE_DNSSEC_LAME_CHECK
3235@@ -158,7 +159,7 @@ extern struct config_parser_state* cfg_p
3236
3237 %%
3238 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
3239-toplevelvar: serverstart contents_server | stubstart contents_stub |
3240+toplevelvar: serverstart contents_server | stubstart contents_stub | rpzstart contents_rpz |
3241 	forwardstart contents_forward | pythonstart contents_py |
3242 	rcstart contents_rc | dtstart contents_dt | viewstart contents_view |
3243 	dnscstart contents_dnsc | cachedbstart contents_cachedb |
3244@@ -2384,6 +2385,50 @@ dt_dnstap_log_forwarder_response_message
3245 			(strcmp($2, "yes")==0);
3246 	}
3247 	;
3248+rpzstart: VAR_RPZ
3249+	{
3250+		OUTYY(("\nP(rpz:)\n"));
3251+	}
3252+	;
3253+contents_rpz: contents_rpz content_rpz
3254+	| ;
3255+content_rpz: rpz_enable | rpz_zone | rpz_option
3256+	;
3257+rpz_enable: VAR_RPZ_ENABLE STRING_ARG
3258+	{
3259+		OUTYY(("P(rpz_enable:%s)\n", $2));
3260+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
3261+			yyerror("expected yes or no.");
3262+		else cfg_parser->cfg->rpz_enable = (strcmp($2, "yes")==0);
3263+		free($2);
3264+	}
3265+	;
3266+rpz_zone: VAR_RPZ_ZONE STRING_ARG
3267+	{
3268+		char *new_cstr, *old_cstr;
3269+
3270+		OUTYY(("P(rpz_zone:%s)\n", $2));
3271+		old_cstr = cfg_parser->cfg->rpz_cstr;
3272+		asprintf(&new_cstr, "%s\nzone %s", old_cstr?old_cstr:"", $2);
3273+		if(!new_cstr)
3274+			yyerror("out of memory");
3275+		free(old_cstr);
3276+		cfg_parser->cfg->rpz_cstr = new_cstr;
3277+	}
3278+	;
3279+rpz_option: VAR_RPZ_OPTION STRING_ARG
3280+	{
3281+		char *new_cstr, *old_cstr;
3282+
3283+		OUTYY(("P(rpz_option:%s)\n", $2));
3284+		old_cstr = cfg_parser->cfg->rpz_cstr;
3285+		asprintf(&new_cstr, "%s\n%s", old_cstr ? old_cstr : "", $2);
3286+		if(!new_cstr)
3287+			yyerror("out of memory");
3288+		free(old_cstr);
3289+		cfg_parser->cfg->rpz_cstr = new_cstr;
3290+	}
3291+	;
3292 pythonstart: VAR_PYTHON
3293 	{
3294 		OUTYY(("\nP(python:)\n"));
3295Index: unbound-1.7.0~rc1/util/data/msgencode.c
3296===================================================================
3297--- unbound-1.7.0~rc1.orig/util/data/msgencode.c
3298+++ unbound-1.7.0~rc1/util/data/msgencode.c
3299@@ -585,6 +585,35 @@ insert_section(struct reply_info* rep, s
3300 	return RETVAL_OK;
3301 }
3302
3303+#ifdef ENABLE_FASTRPZ
3304+/* Insert the RPZ SOA even with MINIMAL_RESPONSES */
3305+static int
3306+insert_rpz_soa(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
3307+	       sldns_buffer* pkt, size_t rrsets_before, time_t timenow,
3308+	       struct regional* region, struct compress_tree_node** tree,
3309+	       size_t rr_offset)
3310+{
3311+	int r;
3312+	size_t i, setstart;
3313+
3314+	*num_rrs = 0;
3315+	for(i=0; i<num_rrsets; i++) {
3316+		if (rep->rrsets[rrsets_before+i]->rk.type != LDNS_RR_TYPE_SOA)
3317+			continue;
3318+		setstart = sldns_buffer_position(pkt);
3319+		if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
3320+					  pkt, num_rrs, timenow, region,
3321+					  1, 0, tree, LDNS_SECTION_ADDITIONAL,
3322+					  LDNS_RR_TYPE_ANY, 0, rr_offset))
3323+		   != RETVAL_OK) {
3324+			sldns_buffer_set_position(pkt, setstart);
3325+			return r;
3326+		}
3327+	}
3328+	return RETVAL_OK;
3329+}
3330+
3331+#endif
3332 /** store query section in wireformat buffer, return RETVAL */
3333 static int
3334 insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
3335@@ -750,6 +779,19 @@ reply_info_encode(struct query_info* qin
3336 			return 0;
3337 		}
3338 		sldns_buffer_write_u16_at(buffer, 10, arcount);
3339+#ifdef ENABLE_FASTRPZ
3340+	} else if(rep->security == sec_status_rpz_rewritten) {
3341+		/* Insert the RPZ SOA for rpz even with MINIMAL_RESPONSES */
3342+		r = insert_rpz_soa(rep, rep->ar_numrrsets, &arcount, buffer,
3343+				   rep->an_numrrsets + rep->ns_numrrsets,
3344+				   timenow, region, &tree, rr_offset);
3345+		if(r!= RETVAL_OK) {
3346+			if(r != RETVAL_TRUNC)
3347+				return 0;
3348+			/* no need to set TC bit, this is the additional */
3349+			sldns_buffer_write_u16_at(buffer, 10, arcount);
3350+		}
3351+#endif
3352 	}
3353 	sldns_buffer_flip(buffer);
3354 	return 1;
3355Index: unbound-1.7.0~rc1/util/data/packed_rrset.c
3356===================================================================
3357--- unbound-1.7.0~rc1.orig/util/data/packed_rrset.c
3358+++ unbound-1.7.0~rc1/util/data/packed_rrset.c
3359@@ -254,6 +254,10 @@ sec_status_to_string(enum sec_status s)
3360 	case sec_status_indeterminate: 	return "sec_status_indeterminate";
3361 	case sec_status_insecure: 	return "sec_status_insecure";
3362 	case sec_status_secure: 	return "sec_status_secure";
3363+#ifdef ENABLE_FASTRPZ
3364+	case sec_status_rpz_rewritten: 	return "sec_status_rpz_rewritten";
3365+	case sec_status_rpz_drop: 	return "sec_status_rpz_drop";
3366+#endif
3367 	}
3368 	return "unknown_sec_status_value";
3369 }
3370Index: unbound-1.7.0~rc1/util/data/packed_rrset.h
3371===================================================================
3372--- unbound-1.7.0~rc1.orig/util/data/packed_rrset.h
3373+++ unbound-1.7.0~rc1/util/data/packed_rrset.h
3374@@ -189,7 +189,15 @@ enum sec_status {
3375 	sec_status_insecure,
3376 	/** SECURE means that the object (RRset or message) validated
3377 	 * according to local policy. */
3378-	sec_status_secure
3379+	sec_status_secure,
3380+#ifdef ENABLE_FASTRPZ
3381+	/** RPZ_REWRITTEN means that the response has been rewritten by
3382+	 * rpz and so cannot be verified. */
3383+	sec_status_rpz_rewritten,
3384+	/** RPZ_DROP means that the response has been rewritten by rpz
3385+	 * as silence. */
3386+	sec_status_rpz_drop
3387+#endif
3388 };
3389
3390 /**
3391Index: unbound-1.7.0~rc1/util/netevent.c
3392===================================================================
3393--- unbound-1.7.0~rc1.orig/util/netevent.c
3394+++ unbound-1.7.0~rc1/util/netevent.c
3395@@ -54,6 +54,9 @@
3396 #ifdef HAVE_OPENSSL_ERR_H
3397 #include <openssl/err.h>
3398 #endif
3399+#ifdef ENABLE_FASTRPZ
3400+#include "fastrpz/rpz.h"
3401+#endif
3402
3403 /* -------- Start of local definitions -------- */
3404 /** if CMSG_ALIGN is not defined on this platform, a workaround */
3405@@ -585,6 +588,9 @@ comm_point_udp_ancil_callback(int fd, sh
3406 	struct cmsghdr* cmsg;
3407 #endif /* S_SPLINT_S */
3408
3409+#ifdef ENABLE_FASTRPZ
3410+	rep.rpz = NULL;
3411+#endif
3412 	rep.c = (struct comm_point*)arg;
3413 	log_assert(rep.c->type == comm_udp);
3414
3415@@ -674,6 +680,9 @@ comm_point_udp_callback(int fd, short ev
3416 	int i;
3417 	struct sldns_buffer *buffer;
3418
3419+#ifdef ENABLE_FASTRPZ
3420+	rep.rpz = NULL;
3421+#endif
3422 	rep.c = (struct comm_point*)arg;
3423 	log_assert(rep.c->type == comm_udp);
3424
3425@@ -717,6 +726,9 @@ comm_point_udp_callback(int fd, short ev
3426 			(void)comm_point_send_udp_msg(rep.c, buffer,
3427 				(struct sockaddr*)&rep.addr, rep.addrlen);
3428 		}
3429+#ifdef ENABLE_FASTRPZ
3430+		rpz_end(&rep);
3431+#endif
3432 		if(!rep.c || rep.c->fd != fd) /* commpoint closed to -1 or reused for
3433 		another UDP port. Note rep.c cannot be reused with TCP fd. */
3434 			break;
3435@@ -2956,6 +2968,9 @@ comm_point_send_reply(struct comm_reply
3436 		comm_point_start_listening(repinfo->c, -1,
3437 			repinfo->c->tcp_timeout_msec);
3438 	}
3439+#ifdef ENABLE_FASTRPZ
3440+	rpz_end(repinfo);
3441+#endif
3442 }
3443
3444 void
3445@@ -2965,6 +2980,9 @@ comm_point_drop_reply(struct comm_reply*
3446 		return;
3447 	log_assert(repinfo && repinfo->c);
3448 	log_assert(repinfo->c->type != comm_tcp_accept);
3449+#ifdef ENABLE_FASTRPZ
3450+	rpz_end(repinfo);
3451+#endif
3452 	if(repinfo->c->type == comm_udp)
3453 		return;
3454 	reclaim_tcp_handler(repinfo->c);
3455@@ -2984,6 +3002,9 @@ comm_point_start_listening(struct comm_p
3456 {
3457 	verbose(VERB_ALGO, "comm point start listening %d",
3458 		c->fd==-1?newfd:c->fd);
3459+#ifdef ENABLE_FASTRPZ
3460+	rpz_end(&c->repinfo);
3461+#endif
3462 	if(c->type == comm_tcp_accept && !c->tcp_free) {
3463 		/* no use to start listening no free slots. */
3464 		return;
3465Index: unbound-1.7.0~rc1/util/netevent.h
3466===================================================================
3467--- unbound-1.7.0~rc1.orig/util/netevent.h
3468+++ unbound-1.7.0~rc1/util/netevent.h
3469@@ -119,6 +119,10 @@ struct comm_reply {
3470 	/** return type 0 (none), 4(IP4), 6(IP6) */
3471 	int srctype;
3472 	/* DnsCrypt context */
3473+#ifdef ENABLE_FASTRPZ
3474+	/** per-request RPZ state */
3475+	struct commreply_rpz* rpz;
3476+#endif
3477 #ifdef USE_DNSCRYPT
3478 	uint8_t client_nonce[crypto_box_HALF_NONCEBYTES];
3479 	uint8_t nmkey[crypto_box_BEFORENMBYTES];
3480Index: unbound-1.7.0~rc1/validator/validator.c
3481===================================================================
3482--- unbound-1.7.0~rc1.orig/validator/validator.c
3483+++ unbound-1.7.0~rc1/validator/validator.c
3484@@ -2688,6 +2688,12 @@ ds_response_to_ke(struct module_qstate*
3485 			default:
3486 				/* NSEC proof did not work, try next */
3487 				break;
3488+#ifdef ENABLE_FASTRPZ
3489+			case sec_status_rpz_rewritten:
3490+			case sec_status_rpz_drop:
3491+				fatal_exit("impossible RPZ sec_status");
3492+				break;
3493+#endif
3494 		}
3495
3496 		sec = nsec3_prove_nods(qstate->env, ve,
3497@@ -2721,6 +2727,12 @@ ds_response_to_ke(struct module_qstate*
3498 			default:
3499 				/* NSEC3 proof did not work */
3500 				break;
3501+#ifdef ENABLE_FASTRPZ
3502+			case sec_status_rpz_rewritten:
3503+			case sec_status_rpz_drop:
3504+				fatal_exit("impossible RPZ sec_status");
3505+				break;
3506+#endif
3507 		}
3508
3509 		/* Apparently, no available NSEC/NSEC3 proved NODATA, so
3510
3511