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