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