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