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