]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Initial RPZ commit
authorRalph Dolmans <ralph@nlnetlabs.nl>
Fri, 5 Apr 2019 15:38:43 +0000 (17:38 +0200)
committerRalph Dolmans <ralph@nlnetlabs.nl>
Fri, 5 Apr 2019 15:38:43 +0000 (17:38 +0200)
services/rpz.c [new file with mode: 0644]
services/rpz.h [new file with mode: 0644]

diff --git a/services/rpz.c b/services/rpz.c
new file mode 100644 (file)
index 0000000..4f159cb
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * 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 "util/data/dname.h"
+#include "util/net_help.h"
+#include "util/log.h"
+#include "util/data/dname.h"
+#include "util/locks.h"
+
+/** string for RPZ action enum */
+static const char*
+rpz_action_to_string(enum rpz_action a)
+{
+       switch(a) {
+       case RPZ_NXDOMAIN_ACTION:       return "NXDOMAIN ACTION";
+       case RPZ_NODATA_ACTION:         return "NODATA ACTION";
+       case RPZ_PASSTHRU_ACTION:       return "PASSTHRU ACTION";
+       case RPZ_DROP_ACTION:           return "DROP ACTION";
+       case RPZ_TCP_ONLY_ACTION:       return "TCP ONLY ACTION";
+       case RPZ_INVALID_ACTION:        return "INVALID ACTION";
+       case RPZ_LOCAL_DATA_ACTION:     return "LOCAL DATA ACTION";
+       }
+       return "UNKNOWN RPZ 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 TRIGGER";
+       case RPZ_CLIENT_IP_TRIGGER:     return "CLIENT IP TRIGGER";
+       case RPZ_RESPONSE_IP_TRIGGER:   return "RESPONSE IP TRIGGER";
+       case RPZ_NSDNAME_TRIGGER:       return "NSDNAME TRIGGER";
+       case RPZ_NSIP_TRIGGER:          return "NSIP TRIGGER";
+       }
+       return "UNKNOWN RPZ TRIGGER";
+}
+
+/**
+ * Get the label that is just before the root label.
+ * @param dname: dname to work on
+ * @return: pointer to TLD label
+ */
+static uint8_t*
+get_tld_label(uint8_t* dname)
+{
+       uint8_t* prevlab = dname;
+
+       /* only root label */
+       if(*dname == 0)
+               return NULL;
+
+       while(*dname) {
+               dname = dname+*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);
+       if(tldlab && dname_lab_startswith(tldlab, "rpz-", &endptr))
+               return RPZ_INVALID_ACTION;
+
+       /* no special label found */
+       return RPZ_LOCAL_DATA_ACTION;
+}
+
+/** Get RPZ trigger for dname */
+static enum rpz_trigger
+rpz_dname_to_trigger(uint8_t* dname)
+{
+       uint8_t* tldlab;
+       char* endptr;
+       tldlab = get_tld_label(dname);
+       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);
+       free(r);
+}
+
+int
+rpz_clear_lz(struct rpz* r)
+{
+       /* must hold write lock on auth_zone */
+       local_zones_delete(r->local_zones);
+       if(!(r->local_zones = local_zones_create())){
+               return 0;
+       }
+       return 1;
+}
+
+struct rpz*
+rpz_create(struct config_auth* p)
+{
+       struct rpz* r = calloc(1, sizeof(*r));
+       if(!r)
+               return 0;
+
+       if(!(r->local_zones = local_zones_create())){
+               free(r);
+               return 0;
+       }
+       r->taglist = memdup(p->rpz_taglist, p->rpz_taglistlen);
+       r->taglistlen = p->rpz_taglistlen;
+       return r;
+}
+
+/** Remove RPZ zone name from dname */
+static size_t
+strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen,
+       uint8_t* newdname)
+{
+       size_t newdnamelen;
+       if(dnamelen < originlen)
+               return 0;
+       newdnamelen = dnamelen - originlen;
+       memmove(newdname, dname, newdnamelen);
+       return newdnamelen + 1; /* + 1 for root label */
+}
+
+/** Insert RR into RPZ's local-zone */
+static int
+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;
+
+       if(a == RPZ_NXDOMAIN_ACTION)
+               tp = local_zone_always_nxdomain;
+       else if(a == RPZ_NODATA_ACTION)
+               tp = local_zone_always_nodata;
+       else if(a == RPZ_DROP_ACTION)
+               tp = local_zone_deny;
+       else if(a == RPZ_PASSTHRU_ACTION)
+               tp = local_zone_always_transparent;
+       else if(a == RPZ_LOCAL_DATA_ACTION)
+               tp = local_zone_redirect;
+       else {
+               verbose(VERB_ALGO, "RPZ: skipping unusupported action: %s",
+                       rpz_action_to_string(a));
+               return 0;
+       }
+
+       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);
+               verbose(VERB_ALGO, "RPZ: skipping duplicate record: '%s'",
+                       rrstr);
+               free(rrstr);
+               lock_rw_unlock(&r->local_zones->lock);
+               return 0;
+       }
+       if(!z) {
+               z = local_zones_add_zone(r->local_zones, dname, dnamelen,
+                       dnamelabs, rrclass, tp);
+       }
+       if(!z) {
+               log_warn("RPZ create failed");
+               lock_rw_unlock(&r->local_zones->lock);
+               return 0;
+       }
+       if(a == RPZ_LOCAL_DATA_ACTION) {
+               /* insert data. TODO synth wildcard cname target on
+                * lookup */
+               rrstr = sldns_wire2str_rr(rr, rr_len);
+               local_zone_enter_rr(z, dname, dnamelen, dnamelabs,
+                       rrtype, rrclass, ttl, rdata, rdata_len, rrstr);
+               free(rrstr);
+       }
+       lock_rw_unlock(&r->local_zones->lock);
+       return 1;
+}
+
+void
+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 */
+       uint8_t* policydname = calloc(1, LDNS_MAX_DOMAINLEN + 1);
+       enum rpz_trigger t;
+       enum rpz_action a;
+       
+       a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
+       if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
+               policydname))) {
+               free(policydname);
+               return;
+       }
+       t = rpz_dname_to_trigger(policydname);
+       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 {
+               free(policydname);
+               verbose(VERB_ALGO, "RPZ: skipping unusupported trigger: %s",
+                       rpz_trigger_to_string(t));
+       }
+}
+
+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, uint8_t* rr, size_t rr_len)
+{
+       /* TODO: remove RR, used for IXFR */
+}
+
+struct local_zone*
+rpz_find_zone(struct rpz* r, struct query_info* qinfo)
+{
+       uint8_t* ce;
+       size_t ce_len, ce_labs;
+       uint8_t wc[LDNS_MAX_DOMAINLEN];
+       int exact;
+       struct local_zone* z = NULL;
+       lock_rw_rdlock(&r->local_zones->lock);
+       z = local_zones_find_le(r->local_zones, qinfo->qname,
+               qinfo->qname_len, dname_count_labels(qinfo->qname),
+               LDNS_RR_CLASS_IN, &exact);
+       if(!z) {
+               lock_rw_unlock(&r->local_zones->lock);
+               return NULL;
+       }
+       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, qinfo->qname);
+       if(!ce /* should not happen */ || !*ce /* root */) {
+               lock_rw_unlock(&z->lock);
+               return NULL;
+       }
+       ce_labs = dname_count_size_labels(ce, &ce_len);
+       if(ce_len+2 > sizeof(wc)) {
+               lock_rw_unlock(&z->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);
+
+       lock_rw_rdlock(&r->local_zones->lock);
+       z = local_zones_find_le(r->local_zones, wc,
+               ce_len+2, ce_labs+1, qinfo->qclass, &exact);
+       if(!z || !exact) {
+               lock_rw_unlock(&r->local_zones->lock);
+               return NULL;
+       }
+       lock_rw_rdlock(&z->lock);
+       lock_rw_unlock(&r->local_zones->lock);
+       return z;
+}
+
+/** print log information for an applied RPZ policy. Based on local-zone's
+ * lz_inform_print().
+ */
+static void
+rpz_inform_print(struct local_zone* z, struct query_info* qinfo,
+       struct comm_reply* repinfo)
+{
+       char ip[128], txt[512];
+       char zname[LDNS_MAX_DOMAINLEN+1];
+       uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port);
+       dname_str(z->name, zname);
+       addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
+       snprintf(txt, sizeof(txt), "RPZ applied %s %s %s@%u", zname,
+               local_zone_type2str(z->type), 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 rpz* r;
+       int ret;
+       struct local_zone* z = NULL;
+       struct local_data* ld = NULL;
+       lock_rw_rdlock(&az->rpz_lock);
+       for(r = az->rpz_first; r && !z; r = r->next) {
+               if(!r->taglist || taglist_intersect(r->taglist, 
+                       r->taglistlen, taglist, taglen))
+                       z = rpz_find_zone(r, qinfo);
+       }
+       lock_rw_unlock(&az->rpz_lock);
+       if(!z)
+               return 0;
+
+       if(z->type == local_zone_redirect && local_data_answer(z, env, qinfo,
+               edns, repinfo, buf, temp, dname_count_labels(qinfo->qname),
+               &ld, z->type, -1, NULL, 0, NULL, 0)) {
+               rpz_inform_print(z, qinfo, repinfo);
+               return !qinfo->local_alias;
+       }
+
+       ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp,
+               0 /* no local data used */, z->type);
+       lock_rw_unlock(&z->lock);
+       if(ret)
+               rpz_inform_print(z, qinfo, repinfo);
+
+       return ret;
+}
diff --git a/services/rpz.h b/services/rpz.h
new file mode 100644 (file)
index 0000000..a631893
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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/config_file.h"
+#include "services/authzone.h"
+#include "sldns/sbuffer.h"
+
+/**
+ * RPZ containing policies. Pointed to from corresponding authz-one. 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;
+       uint8_t* taglist;
+       size_t taglistlen;
+       struct rpz* next;
+       struct rpz* prev;
+       /* tags */
+};
+
+/**
+ * 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 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 */
+};
+
+/**
+ * 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
+ */
+void 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
+ * @param rr: the complete RR, for logging purposes
+ * @param rr_len: the length of the complete RR
+ */
+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, uint8_t* rr, size_t rr_len);
+
+/**
+ * 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.
+ * @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);
+
+/**
+ * Delete RPZ
+ * @param r: RPZ struct to delete
+ */
+void rpz_delete(struct rpz* r);
+
+/**
+ * Clear local-zones in RPZ, used after reloading file or AXFR/HTTP transfer.
+ * @param r: RPZ to use
+ */
+int rpz_clear_lz(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);
+
+#endif /* SERVICES_RPZ_H */