summaryrefslogtreecommitdiff
path: root/usr.sbin/unbound
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/unbound')
-rw-r--r--usr.sbin/unbound/.github/FUNDING.yml12
-rw-r--r--usr.sbin/unbound/services/rpz.c1015
-rw-r--r--usr.sbin/unbound/services/rpz.h201
-rw-r--r--usr.sbin/unbound/testcode/petal.c2
-rw-r--r--usr.sbin/unbound/testcode/replay.h20
-rw-r--r--usr.sbin/unbound/testcode/unitdname.c29
6 files changed, 1269 insertions, 10 deletions
diff --git a/usr.sbin/unbound/.github/FUNDING.yml b/usr.sbin/unbound/.github/FUNDING.yml
new file mode 100644
index 00000000000..ecee84aa413
--- /dev/null
+++ b/usr.sbin/unbound/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: ['https://nlnetlabs.nl/funding/']
diff --git a/usr.sbin/unbound/services/rpz.c b/usr.sbin/unbound/services/rpz.c
new file mode 100644
index 00000000000..643b20c91d2
--- /dev/null
+++ b/usr.sbin/unbound/services/rpz.c
@@ -0,0 +1,1015 @@
+/*
+ * services/rpz.c - rpz service
+ *
+ * Copyright (c) 2019, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to enable RPZ service.
+ */
+
+#include "config.h"
+#include "services/rpz.h"
+#include "util/config_file.h"
+#include "sldns/wire2str.h"
+#include "sldns/str2wire.h"
+#include "util/data/dname.h"
+#include "util/net_help.h"
+#include "util/log.h"
+#include "util/data/dname.h"
+#include "util/locks.h"
+#include "util/regional.h"
+
+/** string for RPZ action enum */
+const char*
+rpz_action_to_string(enum rpz_action a)
+{
+ switch(a) {
+ case RPZ_NXDOMAIN_ACTION: return "nxdomain";
+ case RPZ_NODATA_ACTION: return "nodata";
+ case RPZ_PASSTHRU_ACTION: return "passthru";
+ case RPZ_DROP_ACTION: return "drop";
+ case RPZ_TCP_ONLY_ACTION: return "tcp_only";
+ case RPZ_INVALID_ACTION: return "invalid";
+ case RPZ_LOCAL_DATA_ACTION: return "local_data";
+ case RPZ_DISABLED_ACTION: return "disabled";
+ case RPZ_CNAME_OVERRIDE_ACTION: return "cname_override";
+ case RPZ_NO_OVERRIDE_ACTION: return "no_override";
+ }
+ return "unknown";
+}
+
+/** RPZ action enum for config string */
+static enum rpz_action
+rpz_config_to_action(char* a)
+{
+ if(strcmp(a, "nxdomain") == 0)
+ return RPZ_NXDOMAIN_ACTION;
+ else if(strcmp(a, "nodata") == 0)
+ return RPZ_NODATA_ACTION;
+ else if(strcmp(a, "passthru") == 0)
+ return RPZ_PASSTHRU_ACTION;
+ else if(strcmp(a, "drop") == 0)
+ return RPZ_DROP_ACTION;
+ else if(strcmp(a, "tcp_only") == 0)
+ return RPZ_TCP_ONLY_ACTION;
+ else if(strcmp(a, "cname") == 0)
+ return RPZ_CNAME_OVERRIDE_ACTION;
+ else if(strcmp(a, "disabled") == 0)
+ return RPZ_DISABLED_ACTION;
+ return RPZ_INVALID_ACTION;
+}
+
+/** string for RPZ trigger enum */
+static const char*
+rpz_trigger_to_string(enum rpz_trigger r)
+{
+ switch(r) {
+ case RPZ_QNAME_TRIGGER: return "qname";
+ case RPZ_CLIENT_IP_TRIGGER: return "client_ip";
+ case RPZ_RESPONSE_IP_TRIGGER: return "response_ip";
+ case RPZ_NSDNAME_TRIGGER: return "nsdname";
+ case RPZ_NSIP_TRIGGER: return "nsip";
+ case RPZ_INVALID_TRIGGER: return "invalid";
+ }
+ return "unknown";
+}
+
+/**
+ * Get the label that is just before the root label.
+ * @param dname: dname to work on
+ * @param maxdnamelen: maximum length of the dname
+ * @return: pointer to TLD label, NULL if not found or invalid dname
+ */
+static uint8_t*
+get_tld_label(uint8_t* dname, size_t maxdnamelen)
+{
+ uint8_t* prevlab = dname;
+ size_t dnamelen = 0;
+
+ /* one byte needed for label length */
+ if(dnamelen+1 > maxdnamelen)
+ return NULL;
+
+ /* only root label */
+ if(*dname == 0)
+ return NULL;
+
+ while(*dname) {
+ dnamelen += ((size_t)*dname)+1;
+ if(dnamelen+1 > maxdnamelen)
+ return NULL;
+ dname = dname+((size_t)*dname)+1;
+ if(*dname != 0)
+ prevlab = dname;
+ }
+ return prevlab;
+}
+
+/**
+ * Classify RPZ action for RR type/rdata
+ * @param rr_type: the RR type
+ * @param rdatawl: RDATA with 2 bytes length
+ * @param rdatalen: the length of rdatawl (including its 2 bytes length)
+ * @return: the RPZ action
+ */
+static enum rpz_action
+rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
+{
+ char* endptr;
+ uint8_t* rdata;
+ int rdatalabs;
+ uint8_t* tldlab = NULL;
+
+ switch(rr_type) {
+ case LDNS_RR_TYPE_SOA:
+ case LDNS_RR_TYPE_NS:
+ case LDNS_RR_TYPE_DNAME:
+ /* all DNSSEC-related RRs must be ignored */
+ case LDNS_RR_TYPE_DNSKEY:
+ case LDNS_RR_TYPE_DS:
+ case LDNS_RR_TYPE_RRSIG:
+ case LDNS_RR_TYPE_NSEC:
+ case LDNS_RR_TYPE_NSEC3:
+ return RPZ_INVALID_ACTION;
+ case LDNS_RR_TYPE_CNAME:
+ break;
+ default:
+ return RPZ_LOCAL_DATA_ACTION;
+ }
+
+ /* use CNAME target to determine RPZ action */
+ log_assert(rr_type == LDNS_RR_TYPE_CNAME);
+ if(rdatalen < 3)
+ return RPZ_INVALID_ACTION;
+
+ rdata = rdatawl + 2; /* 2 bytes of rdata length */
+ if(dname_valid(rdata, rdatalen-2) != rdatalen-2)
+ return RPZ_INVALID_ACTION;
+
+ rdatalabs = dname_count_labels(rdata);
+ if(rdatalabs == 1)
+ return RPZ_NXDOMAIN_ACTION;
+ else if(rdatalabs == 2) {
+ if(dname_subdomain_c(rdata, (uint8_t*)&"\001*\000"))
+ return RPZ_NODATA_ACTION;
+ else if(dname_subdomain_c(rdata,
+ (uint8_t*)&"\014rpz-passthru\000"))
+ return RPZ_PASSTHRU_ACTION;
+ else if(dname_subdomain_c(rdata, (uint8_t*)&"\010rpz-drop\000"))
+ return RPZ_DROP_ACTION;
+ else if(dname_subdomain_c(rdata,
+ (uint8_t*)&"\014rpz-tcp-only\000"))
+ return RPZ_TCP_ONLY_ACTION;
+ }
+
+ /* all other TLDs starting with "rpz-" are invalid */
+ tldlab = get_tld_label(rdata, rdatalen-2);
+ if(tldlab && dname_lab_startswith(tldlab, "rpz-", &endptr))
+ return RPZ_INVALID_ACTION;
+
+ /* no special label found */
+ return RPZ_LOCAL_DATA_ACTION;
+}
+
+static enum localzone_type
+rpz_action_to_localzone_type(enum rpz_action a)
+{
+ switch(a) {
+ case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain;
+ case RPZ_NODATA_ACTION: return local_zone_always_nodata;
+ case RPZ_DROP_ACTION: return local_zone_always_deny;
+ case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
+ case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
+ case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect;
+ case RPZ_INVALID_ACTION: /* fallthrough */
+ case RPZ_TCP_ONLY_ACTION: /* fallthrough */
+ default: return local_zone_invalid;
+ }
+}
+
+enum respip_action
+rpz_action_to_respip_action(enum rpz_action a)
+{
+ switch(a) {
+ case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain;
+ case RPZ_NODATA_ACTION: return respip_always_nodata;
+ case RPZ_DROP_ACTION: return respip_always_deny;
+ case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
+ case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
+ case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect;
+ case RPZ_INVALID_ACTION: /* fallthrough */
+ case RPZ_TCP_ONLY_ACTION: /* fallthrough */
+ default: return respip_invalid;
+ }
+}
+
+static enum rpz_action
+localzone_type_to_rpz_action(enum localzone_type lzt)
+{
+ switch(lzt) {
+ case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
+ case local_zone_always_nodata: return RPZ_NODATA_ACTION;
+ case local_zone_always_deny: return RPZ_DROP_ACTION;
+ case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION;
+ case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION;
+ case local_zone_invalid:
+ default:
+ return RPZ_INVALID_ACTION;
+ }
+}
+
+enum rpz_action
+respip_action_to_rpz_action(enum respip_action a)
+{
+ switch(a) {
+ case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
+ case respip_always_nodata: return RPZ_NODATA_ACTION;
+ case respip_always_deny: return RPZ_DROP_ACTION;
+ case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
+ case respip_redirect: return RPZ_LOCAL_DATA_ACTION;
+ case respip_invalid:
+ default:
+ return RPZ_INVALID_ACTION;
+ }
+}
+
+/**
+ * Get RPZ trigger for dname
+ * @param dname: dname containing RPZ trigger
+ * @param dname_len: length of the dname
+ * @return: RPZ trigger enum
+ */
+static enum rpz_trigger
+rpz_dname_to_trigger(uint8_t* dname, size_t dname_len)
+{
+ uint8_t* tldlab;
+ char* endptr;
+
+ if(dname_valid(dname, dname_len) != dname_len)
+ return RPZ_INVALID_TRIGGER;
+
+ tldlab = get_tld_label(dname, dname_len);
+ if(!tldlab || !dname_lab_startswith(tldlab, "rpz-", &endptr))
+ return RPZ_QNAME_TRIGGER;
+
+ if(dname_subdomain_c(tldlab,
+ (uint8_t*)&"\015rpz-client-ip\000"))
+ return RPZ_CLIENT_IP_TRIGGER;
+ else if(dname_subdomain_c(tldlab, (uint8_t*)&"\006rpz-ip\000"))
+ return RPZ_RESPONSE_IP_TRIGGER;
+ else if(dname_subdomain_c(tldlab, (uint8_t*)&"\013rpz-nsdname\000"))
+ return RPZ_NSDNAME_TRIGGER;
+ else if(dname_subdomain_c(tldlab, (uint8_t*)&"\010rpz-nsip\000"))
+ return RPZ_NSIP_TRIGGER;
+
+ return RPZ_QNAME_TRIGGER;
+}
+
+void rpz_delete(struct rpz* r)
+{
+ if(!r)
+ return;
+ local_zones_delete(r->local_zones);
+ respip_set_delete(r->respip_set);
+ regional_destroy(r->region);
+ free(r->taglist);
+ free(r->log_name);
+ free(r);
+}
+
+int
+rpz_clear(struct rpz* r)
+{
+ /* must hold write lock on auth_zone */
+ local_zones_delete(r->local_zones);
+ respip_set_delete(r->respip_set);
+ if(!(r->local_zones = local_zones_create())){
+ return 0;
+ }
+ if(!(r->respip_set = respip_set_create())) {
+ return 0;
+ }
+ return 1;
+}
+
+void
+rpz_finish_config(struct rpz* r)
+{
+ lock_rw_wrlock(&r->respip_set->lock);
+ addr_tree_init_parents(&r->respip_set->ip_tree);
+ lock_rw_unlock(&r->respip_set->lock);
+}
+
+/** new rrset containing CNAME override, does not yet contain a dname */
+static struct ub_packed_rrset_key*
+new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen)
+{
+ struct ub_packed_rrset_key* rrset;
+ struct packed_rrset_data* pd;
+ uint16_t rdlength = htons(ctlen);
+ rrset = (struct ub_packed_rrset_key*)regional_alloc_zero(region,
+ sizeof(*rrset));
+ if(!rrset) {
+ log_err("out of memory");
+ return NULL;
+ }
+ rrset->entry.key = rrset;
+ pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd));
+ if(!pd) {
+ log_err("out of memory");
+ return NULL;
+ }
+ pd->trust = rrset_trust_prim_noglue;
+ pd->security = sec_status_insecure;
+
+ pd->count = 1;
+ pd->rr_len = regional_alloc_zero(region, sizeof(*pd->rr_len));
+ pd->rr_ttl = regional_alloc_zero(region, sizeof(*pd->rr_ttl));
+ pd->rr_data = regional_alloc_zero(region, sizeof(*pd->rr_data));
+ if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
+ log_err("out of memory");
+ return NULL;
+ }
+ pd->rr_len[0] = ctlen+2;
+ pd->rr_ttl[0] = 3600;
+ pd->rr_data[0] = regional_alloc_zero(region, 2 /* rdlength */ + ctlen);
+ if(!pd->rr_data[0]) {
+ log_err("out of memory");
+ return NULL;
+ }
+ memmove(pd->rr_data[0], &rdlength, 2);
+ memmove(pd->rr_data[0]+2, ct, ctlen);
+
+ rrset->entry.data = pd;
+ rrset->rk.type = htons(LDNS_RR_TYPE_CNAME);
+ rrset->rk.rrset_class = htons(LDNS_RR_CLASS_IN);
+ return rrset;
+}
+
+struct rpz*
+rpz_create(struct config_auth* p)
+{
+ struct rpz* r = calloc(1, sizeof(*r));
+ if(!r)
+ goto err;
+
+ r->region = regional_create_custom(sizeof(struct regional));
+ if(!r->region) {
+ goto err;
+ }
+
+ if(!(r->local_zones = local_zones_create())){
+ goto err;
+ }
+ if(!(r->respip_set = respip_set_create())) {
+ goto err;
+ }
+ r->taglistlen = p->rpz_taglistlen;
+ r->taglist = memdup(p->rpz_taglist, r->taglistlen);
+ if(p->rpz_action_override) {
+ r->action_override = rpz_config_to_action(p->rpz_action_override);
+ }
+ else
+ r->action_override = RPZ_NO_OVERRIDE_ACTION;
+
+ if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
+ uint8_t nm[LDNS_MAX_DOMAINLEN+1];
+ size_t nmlen = sizeof(nm);
+
+ if(!p->rpz_cname) {
+ log_err("RPZ override with cname action found, but no "
+ "rpz-cname-override configured");
+ goto err;
+ }
+
+ if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) {
+ log_err("cannot parse RPZ cname override: %s",
+ p->rpz_cname);
+ goto err;
+ }
+ r->cname_override = new_cname_override(r->region, nm, nmlen);
+ if(!r->cname_override) {
+ goto err;
+ }
+ }
+ r->log = p->rpz_log;
+ if(p->rpz_log_name) {
+ if(!(r->log_name = strdup(p->rpz_log_name))) {
+ log_err("malloc failure on RPZ log_name strdup");
+ goto err;
+ }
+ }
+ return r;
+err:
+ if(r) {
+ if(r->local_zones)
+ local_zones_delete(r->local_zones);
+ if(r->respip_set)
+ respip_set_delete(r->respip_set);
+ if(r->taglist)
+ free(r->taglist);
+ free(r);
+ }
+ return NULL;
+}
+
+/**
+ * Remove RPZ zone name from dname
+ * Copy dname to newdname, without the originlen number of trailing bytes
+ */
+static size_t
+strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen,
+ uint8_t* newdname, size_t maxnewdnamelen)
+{
+ size_t newdnamelen;
+ if(dnamelen < originlen)
+ return 0;
+ newdnamelen = dnamelen - originlen;
+ if(newdnamelen+1 > maxnewdnamelen)
+ return 0;
+ memmove(newdname, dname, newdnamelen);
+ newdname[newdnamelen] = 0;
+ return newdnamelen + 1; /* + 1 for root label */
+}
+
+/** Insert RR into RPZ's local-zone */
+static void
+rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
+ uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
+{
+ struct local_zone* z;
+ enum localzone_type tp = local_zone_always_transparent;
+ int dnamelabs = dname_count_labels(dname);
+ char* rrstr;
+ int newzone = 0;
+
+ if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) {
+ verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s",
+ rpz_action_to_string(a));
+ free(dname);
+ return;
+ }
+
+ lock_rw_wrlock(&r->local_zones->lock);
+ /* exact match */
+ z = local_zones_find(r->local_zones, dname, dnamelen, dnamelabs,
+ LDNS_RR_CLASS_IN);
+ if(z && a != RPZ_LOCAL_DATA_ACTION) {
+ rrstr = sldns_wire2str_rr(rr, rr_len);
+ if(!rrstr) {
+ log_err("malloc error while inserting RPZ qname "
+ "trigger");
+ free(dname);
+ lock_rw_unlock(&r->local_zones->lock);
+ return;
+ }
+ verbose(VERB_ALGO, "RPZ: skipping duplicate record: '%s'",
+ rrstr);
+ free(rrstr);
+ free(dname);
+ lock_rw_unlock(&r->local_zones->lock);
+ return;
+ }
+ if(!z) {
+ tp = rpz_action_to_localzone_type(a);
+ if(!(z = local_zones_add_zone(r->local_zones, dname, dnamelen,
+ dnamelabs, rrclass, tp))) {
+ log_warn("RPZ create failed");
+ lock_rw_unlock(&r->local_zones->lock);
+ /* dname will be free'd in failed local_zone_create() */
+ return;
+ }
+ newzone = 1;
+ }
+ if(a == RPZ_LOCAL_DATA_ACTION) {
+ rrstr = sldns_wire2str_rr(rr, rr_len);
+ if(!rrstr) {
+ log_err("malloc error while inserting RPZ qname "
+ "trigger");
+ free(dname);
+ lock_rw_unlock(&r->local_zones->lock);
+ return;
+ }
+ lock_rw_wrlock(&z->lock);
+ local_zone_enter_rr(z, dname, dnamelen, dnamelabs,
+ rrtype, rrclass, ttl, rdata, rdata_len, rrstr);
+ lock_rw_unlock(&z->lock);
+ free(rrstr);
+ }
+ if(!newzone)
+ free(dname);
+ lock_rw_unlock(&r->local_zones->lock);
+ return;
+}
+
+/** Insert RR into RPZ's respip_set */
+static int
+rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
+ uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
+{
+ struct resp_addr* node;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int net, af;
+ char* rrstr;
+ enum respip_action respa = rpz_action_to_respip_action(a);
+
+ if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION ||
+ respa == respip_invalid) {
+ verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s",
+ rpz_action_to_string(a));
+ return 0;
+ }
+
+ if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
+ return 0;
+
+ lock_rw_wrlock(&r->respip_set->lock);
+ rrstr = sldns_wire2str_rr(rr, rr_len);
+ if(!rrstr) {
+ log_err("malloc error while inserting RPZ respip trigger");
+ lock_rw_unlock(&r->respip_set->lock);
+ return 0;
+ }
+ if(!(node=respip_sockaddr_find_or_create(r->respip_set, &addr, addrlen,
+ net, 1, rrstr))) {
+ lock_rw_unlock(&r->respip_set->lock);
+ free(rrstr);
+ return 0;
+ }
+
+ lock_rw_wrlock(&node->lock);
+ lock_rw_unlock(&r->respip_set->lock);
+ node->action = respa;
+
+ if(a == RPZ_LOCAL_DATA_ACTION) {
+ respip_enter_rr(r->respip_set->region, node, rrtype,
+ rrclass, ttl, rdata, rdata_len, rrstr, "");
+ }
+ lock_rw_unlock(&node->lock);
+ free(rrstr);
+ return 1;
+}
+
+int
+rpz_insert_rr(struct rpz* r, size_t aznamelen, uint8_t* dname,
+ size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl,
+ uint8_t* rdatawl, size_t rdatalen, uint8_t* rr, size_t rr_len)
+{
+ size_t policydnamelen;
+ /* name is free'd in local_zone delete */
+ enum rpz_trigger t;
+ enum rpz_action a;
+ uint8_t* policydname;
+
+ log_assert(dnamelen >= aznamelen);
+ if(!(policydname = calloc(1, (dnamelen-aznamelen)+1)))
+ return 0;
+
+ a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
+ if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
+ policydname, (dnamelen-aznamelen)+1))) {
+ free(policydname);
+ return 0;
+ }
+ t = rpz_dname_to_trigger(policydname, policydnamelen);
+ if(t == RPZ_INVALID_TRIGGER) {
+ free(policydname);
+ verbose(VERB_ALGO, "RPZ: skipping invalid trigger");
+ return 1;
+ }
+ if(t == RPZ_QNAME_TRIGGER) {
+ rpz_insert_qname_trigger(r, policydname, policydnamelen,
+ a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
+ rr_len);
+ }
+ else if(t == RPZ_RESPONSE_IP_TRIGGER) {
+ rpz_insert_response_ip_trigger(r, policydname, policydnamelen,
+ a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
+ rr_len);
+ free(policydname);
+ }
+ else {
+ free(policydname);
+ verbose(VERB_ALGO, "RPZ: skipping unsupported trigger: %s",
+ rpz_trigger_to_string(t));
+ }
+ return 1;
+}
+
+/**
+ * Find RPZ local-zone by qname.
+ * @param r: rpz containing local-zone tree
+ * @param qname: qname
+ * @param qname_len: length of qname
+ * @param qclass: qclass
+ * @param only_exact: if 1 only excact (non wildcard) matches are returned
+ * @param wr: get write lock for local-zone if 1, read lock if 0
+ * @param zones_keep_lock: if set do not release the r->local_zones lock, this
+ * makes the caller of this function responsible for releasing the lock.
+ * @return: NULL or local-zone holding rd or wr lock
+ */
+static struct local_zone*
+rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
+ int only_exact, int wr, int zones_keep_lock)
+{
+ uint8_t* ce;
+ size_t ce_len, ce_labs;
+ uint8_t wc[LDNS_MAX_DOMAINLEN+1];
+ int exact;
+ struct local_zone* z = NULL;
+ if(wr) {
+ lock_rw_wrlock(&r->local_zones->lock);
+ } else {
+ lock_rw_rdlock(&r->local_zones->lock);
+ }
+ z = local_zones_find_le(r->local_zones, qname, qname_len,
+ dname_count_labels(qname),
+ LDNS_RR_CLASS_IN, &exact);
+ if(!z || (only_exact && !exact)) {
+ lock_rw_unlock(&r->local_zones->lock);
+ return NULL;
+ }
+ if(wr) {
+ lock_rw_wrlock(&z->lock);
+ } else {
+ lock_rw_rdlock(&z->lock);
+ }
+ if(!zones_keep_lock) {
+ lock_rw_unlock(&r->local_zones->lock);
+ }
+
+ if(exact)
+ return z;
+
+ /* No exact match found, lookup wildcard. closest encloser must
+ * be the shared parent between the qname and the best local
+ * zone match, append '*' to that and do another lookup. */
+
+ ce = dname_get_shared_topdomain(z->name, qname);
+ if(!ce /* should not happen */ || !*ce /* root */) {
+ lock_rw_unlock(&z->lock);
+ if(zones_keep_lock) {
+ lock_rw_unlock(&r->local_zones->lock);
+ }
+ return NULL;
+ }
+ ce_labs = dname_count_size_labels(ce, &ce_len);
+ if(ce_len+2 > sizeof(wc)) {
+ lock_rw_unlock(&z->lock);
+ if(zones_keep_lock) {
+ lock_rw_unlock(&r->local_zones->lock);
+ }
+ return NULL;
+ }
+ wc[0] = 1; /* length of wildcard label */
+ wc[1] = (uint8_t)'*'; /* wildcard label */
+ memmove(wc+2, ce, ce_len);
+ lock_rw_unlock(&z->lock);
+
+ if(!zones_keep_lock) {
+ if(wr) {
+ lock_rw_wrlock(&r->local_zones->lock);
+ } else {
+ lock_rw_rdlock(&r->local_zones->lock);
+ }
+ }
+ z = local_zones_find_le(r->local_zones, wc,
+ ce_len+2, ce_labs+1, qclass, &exact);
+ if(!z || !exact) {
+ lock_rw_unlock(&r->local_zones->lock);
+ return NULL;
+ }
+ if(wr) {
+ lock_rw_wrlock(&z->lock);
+ } else {
+ lock_rw_rdlock(&z->lock);
+ }
+ if(!zones_keep_lock) {
+ lock_rw_unlock(&r->local_zones->lock);
+ }
+ return z;
+}
+
+/**
+ * Remove RR from RPZ's local-data
+ * @param z: local-zone for RPZ, holding write lock
+ * @param policydname: dname of RR to remove
+ * @param policydnamelen: lenth of policydname
+ * @param rr_type: RR type of RR to remove
+ * @param rdata: rdata of RR to remove
+ * @param rdatalen: length of rdata
+ * @return: 1 if zone must be removed after RR deletion
+ */
+static int
+rpz_data_delete_rr(struct local_zone* z, uint8_t* policydname,
+ size_t policydnamelen, uint16_t rr_type, uint8_t* rdata,
+ size_t rdatalen)
+{
+ struct local_data* ld;
+ struct packed_rrset_data* d;
+ size_t index;
+ ld = local_zone_find_data(z, policydname, policydnamelen,
+ dname_count_labels(policydname));
+ if(ld) {
+ struct local_rrset* prev=NULL, *p=ld->rrsets;
+ while(p && ntohs(p->rrset->rk.type) != rr_type) {
+ prev = p;
+ p = p->next;
+ }
+ if(!p)
+ return 0;
+ d = (struct packed_rrset_data*)p->rrset->entry.data;
+ if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) {
+ if(d->count == 1) {
+ /* no memory recycling for zone deletions ... */
+ if(prev) prev->next = p->next;
+ else ld->rrsets = p->next;
+ }
+ if(d->count > 1) {
+ if(!local_rrset_remove_rr(d, index))
+ return 0;
+ }
+ }
+ }
+ if(ld && ld->rrsets)
+ return 0;
+ return 1;
+}
+
+/**
+ * Remove RR from RPZ's respip set
+ * @param raddr: respip node
+ * @param rr_type: RR type of RR to remove
+ * @param rdata: rdata of RR to remove
+ * @param rdatalen: length of rdata
+ * @return: 1 if zone must be removed after RR deletion
+ */
+static int
+rpz_rrset_delete_rr(struct resp_addr* raddr, uint16_t rr_type, uint8_t* rdata,
+ size_t rdatalen)
+{
+ size_t index;
+ struct packed_rrset_data* d;
+ if(!raddr->data)
+ return 1;
+ d = raddr->data->entry.data;
+ if(ntohs(raddr->data->rk.type) != rr_type) {
+ return 0;
+ }
+ if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) {
+ if(d->count == 1) {
+ /* regional alloc'd */
+ raddr->data->entry.data = NULL;
+ raddr->data = NULL;
+ return 1;
+ }
+ if(d->count > 1) {
+ if(!local_rrset_remove_rr(d, index))
+ return 0;
+ }
+ }
+ return 0;
+
+}
+
+/** Remove RR from RPZ's local-zone */
+static void
+rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
+ uint8_t* rdatawl, size_t rdatalen)
+{
+ struct local_zone* z;
+ int delete_zone = 1;
+ z = rpz_find_zone(r, dname, dnamelen, rr_class,
+ 1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/);
+ if(!z) {
+ verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, "
+ "RPZ domain not found");
+ return;
+ }
+ if(a == RPZ_LOCAL_DATA_ACTION)
+ delete_zone = rpz_data_delete_rr(z, dname,
+ dnamelen, rr_type, rdatawl, rdatalen);
+ else if(a != localzone_type_to_rpz_action(z->type)) {
+ return;
+ }
+ lock_rw_unlock(&z->lock);
+ if(delete_zone) {
+ local_zones_del_zone(r->local_zones, z);
+ }
+ lock_rw_unlock(&r->local_zones->lock);
+ return;
+}
+
+static void
+rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
+{
+ struct resp_addr* node;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int net, af;
+ int delete_respip = 1;
+
+ if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
+ return;
+
+ lock_rw_wrlock(&r->respip_set->lock);
+ if(!(node = (struct resp_addr*)addr_tree_find(
+ &r->respip_set->ip_tree, &addr, addrlen, net))) {
+ verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, "
+ "RPZ domain not found");
+ lock_rw_unlock(&r->respip_set->lock);
+ return;
+ }
+
+ lock_rw_wrlock(&node->lock);
+ if(a == RPZ_LOCAL_DATA_ACTION) {
+ /* remove RR, signal whether RR can be removed */
+ delete_respip = rpz_rrset_delete_rr(node, rr_type, rdatawl,
+ rdatalen);
+ }
+ lock_rw_unlock(&node->lock);
+ if(delete_respip)
+ respip_sockaddr_delete(r->respip_set, node);
+ lock_rw_unlock(&r->respip_set->lock);
+}
+
+void
+rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, size_t dnamelen,
+ uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen)
+{
+ size_t policydnamelen;
+ enum rpz_trigger t;
+ enum rpz_action a;
+ uint8_t* policydname;
+
+ if(!(policydname = calloc(1, LDNS_MAX_DOMAINLEN + 1)))
+ return;
+
+ a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
+ if(a == RPZ_INVALID_ACTION) {
+ free(policydname);
+ return;
+ }
+ if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
+ policydname, LDNS_MAX_DOMAINLEN + 1))) {
+ free(policydname);
+ return;
+ }
+ t = rpz_dname_to_trigger(policydname, policydnamelen);
+ if(t == RPZ_QNAME_TRIGGER) {
+ rpz_remove_qname_trigger(r, policydname, policydnamelen, a,
+ rr_type, rr_class, rdatawl, rdatalen);
+ } else if(t == RPZ_RESPONSE_IP_TRIGGER) {
+ rpz_remove_response_ip_trigger(r, policydname, policydnamelen,
+ a, rr_type, rdatawl, rdatalen);
+ }
+ free(policydname);
+}
+
+/** print log information for an applied RPZ policy. Based on local-zone's
+ * lz_inform_print().
+ */
+static void
+log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo,
+ struct comm_reply* repinfo, char* log_name)
+{
+ char ip[128], txt[512];
+ char dnamestr[LDNS_MAX_DOMAINLEN+1];
+ uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port);
+ dname_str(dname, dnamestr);
+ addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
+ if(log_name)
+ snprintf(txt, sizeof(txt), "RPZ applied [%s] %s %s %s@%u",
+ log_name, dnamestr, rpz_action_to_string(a), ip,
+ (unsigned)port);
+ else
+ snprintf(txt, sizeof(txt), "RPZ applied %s %s %s@%u",
+ dnamestr, rpz_action_to_string(a), ip, (unsigned)port);
+ log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
+}
+
+int
+rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
+ struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
+ struct regional* temp, struct comm_reply* repinfo,
+ uint8_t* taglist, size_t taglen, struct ub_server_stats* stats)
+{
+ struct rpz* r;
+ int ret;
+ enum localzone_type lzt;
+ struct local_zone* z = NULL;
+ struct local_data* ld = NULL;
+ lock_rw_rdlock(&az->rpz_lock);
+ for(r = az->rpz_first; r; r = r->next) {
+ if(!r->taglist || taglist_intersect(r->taglist,
+ r->taglistlen, taglist, taglen)) {
+ z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len,
+ qinfo->qclass, 0, 0, 0);
+ if(z && r->action_override == RPZ_DISABLED_ACTION) {
+ if(r->log)
+ log_rpz_apply(z->name,
+ r->action_override,
+ qinfo, repinfo, r->log_name);
+ /* TODO only register stats when stats_extended?
+ * */
+ stats->rpz_action[r->action_override]++;
+ lock_rw_unlock(&z->lock);
+ z = NULL;
+ }
+ if(z)
+ break;
+ }
+ }
+ lock_rw_unlock(&az->rpz_lock);
+ if(!z)
+ return 0;
+
+
+ if(r->action_override == RPZ_NO_OVERRIDE_ACTION)
+ lzt = z->type;
+ else
+ lzt = rpz_action_to_localzone_type(r->action_override);
+
+ if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
+ qinfo->local_alias =
+ regional_alloc_zero(temp, sizeof(struct local_rrset));
+ if(!qinfo->local_alias) {
+ lock_rw_unlock(&z->lock);
+ return 0; /* out of memory */
+ }
+ qinfo->local_alias->rrset =
+ regional_alloc_init(temp, r->cname_override,
+ sizeof(*r->cname_override));
+ if(!qinfo->local_alias->rrset) {
+ lock_rw_unlock(&z->lock);
+ return 0; /* out of memory */
+ }
+ qinfo->local_alias->rrset->rk.dname = qinfo->qname;
+ qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
+ if(r->log)
+ log_rpz_apply(z->name, RPZ_CNAME_OVERRIDE_ACTION,
+ qinfo, repinfo, r->log_name);
+ stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
+ lock_rw_unlock(&z->lock);
+ return 0;
+ }
+
+ if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo,
+ edns, repinfo, buf, temp, dname_count_labels(qinfo->qname),
+ &ld, lzt, -1, NULL, 0, NULL, 0)) {
+ if(r->log)
+ log_rpz_apply(z->name,
+ localzone_type_to_rpz_action(lzt), qinfo,
+ repinfo, r->log_name);
+ stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
+ lock_rw_unlock(&z->lock);
+ return !qinfo->local_alias;
+ }
+
+ ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp,
+ 0 /* no local data used */, lzt);
+ if(r->log)
+ log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt),
+ qinfo, repinfo, r->log_name);
+ stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
+ lock_rw_unlock(&z->lock);
+
+ return ret;
+}
diff --git a/usr.sbin/unbound/services/rpz.h b/usr.sbin/unbound/services/rpz.h
new file mode 100644
index 00000000000..676a4f2a840
--- /dev/null
+++ b/usr.sbin/unbound/services/rpz.h
@@ -0,0 +1,201 @@
+/*
+ * services/rpz.h - rpz service
+ *
+ * Copyright (c) 2019, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to enable RPZ service.
+ */
+
+#ifndef SERVICES_RPZ_H
+#define SERVICES_RPZ_H
+
+#include "services/localzone.h"
+#include "util/locks.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "services/authzone.h"
+#include "sldns/sbuffer.h"
+#include "daemon/stats.h"
+#include "respip/respip.h"
+
+/**
+ * RPZ triggers, only the QNAME trigger is currently supported in Unbound.
+ */
+enum rpz_trigger {
+ RPZ_QNAME_TRIGGER = 0,
+ /* unsupported triggers */
+ RPZ_CLIENT_IP_TRIGGER, /* rpz-client-ip */
+ RPZ_RESPONSE_IP_TRIGGER, /* rpz-ip */
+ RPZ_NSDNAME_TRIGGER, /* rpz-nsdname */
+ RPZ_NSIP_TRIGGER, /* rpz-nsip */
+ RPZ_INVALID_TRIGGER, /* dname does not contain valid trigger */
+};
+
+/**
+ * RPZ actions.
+ */
+enum rpz_action {
+ RPZ_NXDOMAIN_ACTION = 0,/* CNAME . */
+ RPZ_NODATA_ACTION, /* CNAME *. */
+ RPZ_PASSTHRU_ACTION, /* CNAME rpz-passthru. */
+ RPZ_DROP_ACTION, /* CNAME rpz-drop. */
+ RPZ_TCP_ONLY_ACTION, /* CNAME rpz-tcp-only. */
+ RPZ_INVALID_ACTION, /* CNAME with (child of) TLD starting with
+ "rpz-" in target, SOA, NS, DNAME and
+ DNSSEC-related records. */
+ RPZ_LOCAL_DATA_ACTION, /* anything else */
+ /* RPZ override actions */
+ RPZ_DISABLED_ACTION, /* RPZ action disabled using override */
+ RPZ_NO_OVERRIDE_ACTION, /* RPZ action no override*/
+ RPZ_CNAME_OVERRIDE_ACTION, /* RPZ CNAME action override*/
+};
+
+/**
+ * RPZ containing policies. Pointed to from corresponding auth-zone. Part of a
+ * linked list to keep configuration order. Iterating or changing the linked
+ * list requires the rpz_lock from struct auth_zones.
+ */
+struct rpz {
+ struct local_zones* local_zones;
+ struct respip_set* respip_set;
+ uint8_t* taglist;
+ size_t taglistlen;
+ enum rpz_action action_override;
+ struct ub_packed_rrset_key* cname_override;
+ int log;
+ char* log_name;
+ struct rpz* next;
+ struct rpz* prev;
+ struct regional* region;
+};
+
+/**
+ * Create policy from RR and add to this RPZ.
+ * @param r: the rpz to add the policy to.
+ * @param aznamelen: the length of the auth-zone name
+ * @param dname: dname of the RR
+ * @param dnamelen: length of the dname
+ * @param rr_type: RR type of the RR
+ * @param rr_class: RR class of the RR
+ * @param rr_ttl: TTL of the RR
+ * @param rdatawl: rdata of the RR, prepended with the rdata size
+ * @param rdatalen: length if the RR, including the prepended rdata size
+ * @param rr: the complete RR, for logging purposes
+ * @param rr_len: the length of the complete RR
+ * @return: 0 on error
+ */
+int rpz_insert_rr(struct rpz* r, size_t aznamelen, uint8_t* dname,
+ size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl,
+ uint8_t* rdatawl, size_t rdatalen, uint8_t* rr, size_t rr_len);
+
+/**
+ * Delete policy matching RR, used for IXFR.
+ * @param r: the rpz to add the policy to.
+ * @param aznamelen: the length of the auth-zone name
+ * @param dname: dname of the RR
+ * @param dnamelen: length of the dname
+ * @param rr_type: RR type of the RR
+ * @param rr_class: RR class of the RR
+ * @param rdatawl: rdata of the RR, prepended with the rdata size
+ * @param rdatalen: length if the RR, including the prepended rdata size
+ */
+void rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname,
+ size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl,
+ size_t rdatalen);
+
+/**
+ * Walk over the RPZ zones to find and apply a QNAME trigger policy.
+ * @param az: auth_zones struct, containing first RPZ item and RPZ lock
+ * @param env: module env
+ * @param qinfo: qinfo containing qname and qtype
+ * @param edns: edns data
+ * @param buf: buffer to write answer to
+ * @param temp: scratchpad
+ * @param repinfo: reply info
+ * @param taglist: taglist to lookup.
+ * @param taglen: lenth of taglist.
+ * @param stats: worker stats struct
+ * @return: 1 if client answer is ready, 0 to continue resolving
+ */
+int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
+ struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
+ struct regional* temp, struct comm_reply* repinfo,
+ uint8_t* taglist, size_t taglen, struct ub_server_stats* stats);
+
+/**
+ * Delete RPZ
+ * @param r: RPZ struct to delete
+ */
+void rpz_delete(struct rpz* r);
+
+/**
+ * Clear local-zones and respip data in RPZ, used after reloading file or
+ * AXFR/HTTP transfer.
+ * @param r: RPZ to use
+ */
+int rpz_clear(struct rpz* r);
+
+/**
+ * Create RPZ. RPZ must be added to linked list after creation.
+ * @return: the newly created RPZ
+ */
+struct rpz* rpz_create(struct config_auth* p);
+
+/**
+ * String for RPZ action enum
+ * @param a: RPZ action to get string for
+ * @return: string for RPZ action
+ */
+const char* rpz_action_to_string(enum rpz_action a);
+
+enum rpz_action
+respip_action_to_rpz_action(enum respip_action a);
+
+/**
+ * Prepare RPZ after procesing feed content.
+ * @param r: RPZ to use
+ */
+void rpz_finish_config(struct rpz* r);
+
+/**
+ * Classify respip action for RPZ action
+ * @param a: RPZ action
+ * @return: the respip action
+ */
+enum respip_action
+rpz_action_to_respip_action(enum rpz_action a);
+
+#endif /* SERVICES_RPZ_H */
diff --git a/usr.sbin/unbound/testcode/petal.c b/usr.sbin/unbound/testcode/petal.c
index dcc31fdc5d9..123684aab52 100644
--- a/usr.sbin/unbound/testcode/petal.c
+++ b/usr.sbin/unbound/testcode/petal.c
@@ -234,7 +234,9 @@ setup_ctx(char* key, char* cert)
{
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
if(!ctx) print_exit("out of memory");
+#if SSL_OP_NO_SSLv2 != 0
(void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+#endif
(void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
if(!SSL_CTX_use_certificate_chain_file(ctx, cert))
print_exit("cannot read cert");
diff --git a/usr.sbin/unbound/testcode/replay.h b/usr.sbin/unbound/testcode/replay.h
index 0cce0b49010..5132cdacbd4 100644
--- a/usr.sbin/unbound/testcode/replay.h
+++ b/usr.sbin/unbound/testcode/replay.h
@@ -74,18 +74,18 @@
* o CHECK_ANSWER - followed by entry
* o CHECK_OUT_QUERY - followed by entry (if copy-id it is also reply).
* o REPLY - followed by entry
- * o TIMEOUT
- * o TIME_PASSES ELAPSE [seconds] - increase 'now' time counter, can be
- * a floating point number.
- * TIME_PASSES EVAL [macro] - expanded for seconds to move time.
- * o TRAFFIC - like CHECK_ANSWER, causes traffic to flow.
+ * o TIMEOUT
+ * o TIME_PASSES ELAPSE [seconds] - increase 'now' time counter, can be
+ * a floating point number.
+ * TIME_PASSES EVAL [macro] - expanded for seconds to move time.
+ * o TRAFFIC - like CHECK_ANSWER, causes traffic to flow.
* actually the traffic flows before this step is taken.
* the step waits for traffic to stop.
- * o CHECK_AUTOTRUST [id] - followed by FILE_BEGIN [to match] FILE_END.
- * The file contents is macro expanded before match.
- * o CHECK_TEMPFILE [fname] - followed by FILE_BEGIN [to match] FILE_END
- * o INFRA_RTT [ip] [dp] [rtt] - update infra cache entry with rtt.
- * o ERROR
+ * o CHECK_AUTOTRUST [id] - followed by FILE_BEGIN [to match] FILE_END.
+ * The file contents is macro expanded before match.
+ * o CHECK_TEMPFILE [fname] - followed by FILE_BEGIN [to match] FILE_END
+ * o INFRA_RTT [ip] [dp] [rtt] - update infra cache entry with rtt.
+ * o ERROR
* ; following entry starts on the next line, ENTRY_BEGIN.
* ; more STEP items
* SCENARIO_END
diff --git a/usr.sbin/unbound/testcode/unitdname.c b/usr.sbin/unbound/testcode/unitdname.c
index 238c3edf7ad..6769127b934 100644
--- a/usr.sbin/unbound/testcode/unitdname.c
+++ b/usr.sbin/unbound/testcode/unitdname.c
@@ -794,6 +794,34 @@ dname_test_valid(void)
, 4096) == 0);
}
+/** Test dname_has_label */
+static void
+dname_test_has_label(void)
+{
+ unit_show_func("util/data/dname.c", "dname_has_label");
+ /* label past root label */
+ unit_assert(dname_has_label((uint8_t*)"\01a\0\01c", 5, (uint8_t*)"\01c") == 0);
+ /* label not found */
+ unit_assert(dname_has_label((uint8_t*)"\02ab\01c\0", 6, (uint8_t*)"\01e") == 0);
+ /* buffer too short */
+ unit_assert(dname_has_label((uint8_t*)"\02ab\01c\0", 5, (uint8_t*)"\0") == 0);
+ unit_assert(dname_has_label((uint8_t*)"\1a\0", 2, (uint8_t*)"\0") == 0);
+ unit_assert(dname_has_label((uint8_t*)"\0", 0, (uint8_t*)"\0") == 0);
+ unit_assert(dname_has_label((uint8_t*)"\02ab\01c", 4, (uint8_t*)"\01c") == 0);
+ unit_assert(dname_has_label((uint8_t*)"\02ab\03qwe\06oqieur\03def\01c\0", 19, (uint8_t*)"\01c") == 0);
+
+ /* positive cases */
+ unit_assert(dname_has_label((uint8_t*)"\0", 1, (uint8_t*)"\0") == 1);
+ unit_assert(dname_has_label((uint8_t*)"\1a\0", 3, (uint8_t*)"\0") == 1);
+ unit_assert(dname_has_label((uint8_t*)"\01a\0\01c", 5, (uint8_t*)"\0") == 1);
+ unit_assert(dname_has_label((uint8_t*)"\02ab\01c", 5, (uint8_t*)"\01c") == 1);
+ unit_assert(dname_has_label((uint8_t*)"\02ab\01c\0", 10, (uint8_t*)"\0") == 1);
+ unit_assert(dname_has_label((uint8_t*)"\02ab\01c\0", 7, (uint8_t*)"\0") == 1);
+ unit_assert(dname_has_label((uint8_t*)"\02ab\03qwe\06oqieur\03def\01c\0", 22, (uint8_t*)"\03def") == 1);
+ unit_assert(dname_has_label((uint8_t*)"\02ab\03qwe\06oqieur\03def\01c\0", 22, (uint8_t*)"\02ab") == 1);
+ unit_assert(dname_has_label((uint8_t*)"\02ab\03qwe\06oqieur\03def\01c\0", 22, (uint8_t*)"\01c") == 1);
+}
+
/** test pkt_dname_tolower */
static void
dname_test_pdtl(sldns_buffer* loopbuf, sldns_buffer* boundbuf)
@@ -855,6 +883,7 @@ void dname_test(void)
dname_test_canoncmp();
dname_test_topdomain();
dname_test_valid();
+ dname_test_has_label();
sldns_buffer_free(buff);
sldns_buffer_free(loopbuf);
sldns_buffer_free(boundbuf);