1 Description: based on the included patch contrib/fastrpz.patch
2 Author: fastrpz@farsightsecurity.com
4 This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
5 ===================================================================
6 RCS file: ./RCS/Makefile.in,v
7 retrieving revision 1.1
8 Index: 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 \
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
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
49 pythonmod.lo pythonmod.o: $(srcdir)/pythonmod/pythonmod.c config.h \
50 pythonmod/interface.h \
51 Index: 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
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
68 Index: 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)
79 # must be numbers. ac_defun because of later processing
80 @@ -1453,6 +1454,9 @@ case "$enable_ipsecmod" in
84 +# check for Fastrpz with fastrpz/rpz.m4
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).
90 Index: 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
95 #include "sldns/keyraw.h"
96 #include "respip/respip.h"
98 +#ifdef ENABLE_FASTRPZ
99 +#include "fastrpz/rpz.h"
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");
108 + if(daemon->cfg->rpz_enable) {
109 +#ifdef ENABLE_FASTRPZ
110 + rpz_init(&daemon->rpz_clist, &daemon->rpz_client, daemon->cfg);
112 + fatal_exit("fastrpz enabled in config"
113 + " but not built with fastrpz");
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)
121 dnsc_delete(daemon->dnscenv);
123 +#ifdef ENABLE_FASTRPZ
124 + rpz_delete(&daemon->rpz_clist, &daemon->rpz_client);
129 Index: 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;
137 +#ifdef ENABLE_FASTRPZ
138 + /** global opaque rpz handles */
139 + struct librpz_clist *rpz_clist;
140 + struct librpz_client *rpz_client;
145 Index: 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
150 #include "libunbound/context.h"
151 #include "libunbound/libworker.h"
152 #include "sldns/sbuffer.h"
153 +#ifdef ENABLE_FASTRPZ
154 +#include "fastrpz/rpz.h"
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
163 +#ifdef ENABLE_FASTRPZ
164 + case sec_status_rpz_rewritten:
165 + case sec_status_rpz_drop:
166 + fatal_exit("impossible cached RPZ sec_status");
171 +#ifdef ENABLE_FASTRPZ
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
178 + int ret = rpz_worker_cache(worker, msg->rep, qinfo,
179 + id, flags, edns, repinfo);
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,
191 +#ifdef ENABLE_FASTRPZ
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
198 + int ret = rpz_worker_cache(worker, rep, qinfo, id, flags, edns,
201 + rrset_array_unlock_touch(worker->env.rrset_cache,
202 + worker->scratchpad, rep->ref,
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);
215 +#ifdef ENABLE_FASTRPZ
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);
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,
239 +#ifdef ENABLE_FASTRPZ
241 + /* RPZ already dropped or sent a response. */
242 + lock_rw_unlock(&e->lock);
243 + regional_free_all(worker->scratchpad);
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);
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,
262 regional_free_all(worker->scratchpad);
263 +#ifdef ENABLE_FASTRPZ
265 + /* RPZ already dropped
266 + * or sent a response. */
272 verbose(VERB_ALGO, "answer norec from cache -- "
273 Index: 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"
283 +Response policy zone rewriting is controlled with the
288 +option, and one or more
291 +It will usually also contain
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
300 +.B rpz-option \fI<"text">\fR
301 +is also added to that configuration string.
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.
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).
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.
317 +.B rpz-enable: \fI<yes or no>
319 +If not enabled, the other options in the
323 +.B rpz-zone: \fI<"zone and options">
324 +specifies a policy zone and optional per-zone rewriting parameters.
326 +.B rpz-option: \fI<"option">
327 +specifies general Fastrpz options.
329 +Fastrpz is available only on POSIX compliant UNIX-like systems with the
330 +\fImmap\fR(2) system call.
332 +Fastrpz in Unbound differs from rpz and fastrpz in BIND by
335 +RPZ-CLIENT-IP triggers can only be used in the first policy zone
339 +Policy zone rewriting is disabled by the DO bit in DNS requests
340 +even when no DNSSEC signatures are supplied by authorities.
342 +Unbound local zones are not subject to rpz rewriting.
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.
350 +# example Fastrpz settings for use with chroot on Freebsd
352 + rpz-zone: "rpz.example.org"
353 + rpz-zone: "other.rpz.example.org ip-as-ns yes"
354 + rpz-option: "dnsrpzd ./dnsrpzd"
356 .SS "DNSCrypt Options"
359 Index: unbound-1.7.0~rc1/fastrpz/librpz.h
360 ===================================================================
362 +++ unbound-1.7.0~rc1/fastrpz/librpz.h
365 + * Define the interface from a DNS resolver to the Response Policy Zone
368 + * This file should be included only the interface functions between the
369 + * resolver and librpz to avoid name space pollution.
371 + * Copyright (c) 2016-2017 Farsight Security, Inc.
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
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.
384 + * Fastrpz version 1.2.10
390 +#include <arpa/nameser.h>
391 +#include <netinet/in.h>
393 +#include <stdbool.h>
395 +#include <sys/types.h>
399 + * Allow either ordinary or dlopen() linking.
401 +#ifdef LIBRPZ_INTERNAL
402 +#define LIBDEF(t,s) extern t s;
403 +#define LIBDEF_F(f) LIBDEF(librpz_##f##_t, librpz_##f)
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}
416 + LIBRPZ_TRIG_BAD =0,
417 + LIBRPZ_TRIG_CLIENT_IP =1,
418 + LIBRPZ_TRIG_QNAME =2,
420 + LIBRPZ_TRIG_NSDNAME =4,
421 + LIBRPZ_TRIG_NSIP =5
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
429 + * Response Policy Zone Actions or policies
432 + LIBRPZ_POLICY_UNDEFINED =0, /* an empty entry or no decision yet */
433 + LIBRPZ_POLICY_DELETED =1, /* placeholder for a deleted policy */
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 */
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' */
447 +#define LIBRPZ_POLICY_BITS 4
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.
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"
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] */
467 +typedef struct librpz_prefix {
470 + struct in6_addr in6;
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 */
486 + * A maximal domain buffer
488 +typedef struct librpz_domain_buf {
489 + librpz_dsize_t size;
490 + uint8_t d[NS_MAXCDNAME];
491 +} librpz_domain_buf_t;
494 + * A resource record without the owner name.
495 + * C compilers say that sizeof(librpz_rr_t)=12 instead of 10.
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 */
506 + * The database file might be mapped with different starting addresses
507 + * by concurrent clients (resolvers), and so all pointers are offsets.
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)
514 + * Partial decoded results of a set of RPZ queries for a single DNS response
515 + * or interation through the mapped file.
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 */
531 + * librpz trace or log levels.
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)
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.
551 +typedef void(librpz_log_fnc_t)(librpz_log_level_t level, void *ctx,
555 + * Point librpz logging functions to the resolver's choice.
557 +typedef void (librpz_set_log_t)(librpz_log_fnc_t *new_log, const char *prog_nm);
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.
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__))
577 +#define LIBRPZ_UNUSED
578 +#define LIBRPZ_PF(f,l)
579 +#define LIBRPZ_NORET
582 +#ifdef HAVE_BUILTIN_EXPECT
583 +#define LIBRPZ_LIKELY(c) __builtin_expect(!!(c), 1)
584 +#define LIBRPZ_UNLIKELY(c) __builtin_expect(!!(c), 0)
586 +#define LIBRPZ_LIKELY(c) (c)
587 +#define LIBRPZ_UNLIKELY(c) (c)
590 +typedef bool (librpz_parse_log_opt_t)(librpz_emsg_t *emsg, const char *arg);
591 +LIBDEF_F(parse_log_opt)
593 +typedef void (librpz_vpemsg_t)(librpz_emsg_t *emsg,
594 + const char *p, va_list args);
596 +typedef void (librpz_pemsg_t)(librpz_emsg_t *emsg,
597 + const char *p, ...) LIBRPZ_PF(2,3);
600 +typedef void (librpz_vlog_t)(librpz_log_level_t level, void *ctx,
601 + const char *p, va_list args);
603 +typedef void (librpz_log_t)(librpz_log_level_t level, void *ctx,
604 + const char *p, ...) LIBRPZ_PF(3,4);
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;
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;
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;
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.
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.
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.
640 + * As far as clients are concerned, there are the following possible librpz
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.
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
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:
668 + * lock(void *mutex) { assert(pthread_mutex_lock(mutex) == 0); }
671 + * unlock(void *mutex) { assert(pthread_mutex_unlock(mutex) == 0); }
674 + * mutex_destroy(void *mutex) { assert(pthread_mutex_destroy(mutex) == 0); }
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.
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.
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.
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.
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.
711 + * Lock functions for concurrent use of a single librpz_client_t client handle.
713 +typedef void(librpz_mutex_t)(void *mutex);
716 + * List of connections to dnsrpzd daemons.
718 +typedef struct librpz_clist librpz_clist_t;
721 + * Client's handle on dnsrpzd.
723 +typedef struct librpz_client librpz_client_t;
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
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)
743 + * Release the list of dnsrpzd connections.
745 +typedef void (librpz_clist_detach_t)(librpz_clist_t **clistp);
746 +LIBDEF_F(clist_detach)
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
756 +typedef librpz_client_t *(librpz_client_create_t)(librpz_emsg_t *emsg,
757 + librpz_clist_t *clist,
760 +LIBDEF_F(client_create)
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
768 +typedef bool (librpz_connect_t)(librpz_emsg_t *emsg, librpz_client_t *client,
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
779 +typedef void (librpz_client_detach_t)(librpz_client_t **clientp);
780 +LIBDEF_F(client_detach)
783 + * State for a set of RPZ queries for a single DNS response
784 + * or for listing the database.
786 +typedef struct librpz_rsp librpz_rsp_t;
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
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)
804 + * Finish RPZ work for a DNS response.
806 +typedef void (librpz_rsp_detach_t)(librpz_rsp_t **rspp);
807 +LIBDEF_F(rsp_detach)
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
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
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)
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
837 +typedef bool (librpz_have_trig_t)(librpz_trig_t trig, bool ipv6,
838 + const librpz_rsp_t *rsp);
842 + * Might looking for NSDNAME and NSIP triggers be worthwhile?
843 + * @return: true if looking could be worthwhile
845 +typedef bool (librpz_have_ns_trig_t)(const librpz_rsp_t *rsp);
846 +LIBDEF_F(have_ns_trig)
849 + * Convert the found client IP trie key to a CIDR block
851 + * @param[out] prefix trigger
852 + * @param[in,out] rsp state from librpz_itr_start()
853 + * @return false on error
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)
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.
866 + * @param[out] owner domain
867 + * @param[in,out] rsp state from librpz_itr_start()
868 + * @return false on error
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)
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.
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
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);
900 + * Get the next RR of the LIBRPZ_POLICY_RECORD result.
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
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);
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.
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)
924 + * Save the current policy checking state.
926 + * @param[in,out] rsp state from librpz_itr_start()
927 + * @return false on error
929 +typedef bool (librpz_rsp_push_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
931 +#define LIBRPZ_RSP_STACK_DEPTH 3
934 + * Restore the previous policy checking state.
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
940 +typedef bool (librpz_rsp_pop_t)(librpz_emsg_t *emsg, librpz_result_t *result,
941 + librpz_rsp_t *rsp);
945 + * Discard the most recently save policy checking state.
947 + * @param[out] result: NULL or restored policy rewrite values
948 + * @return false on error
950 +typedef bool (librpz_rsp_pop_discard_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
951 +LIBDEF_F(rsp_pop_discard)
957 + * @param[in,out] rsp state from librpz_itr_start()
958 + * @return false on error
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)
965 + * Apply RPZ to an IP address.
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
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);
982 + * Apply RPZ to a wire-format domain.
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
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);
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
1005 +typedef bool (librpz_zone_refresh_t)(librpz_emsg_t *emsg, const char *domain,
1006 + librpz_rsp_t *rsp);
1007 +LIBDEF_F(zone_refresh)
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
1018 +typedef char *(librpz_db_info_t)(librpz_emsg_t *emsg,
1019 + bool license, bool cfiles, bool listens,
1020 + librpz_rsp_t *rsp);
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
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)
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
1040 +typedef char *(librpz_mf_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
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
1049 +typedef char *(librpz_vers_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
1050 +LIBDEF_F(vers_stats)
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
1059 +typedef char *(librpz_itr_zone_t)(librpz_emsg_t *emsg, bool all_zones,
1060 + librpz_rsp_t *rsp);
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
1072 +typedef bool (librpz_itr_node_t)(librpz_emsg_t *emsg, librpz_result_t *result,
1073 + bool all_zones, librpz_rsp_t *rsp);
1077 + * RPZ policy to string with a backup buffer of POLICY2STR_SIZE size
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)
1085 + * Trigger type to string.
1087 +typedef const char *(librpz_trig2str_t)(librpz_trig_t trig);
1091 + * Convert a number of seconds to a zone file duration string
1093 +typedef const char *(librpz_secs2str_t)(time_t secs,
1094 + char *buf, size_t buf_size);
1095 +#define SECS2STR_SIZE sizeof("1234567w7d24h59m59s")
1099 + * Parse a duration with 's', 'm', 'h', 'd', and 'w' units.
1101 +typedef bool (librpz_str2secs_t)(librpz_emsg_t *emsg, time_t *val,
1102 + const char *str0);
1106 + * Translate selected rtypes to strings
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)
1113 + * Local version of ns_name_ntop() for portability.
1115 +typedef int (librpz_domain_ntop_t)(const u_char *src, char *dst, size_t dstsiz);
1116 +LIBDEF_F(domain_ntop)
1119 + * Local version of ns_name_pton().
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)
1125 +typedef union socku socku_t;
1126 +typedef socku_t *(librpz_mk_inet_su_t)(socku_t *su, const struct in_addr *addrp,
1128 +LIBDEF_F(mk_inet_su)
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)
1135 +typedef bool (librpz_str2su_t)(socku_t *sup, const char *str);
1138 +typedef char *(librpz_su2str_t)(char *str, size_t str_len, const socku_t *su);
1140 +#define SU2STR_SIZE (INET6_ADDRSTRLEN+1+6+1)
1144 + * default path to dnsrpzd
1146 +const char *librpz_dnsrpzd_path;
1152 + * This is the dlopen() interface to librpz.
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;
1207 +extern librpz_0_t librpz_def_0;
1210 + * Future versions can be upward compatible by defining LIBRPZ_DEF as
1213 +#define LIBRPZ_DEF librpz_def_0
1214 +#define LIBRPZ_DEF_STR "librpz_def_0"
1216 +typedef librpz_0_t librpz_t;
1217 +extern librpz_t *librpz;
1220 +#if LIBRPZ_LIB_OPEN == 2
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
1230 +static inline librpz_t *
1231 +librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path)
1234 + librpz_t *new_librpz;
1236 + emsg->c[0] = '\0';
1239 + * Close a previously opened handle on librpz.so.
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());
1247 + *dl_handle = NULL;
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.
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);
1263 + if (dlclose(handle) != 0) {
1264 + snprintf(emsg->c, sizeof(librpz_emsg_t),
1265 + "dlsym(NULL, "LIBRPZ_DEF_STR"): %s",
1271 + if (path == NULL || path[0] == '\0') {
1272 + snprintf(emsg->c, sizeof(librpz_emsg_t),
1273 + "librpz not linked and no dlopen() path provided");
1277 + handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
1278 + if (handle == NULL) {
1279 + snprintf(emsg->c, sizeof(librpz_emsg_t), "dlopen(%s): %s",
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);
1289 + snprintf(emsg->c, sizeof(librpz_emsg_t),
1290 + "dlsym(%s, "LIBRPZ_DEF_STR"): %s",
1296 +#elif defined(LIBRPZ_LIB_OPEN)
1299 + * Statically link to the librpz.so DSO on systems without dlopen()
1301 +static inline librpz_t *
1302 +librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path)
1306 + if (dl_handle != NULL)
1307 + *dl_handle = NULL;
1309 +#if LIBRPZ_LIB_OPEN == 1
1310 + emsg->c[0] = '\0';
1311 + return (&LIBRPZ_DEF);
1313 + snprintf(emsg->c, sizeof(librpz_emsg_t),
1314 + "librpz not available via ./configure");
1316 +#endif /* LIBRPZ_LIB_OPEN */
1318 +#endif /* LIBRPZ_LIB_OPEN */
1320 +#endif /* LIBRPZ_H */
1321 Index: unbound-1.7.0~rc1/fastrpz/rpz.c
1322 ===================================================================
1324 +++ unbound-1.7.0~rc1/fastrpz/rpz.c
1327 + * fastrpz/rpz.c - interface to the fastrpz response policy zone library
1329 + * Optimize no-rewrite cases for speed but optimize rewriting for
1330 + * simplicity and size.
1333 +#include "config.h"
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"
1359 +typedef enum state {
1360 + /* No more rewriting */
1362 + /* Send SERVFAIL */
1364 + /* No dispositive hit yet */
1366 + /* Let the iterator resolve a CNAME or get a delegation point. */
1368 + /* Let the iterator resolve NS to check NSIP or NSDNAME triggers. */
1370 + /* We have an answer */
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 */
1382 + /* from configuration */
1385 + /* Running in the iterator */
1388 + /* current and previous state and librpz result */
1390 + st_t saved_st[LIBRPZ_RSP_STACK_DEPTH-1];
1391 + librpz_result_t result;
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;
1401 +/* Generate an ID for log messages. */
1407 +static void LIBRPZ_NORET
1408 +rpz_assert(const char *s)
1410 + fatal_exit("%s", s);
1413 +#define RPZ_ASSERT(c) ((c) ? (void)0 : rpz_assert(#c), (void)0)
1416 + * librpz client handle locking
1419 +lock_destroy(void* mutex)
1421 + lock_basic_destroy(mutex);
1428 + lock_basic_lock(mutex);
1432 +unlock(void* mutex)
1434 + lock_basic_unlock(mutex);
1439 +log_fnc(librpz_log_level_t level, void* ATTR_UNUSED(ctx), const char* buf)
1441 + char label_buf[sizeof("rpz ")+8];
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;
1449 + case LIBRPZ_LOG_FATAL:
1450 + case LIBRPZ_LOG_ERROR: /* errors */
1452 + log_err("rpz: %s", buf);
1455 + case LIBRPZ_LOG_TRACE1: /* big events such as dnsrpzd starts */
1456 + verbose(VERB_OPS, "rpz: %s", buf);
1459 + case LIBRPZ_LOG_TRACE2: /* smaller dnsrpzd zone transfers */
1460 + verbose(VERB_DETAIL, "rpz: %s", buf);
1463 + case LIBRPZ_LOG_TRACE3: /* librpz hits */
1464 + verbose(VERB_QUERY, "rpz: %s", buf);
1467 + case LIBRPZ_LOG_TRACE4: /* librpz lookups */
1468 + verbose(VERB_CLIENT, "rpz: %s", buf);
1474 +/* Release the librpz version. */
1476 +rpz_off(commreply_rpz_t* rpz, st_t st)
1481 + librpz->rsp_detach(&rpz->rsp);
1485 +static void LIBRPZ_PF(2,3)
1486 +log_fail(commreply_rpz_t* rpz, const char* p, ...)
1490 + if(rpz->st == st_servfail)
1493 + va_start(args, p);
1494 + librpz->vlog(LIBRPZ_LOG_ERROR, rpz, p, args);
1498 + rpz_off(rpz, st_servfail);
1502 +/* Announce a rewrite. */
1504 +log_rewrite(uint8_t* qname, librpz_policy_t policy, const char* msg,
1505 + commreply_rpz_t* rpz)
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;
1513 + if(rpz->st == st_servfail || !rpz->result.log)
1515 + if(librpz->log_level_val(LIBRPZ_LOG_INVALID) < LIBRPZ_LOG_TRACE1)
1518 + dname_str(qname, qname_nm);
1520 + if(!librpz->rsp_domain(&emsg, &tdomain, rpz->rsp)) {
1521 + librpz->log(LIBRPZ_LOG_ERROR, rpz, "%s", emsg.c);
1524 + dname_str(tdomain.d, tdomain_nm);
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)));
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.
1542 +rpz_init(librpz_clist_t** pclist, librpz_client_t** pclient,
1543 + const struct config_file* cfg)
1545 + lock_basic_type* mutex;
1546 + librpz_emsg_t emsg;
1549 + librpz = librpz_lib_open(&emsg, NULL, FASTRPZ_LIBRPZ_PATH);
1551 + fatal_exit("rpz: %s", emsg.c);
1554 + librpz->set_log(&log_fnc, NULL);
1556 + if(!cfg->rpz_cstr)
1557 + fatal_exit("rpz: rpz-zone: not set");
1559 + librpz->client_detach(pclient);
1560 + librpz->clist_detach(pclist);
1562 + mutex = malloc(sizeof(*mutex));
1564 + fatal_exit("rpz: no memory for lock");
1565 + lock_basic_init(mutex);
1567 + *pclist = librpz->clist_create(&emsg, &lock, &unlock, &lock_destroy,
1570 + fatal_exit("rpz: %s", emsg.c);
1572 + *pclient = librpz->client_create(&emsg, *pclist, cfg->rpz_cstr, false);
1574 + fatal_exit("rpz: %s", emsg.c);
1576 + if(!librpz->connect(&emsg, *pclient, true))
1577 + fatal_exit("rpz: %s", emsg.c);
1579 + verbose(VERB_OPS, "rpz: librpz version %s", librpz->version);
1583 +/* Stop using librpz on behalf of a worker thread. */
1585 +rpz_delete(librpz_clist_t** pclist, librpz_client_t** pclient)
1588 + librpz->client_detach(pclient);
1589 + librpz->clist_detach(pclist);
1594 +/* Release the librpz resources held for a DNS client request. */
1596 +rpz_end(struct comm_reply* commreply)
1598 + if(!commreply->rpz)
1600 + rpz_off(commreply->rpz, commreply->rpz->st);
1601 + free(commreply->rpz);
1602 + commreply->rpz = NULL;
1607 +push_st(commreply_rpz_t* rpz)
1609 + librpz_emsg_t emsg;
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);
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;
1626 +pop_st(commreply_rpz_t* rpz)
1628 + librpz_emsg_t emsg;
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;
1640 +pop_discard_st(commreply_rpz_t* rpz)
1642 + librpz_emsg_t emsg;
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;
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)
1656 + librpz_emsg_t emsg;
1658 + if(rpz->st == st_servfail)
1661 + if(!librpz->rsp_result(&emsg, &rpz->result, recursed, rpz->rsp)) {
1662 + log_fail(rpz, "%s", emsg.c);
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);
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);
1696 + log_fail(rpz, "%s", emsg.c);
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);
1708 +/* Get the next RR from the policy record. */
1710 +next_rr(librpz_rr_t** rrp, const uint8_t* qname, size_t qname_len,
1711 + commreply_rpz_t* rpz)
1713 + librpz_emsg_t emsg;
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);
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)
1731 + rdlength = ntohs(rr->rdlength);
1733 + if(!sldns_buffer_available(pkt, owner_len + 10 + rdlength)) {
1734 + log_fail(rpz, "comm_reply buffer exhausted");
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);
1745 +/* Convert a fake incoming DNS message to an Unbound struct dns_msg */
1747 +pkt2dns_msg(struct dns_msg** dnsmsg, struct sldns_buffer* pkt,
1748 + commreply_rpz_t* rpz, struct regional* region)
1750 + struct msg_parse* msgparse;
1752 + msgparse = regional_alloc(region, sizeof(*msgparse));
1754 + log_fail(rpz, "out of memory for msgparse");
1758 + memset(msgparse, 0, sizeof(*msgparse));
1759 + if(parse_packet(pkt, msgparse, region) != LDNS_RCODE_NOERROR) {
1760 + log_fail(rpz, "packet parse error");
1764 + *dnsmsg = dns_alloc_msg(pkt, msgparse, region);
1766 + log_fail(rpz, "dns_alloc_msg() failed");
1770 + (*dnsmsg)->rep->security = sec_status_rpz_rewritten;
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)
1778 + const struct packed_rrset_data* data;
1781 + librpz_emsg_t emsg;
1785 + /* Loop to ignore disabled zones. */
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))
1797 + if(!librpz->ck_ip(&emsg, data->rr_data[rr_n]+2,
1798 + family, trig, rpz->hit_id, true,
1800 + log_fail(rpz, "%s", emsg.c);
1804 + } while(ck_after(qname, true, trig, rpz));
1805 + return rpz->st != st_servfail;
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)
1813 + librpz_emsg_t emsg;
1815 + /* Refuse to check the root. */
1816 + if(dname_is_root(dname))
1817 + return rpz->st != st_servfail;
1819 + /* Loop to ignore disabled zones. */
1823 + if(!librpz->ck_domain(&emsg, dname, dname_size, trig,
1824 + rpz->hit_id, recursed, rpz->rsp)) {
1825 + log_fail(rpz, "%s", emsg.c);
1828 + } while(ck_after(qname, recursed, trig, rpz));
1830 + return rpz->st != st_servfail;
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)
1839 + struct ub_packed_rrset_key* akey;
1841 + akey = rrset_cache_lookup(env->rrset_cache, nsname, nsname_size,
1842 + qtype, LDNS_RR_CLASS_IN, 0, 0, 0);
1846 + if(!ck_ip_rrset(akey->entry.data, family, LIBRPZ_TRIG_NSIP,
1848 + lock_rw_unlock(&akey->entry.lock);
1851 + lock_rw_unlock(&akey->entry.lock);
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)
1864 + size_t dname_size;
1866 + struct ub_packed_rrset_key* nskey;
1867 + const struct packed_rrset_data* nsdata;
1869 + size_t nsname_size;
1871 + bool have_ns, tried_ns;
1873 + if(!ck_dname(qname, qname_len, LIBRPZ_TRIG_QNAME, qname, false, rpz))
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))
1884 + dname_size = qname_len;
1885 + for(cur_lab = dname_count_labels(dname) - 2;
1886 + cur_lab > rpz->min_ns_dots;
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,
1896 + nsdata = (const struct packed_rrset_data*)nskey->entry.data;
1898 + rr_n < nsdata->count && rpz->st == st_unknown;
1900 + nsname = nsdata->rr_data[rr_n]+2;
1901 + nsname_size = nsdata->rr_len[rr_n];
1902 + if(nsname_size <= 2)
1905 + if(!ck_dname(nsname, nsname_size, LIBRPZ_TRIG_NSDNAME,
1906 + qname, recursed, rpz))
1908 + if(!ck_1nsip(nsname, nsname_size, AF_INET,
1909 + LDNS_RR_TYPE_A, &have_ns, rpz, env))
1911 + if(!ck_1nsip(nsname, nsname_size, AF_INET6,
1912 + LDNS_RR_TYPE_AAAA, &have_ns, rpz, env))
1915 + lock_rw_unlock(&nskey->entry.lock);
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;
1932 + * Are we ready to rewrite the response?
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)
1938 + librpz_emsg_t emsg;
1943 + case st_rewritten:
1950 + /* An NSDNAME or NSIP check failed for lack of cached data. */
1952 +#pragma clang diagnostic push
1953 +#pragma clang diagnostic ignored "-Wunreachable-code"
1955 + fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
1957 +#pragma clang diagnostic pop
1960 + /* Wait for a trigger. */
1961 + if(rpz->result.policy == LIBRPZ_POLICY_UNDEFINED) {
1963 + rpz->result.zpolicy != LIBRPZ_POLICY_UNDEFINED &&
1964 + !librpz->rsp_result(&emsg, &rpz->result, true, rpz->rsp)) {
1965 + log_fail(rpz, "%s", emsg.c);
1968 + if(rpz->result.policy == LIBRPZ_POLICY_UNDEFINED)
1972 + if(rpz->result.policy == LIBRPZ_POLICY_PASSTHRU) {
1973 + log_rewrite(qname, rpz->result.policy, "", rpz);
1974 + rpz_off(rpz, st_off);
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);
1990 + * Convert an RPZ hit to a struct dns_msg
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)
1998 + librpz_domain_buf_t origin;
1999 + struct sldns_buffer* pkt;
2001 + librpz_emsg_t emsg;
2004 + if(!ck_result(qinfo->qname, recursed, rpz, commpoint))
2007 + rpz->st = st_rewritten;
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;
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. */
2028 + flags |= BIT_QR | BIT_RA;
2031 + /* The TCP-only policy answers UDP requests with truncated responses. */
2032 + if(rpz->result.policy == LIBRPZ_POLICY_TCP_ONLY) {
2035 + } else if(rpz->result.policy == LIBRPZ_POLICY_NXDOMAIN) {
2036 + flags |= LDNS_RCODE_NXDOMAIN;
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;
2048 + next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
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)) {
2059 + rpz->st = st_iterate;
2063 + log_rewrite(qinfo->qname, rpz->result.policy, "", rpz);
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");
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 */
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);
2090 + /* Install the RPZ RRs in the answer section */
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,
2105 + next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
2107 + /* Finish ANCOUNT. */
2109 + sldns_buffer_write_u16_at(pkt, 6, num_rrs);
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");
2118 + if(!add_rr(pkt, origin.d, origin.size, rr, rpz))
2122 + /* Create a dns_msg representation of the fake incoming message. */
2123 + sldns_buffer_flip(pkt);
2124 + pkt2dns_msg(dnsmsg, pkt, rpz, region);
2128 +/* Check the RRs in the ANSWER section of a reply_info. */
2130 +ck_reply(struct reply_info* reply, uint8_t* qname, bool wait_ns,
2131 + commreply_rpz_t* rpz, struct module_env* env)
2133 + struct ub_packed_rrset_key* rrset;
2134 + enum sldns_enum_rr_type type;
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)
2145 + rrset = reply->rrsets[rrset_n];
2146 + if(ntohs(rrset->rk.rrset_class) != LDNS_RR_CLASS_IN)
2148 + type = ntohs(rrset->rk.type);
2150 + if(type == LDNS_RR_TYPE_A) {
2151 + if(!ck_ip_rrset(rrset->entry.data, AF_INET,
2152 + LIBRPZ_TRIG_IP, qname, rpz))
2155 + } else if(type == LDNS_RR_TYPE_AAAA) {
2156 + if(!ck_ip_rrset(rrset->entry.data, AF_INET6,
2157 + LIBRPZ_TRIG_IP, qname, rpz))
2160 + } else if(type == LDNS_RR_TYPE_CNAME) {
2161 + /* Check CNAME owners unless we already have a hit. */
2163 + if(!ck_qname(rrset->rk.dname, rrset->rk.dname_len,
2164 + true, wait_ns, rpz, env))
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;
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);
2189 +worker_servfail(struct worker* worker, struct query_info* qinfo,
2190 + uint16_t id, uint16_t flags, struct comm_reply* commreply)
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);
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. */
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)
2208 + switch (commreply->rpz->st) {
2212 + worker_servfail(worker, qinfo, id, flags, commreply);
2218 + return 0; /* punt to the iterator */
2219 + case st_rewritten:
2222 + fatal_exit("impossible RPZ state %d in worker_send()",
2223 + commreply->rpz->st);
2226 + if(dnsmsg->rep->security == sec_status_rpz_drop) {
2227 + regional_free_all(worker->scratchpad);
2228 + comm_point_drop_reply(commreply);
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);
2243 + regional_free_all(worker->scratchpad);
2244 + comm_point_send_reply(commreply);
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)
2256 + commreply_rpz_t* rpz;
2257 + uint16_t id, flags;
2258 + struct dns_msg* dnsmsg;
2261 + librpz_emsg_t emsg;
2263 + /* Quit if rpz not configured. */
2264 + if(!worker->daemon->rpz_client)
2267 + /* Rewrite only the Internet class */
2268 + if(qinfo->qclass != LDNS_RR_CLASS_IN)
2271 + rpz = commreply->rpz;
2275 + id = htons(sldns_buffer_read_u16_at(commreply->c->buffer, 0));
2276 + flags = sldns_buffer_read_u16_at(commreply->c->buffer, 2);
2278 + rpz = malloc(sizeof(*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);
2284 + memset(rpz, 0, sizeof(*rpz));
2285 + rpz->st = st_unknown;
2286 + commreply->rpz = rpz;
2288 + /* Make a new ID for log messages */
2289 + rpz->log_id = __sync_add_and_fetch(&log_id, 1);
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);
2299 + /* Quit if benign reasons prevent rewriting. */
2302 + librpz->log(LIBRPZ_LOG_TRACE1, rpz, "%s", emsg.c);
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;
2311 + addr = &((struct sockaddr_in*)&commreply->addr)->sin_addr;
2314 + addr = &((struct sockaddr_in6*)&commreply->addr)->sin6_addr;
2317 + /* Maybe the client is on a UNIX domain socket. */
2318 + librpz->log(LIBRPZ_LOG_TRACE2, rpz,
2319 + "unknown client address family %d", family);
2323 + /* Loop to ignore disabled zones. */
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);
2332 + if(!ck_after(qinfo->qname, false, LIBRPZ_TRIG_CLIENT_IP, rpz))
2335 + if(rpz->st == st_servfail)
2336 + return 0 > worker_send(dnsmsg, worker, qinfo,
2337 + id, flags, edns, commreply);
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);
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);
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 */
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)
2359 + commreply_rpz_t* rpz;
2360 + struct dns_msg* dnsmsg;
2366 + rpz = commreply->rpz;
2369 + return 1; /* Send the cache entry. */
2371 + return worker_send(dnsmsg, worker, qinfo, id, flags,
2377 + return 0; /* Punt to the iterator. */
2378 + case st_rewritten:
2380 + fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
2384 + /* Check the RRs in the ANSWER section. */
2386 + return worker_send(dnsmsg, worker, qinfo, id, flags, edns,
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,
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;
2400 + } else if(rpz->result.policy == LIBRPZ_POLICY_CNAME) {
2401 + /* Punt if the rewritten response is to a CNAME. */
2402 + new_st = st_iterate;
2405 + if(rpz->result.policy == LIBRPZ_POLICY_RECORD) {
2406 + next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz);
2408 + /* Punt we are rewriting to a CNAME. */
2409 + if(rr->type == ntohs(LDNS_RR_TYPE_CNAME)) {
2411 + rpz->st = st_iterate;
2417 + get_result_msg(&dnsmsg, qinfo, id, flags, true,
2418 + rpz, commreply->c, worker->scratchpad);
2427 + pop_discard_st(rpz);
2434 + case st_rewritten:
2435 + pop_discard_st(rpz);
2438 + fatal_exit("impossible RPZ state %d in rpz_worker_cache()",
2442 + return worker_send(dnsmsg, worker, qinfo, id, flags, edns, commreply);
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)
2454 + struct comm_reply* commreply;
2455 + commreply_rpz_t* rpz;
2456 + struct dns_msg* dnsmsg;
2458 + commreply = &qstate->mesh_info->reply_list->query_reply;
2459 + rpz = commreply->rpz;
2461 + rpz->iterating = true;
2465 + iq->rpz_rewritten = 1; /* RPZ has nothing to say. */
2473 + rpz->st = st_unknown;
2474 + if(!ck_qname(iq->qchase.qname, iq->qchase.qname_len,
2475 + *msg != NULL, true, rpz, qstate->env))
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)
2483 + case st_rewritten:
2485 + fatal_exit("impossible RPZ state %d in rpz_iter_cache()",
2491 + /* Check the cache hit. */
2493 + ck_reply((*msg)->rep, iq->qchase.qname, true, rpz, qstate->env);
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);
2502 + iq->rpz_rewritten = 1; /* RPZ has nothing to say. */
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);
2511 + /* Try to get NS data for a CNAME found by ck_reply() */
2512 + *type = RESPONSE_TYPE_CNAME;
2513 + return pop_discard_st(rpz);
2516 + fatal_exit("impossible RPZ state %d in rpz_iter_cache()",
2518 + case st_rewritten:
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);
2537 + iq->rpz_security = dnsmsg->rep->security;
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;
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;
2562 +/* Check a RESPONSE_TYPE_ANSWER response from an authority in the iterator. */
2564 +rpz_iter_resp(struct module_qstate* qstate, struct iter_qstate* iq,
2565 + struct dns_msg** resp, bool* is_cname)
2567 + struct comm_reply* commreply;
2568 + commreply_rpz_t* rpz;
2569 + struct reply_info* rep;
2571 + *is_cname = false;
2573 + commreply = &qstate->mesh_info->reply_list->query_reply;
2574 + rpz = commreply->rpz;
2579 + case st_rewritten:
2581 + fatal_exit("impossible RPZ state %d in rpz_iter_resp()",
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
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);
2598 + /* Check the RRs in the ANSWER section. */
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);
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;
2612 + return rpz_iter_resp_done; /* continue without change */
2615 + fatal_exit("impossible RPZ state %d in rpz_iter_resp()",
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)) {
2627 + iq->rpz_soa = rep->rrsets[1];
2629 + return rpz_iter_resp_rewrite;
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. */
2640 +rpz_cname(struct module_qstate* qstate,
2641 + uint8_t* oname, size_t oname_size)
2643 + struct mesh_reply* reply_list;
2644 + struct comm_reply* commreply;
2645 + commreply_rpz_t* rpz;
2648 + /* Quit if RPZ is off */
2649 + reply_list = qstate->mesh_info->reply_list;
2651 + return rpz_cname_prepend;
2652 + commreply = &reply_list->query_reply;
2653 + rpz = commreply->rpz;
2655 + if(!rpz || rpz->st == st_off)
2656 + return rpz_cname_prepend;
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;
2665 + if(rpz->st != st_unknown)
2666 + fatal_exit("impossible RPZ state %d in rpz_cname()", rpz->st);
2668 + ret = rpz_cname_prepend;
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. */
2674 + ck_qname(oname, oname_size, true, true, rpz, qstate->env);
2675 + if(rpz->st != st_unknown)
2676 + ret = rpz_cname_stop;
2678 + return rpz_cname_fail;
2682 +#endif /* ENABLE_FASTRPZ */
2683 Index: unbound-1.7.0~rc1/fastrpz/rpz.h
2684 ===================================================================
2686 +++ unbound-1.7.0~rc1/fastrpz/rpz.h
2689 + * fastrpz/rpz.h - interface to the fastrpz response policy zone library
2691 + * Copyright (c) 2016 Farsight Security, Inc.
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
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.
2705 +#ifndef UNBOUND_FASTRPZ_RPZ_H
2706 +#define UNBOUND_FASTRPZ_RPZ_H
2708 +#ifndef PACKAGE_VERSION
2709 +/* Ensure that config.h has been included to correctly set ENABLE_FASTRPZ */
2710 +#include "config.h"
2713 +#ifdef ENABLE_FASTRPZ
2715 +#include "librpz.h"
2717 +#include "daemon/daemon.h"
2718 +#include "util/config_file.h"
2720 +struct comm_point; /* forward references */
2724 +struct iter_qstate;
2727 +enum response_type; /* iterator/iter_utils.h */
2730 +struct commreply_rpz;
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
2738 +void rpz_init(librpz_clist_t** pclist, librpz_client_t** pclient,
2739 + const struct config_file* cfg);
2742 + * Disconnect from the librpz database
2743 + * @param client: opaque librpz client data
2745 +void rpz_delete(librpz_clist_t** pclist, librpz_client_t** pclient);
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
2756 +bool rpz_start(struct worker* worker, struct query_info* qinfo,
2757 + struct comm_reply* commreply, struct edns_data* edns);
2760 + * Release resources held for a DNS request
2761 + * @param rspp: pointer to pointer to rpz client context.
2763 +void rpz_end(struct comm_reply* comm_rep);
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
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);
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
2789 +bool rpz_iter_cache(struct dns_msg** msg, enum response_type* type,
2790 + struct module_qstate* qstate, struct iter_qstate* iq);
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
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. */
2805 +rpz_iter_resp_t rpz_iter_resp(struct module_qstate* qstate,
2806 + struct iter_qstate* iq, struct dns_msg** resp,
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
2817 + rpz_cname_fail, /* send SERVFAIL */
2818 + rpz_cname_prepend, /* prepend CNAME as usual */
2819 + rpz_cname_stop, /* stop before prepending this CNAME */
2821 +rpz_cname_t rpz_cname(struct module_qstate* qstate,
2822 + uint8_t* oname, size_t oname_size);
2824 +#endif /* ENABLE_FASTRPZ */
2825 +#endif /* UNBOUND_FASTRPZ_RPZ_H */
2826 Index: unbound-1.7.0~rc1/fastrpz/rpz.m4
2827 ===================================================================
2829 +++ unbound-1.7.0~rc1/fastrpz/rpz.m4
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
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.
2843 +AC_DEFUN([ck_FASTRPZ],
2846 + AC_MSG_CHECKING([for librpz __attribute__s])
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]))
2856 + AC_SEARCH_LIBS(dlopen, dl)
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
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])
2877 + AC_DEFINE_UNQUOTED([FASTRPZ_LIB_OPEN], [$fastrpz_lib_open],
2878 + [0=no fastrpz 1=static link 2=dlopen()])
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]])
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]])
2895 Index: 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
2900 #include "sldns/str2wire.h"
2901 #include "sldns/parseutil.h"
2902 #include "sldns/sbuffer.h"
2903 +#ifdef ENABLE_FASTRPZ
2904 +#include "fastrpz/rpz.h"
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 */
2921 + case rpz_cname_prepend:
2922 + /* save the CNAME. */
2924 + case rpz_cname_stop:
2925 + /* Pause before adding the CNAME. */
2930 /* Add this relevant CNAME rrset to the prepend list.*/
2931 if(!iter_add_prepend_answer(qstate, iq, r))
2933 @@ -519,6 +539,9 @@ handle_cname_response(struct module_qsta
2935 /* Other rrsets in the section are ignored. */
2937 +#ifdef ENABLE_FASTRPZ
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*
2946 struct dns_msg* msg = NULL;
2947 + enum response_type type;
2949 log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
2951 @@ -1223,8 +1247,7 @@ processInitRequest(struct module_qstate*
2954 /* handle positive cache response */
2955 - enum response_type type = response_type_from_cache(msg,
2957 + type = response_type_from_cache(msg, &iq->qchase);
2958 if(verbosity >= VERB_ALGO) {
2959 log_dns_msg("msg from cache lookup", &msg->qinfo,
2961 @@ -1232,7 +1255,22 @@ processInitRequest(struct module_qstate*
2963 (int)msg->rep->prefetch_ttl);
2965 +#ifdef ENABLE_FASTRPZ
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);
2981 if(type == RESPONSE_TYPE_CNAME) {
2984 @@ -2552,6 +2590,62 @@ processQueryResponse(struct module_qstat
2985 sock_list_insert(&qstate->reply_origin,
2986 &qstate->reply->addr, qstate->reply->addrlen,
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;
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,
3011 + return error_response(qstate, id,
3012 + LDNS_RCODE_SERVFAIL);
3014 + iq->response = resp;
3015 + iq->rpz_security = resp->rep->security;
3016 + iq->rpz_rewritten = 1;
3018 + /* Send the rewritten record if it
3019 + * is not a CNAME. */
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);
3030 + iq->qchase.qname = sname;
3031 + iq->qchase.qname_len = slen;
3033 + iq->refetch_glue = 0;
3034 + iq->query_restart_count++;
3035 + iq->sent_count = 0;
3036 + iq->state = INIT_REQUEST_STATE;
3039 + case rpz_iter_resp_done:
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)
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);
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;
3067 + /* Append the RPZ SOA to rewritten CNAME chains. */
3069 + struct ub_packed_rrset_key** sets;
3072 + n = iq->response->rep->rrset_count;
3073 + sets = regional_alloc(qstate->region,
3074 + (1+n) * sizeof(*sets));
3076 + log_err("append RPZ SOA: out of memory");
3077 + return error_response(qstate, id,
3078 + LDNS_RCODE_SERVFAIL);
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;
3089 qstate->return_rcode = LDNS_RCODE_NOERROR;
3090 qstate->return_msg = iq->response;
3092 Index: 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 {
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;
3111 * Count number of time-outs. Used to prevent resolving failures when
3112 * the QNAME minimisation QTYPE is blocked. */
3113 Index: 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)
3120 struct reply_info* rep = NULL;
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)
3129 /* alloc, malloc properly (not in region, like msg is) */
3130 rep = reply_info_copy(msgrep, env->alloc, NULL);
3132 Index: 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
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"
3143 #include "respip/respip.h"
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
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);
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
3171 r->query_reply = *rep;
3172 +#ifdef ENABLE_FASTRPZ
3173 + /* The new reply structure owns the RPZ state. */
3177 if(edns->opt_list) {
3178 r->edns.opt_list = edns_opt_copy_region(edns->opt_list,
3179 Index: 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);
3192 Index: 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;
3200 + /** true to enable RPZ */
3202 + /** RPZ configuration */
3205 /** ratelimit for ip addresses. 0 is off, otherwise qps (unless overridden) */
3207 /** number of slabs for ip_ratelimit cache */
3208 Index: 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) }
3223 Index: 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
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);
3250 + OUTYY(("\nP(rpz:)\n"));
3253 +contents_rpz: contents_rpz content_rpz
3255 +content_rpz: rpz_enable | rpz_zone | rpz_option
3257 +rpz_enable: VAR_RPZ_ENABLE STRING_ARG
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);
3266 +rpz_zone: VAR_RPZ_ZONE STRING_ARG
3268 + char *new_cstr, *old_cstr;
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);
3274 + yyerror("out of memory");
3276 + cfg_parser->cfg->rpz_cstr = new_cstr;
3279 +rpz_option: VAR_RPZ_OPTION STRING_ARG
3281 + char *new_cstr, *old_cstr;
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);
3287 + yyerror("out of memory");
3289 + cfg_parser->cfg->rpz_cstr = new_cstr;
3292 pythonstart: VAR_PYTHON
3294 OUTYY(("\nP(python:)\n"));
3295 Index: 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
3303 +#ifdef ENABLE_FASTRPZ
3304 +/* Insert the RPZ SOA even with MINIMAL_RESPONSES */
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,
3312 + size_t i, setstart;
3315 + for(i=0; i<num_rrsets; i++) {
3316 + if (rep->rrsets[rrsets_before+i]->rk.type != LDNS_RR_TYPE_SOA)
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))
3324 + sldns_buffer_set_position(pkt, setstart);
3332 /** store query section in wireformat buffer, return RETVAL */
3334 insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
3335 @@ -750,6 +779,19 @@ reply_info_encode(struct query_info* qin
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)
3348 + /* no need to set TC bit, this is the additional */
3349 + sldns_buffer_write_u16_at(buffer, 10, arcount);
3353 sldns_buffer_flip(buffer);
3355 Index: 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";
3368 return "unknown_sec_status_value";
3370 Index: 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. */
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
3386 + sec_status_rpz_drop
3391 Index: 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
3396 #ifdef HAVE_OPENSSL_ERR_H
3397 #include <openssl/err.h>
3399 +#ifdef ENABLE_FASTRPZ
3400 +#include "fastrpz/rpz.h"
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 */
3409 +#ifdef ENABLE_FASTRPZ
3412 rep.c = (struct comm_point*)arg;
3413 log_assert(rep.c->type == comm_udp);
3415 @@ -674,6 +680,9 @@ comm_point_udp_callback(int fd, short ev
3417 struct sldns_buffer *buffer;
3419 +#ifdef ENABLE_FASTRPZ
3422 rep.c = (struct comm_point*)arg;
3423 log_assert(rep.c->type == comm_udp);
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);
3429 +#ifdef ENABLE_FASTRPZ
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. */
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);
3439 +#ifdef ENABLE_FASTRPZ
3445 @@ -2965,6 +2980,9 @@ comm_point_drop_reply(struct comm_reply*
3447 log_assert(repinfo && repinfo->c);
3448 log_assert(repinfo->c->type != comm_tcp_accept);
3449 +#ifdef ENABLE_FASTRPZ
3452 if(repinfo->c->type == comm_udp)
3454 reclaim_tcp_handler(repinfo->c);
3455 @@ -2984,6 +3002,9 @@ comm_point_start_listening(struct comm_p
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);
3462 if(c->type == comm_tcp_accept && !c->tcp_free) {
3463 /* no use to start listening no free slots. */
3465 Index: 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) */
3472 /* DnsCrypt context */
3473 +#ifdef ENABLE_FASTRPZ
3474 + /** per-request RPZ state */
3475 + struct commreply_rpz* rpz;
3478 uint8_t client_nonce[crypto_box_HALF_NONCEBYTES];
3479 uint8_t nmkey[crypto_box_BEFORENMBYTES];
3480 Index: 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*
3486 /* NSEC proof did not work, try next */
3488 +#ifdef ENABLE_FASTRPZ
3489 + case sec_status_rpz_rewritten:
3490 + case sec_status_rpz_drop:
3491 + fatal_exit("impossible RPZ sec_status");
3496 sec = nsec3_prove_nods(qstate->env, ve,
3497 @@ -2721,6 +2727,12 @@ ds_response_to_ke(struct module_qstate*
3499 /* NSEC3 proof did not work */
3501 +#ifdef ENABLE_FASTRPZ
3502 + case sec_status_rpz_rewritten:
3503 + case sec_status_rpz_drop:
3504 + fatal_exit("impossible RPZ sec_status");
3509 /* Apparently, no available NSEC/NSEC3 proved NODATA, so