have_view_respip_cfg;
/* read auth zonefiles */
- if(!auth_zones_apply_cfg(daemon->env->auth_zones, daemon->cfg, 1))
+ if(!auth_zones_apply_cfg(daemon->env->auth_zones, daemon->cfg, 1,
+ &daemon->use_rpz))
fatal_exit("auth_zones could not be setup");
/* setup modules */
if(daemon->use_response_ip &&
modstack_find(&daemon->mods, "respip") < 0)
fatal_exit("response-ip options require respip module");
+ /* RPZ response ip triggers don't work as expected without the respip
+ * module. To avoid run-time operational surprise we reject such
+ * configuration. */
+ if(daemon->use_rpz &&
+ modstack_find(&daemon->mods, "respip") < 0)
+ fatal_exit("RPZ requires the respip module");
/* first create all the worker structures, so we can pass
* them to the newly created threads.
struct respip_set* respip_set;
/** some response-ip tags or actions are configured if true */
int use_response_ip;
+ /** some RPZ policies are configured */
+ int use_rpz;
#ifdef USE_DNSCRYPT
/** the dnscrypt environment */
struct dnsc_env* dnscenv;
apply_respip_action(struct worker* worker, const struct query_info* qinfo,
struct respip_client_info* cinfo, struct reply_info* rep,
struct comm_reply* repinfo, struct ub_packed_rrset_key** alias_rrset,
- struct reply_info** encode_repp)
+ struct reply_info** encode_repp, struct auth_zones* az)
{
struct respip_action_info actinfo = {respip_none, NULL};
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo,
- alias_rrset, 0, worker->scratchpad))
+ alias_rrset, 0, worker->scratchpad, az))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
(int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad))
goto bail_out;
*alias_rrset = NULL; /* avoid confusion if caller set it to non-NULL */
- if(worker->daemon->use_response_ip && !partial_rep &&
- !apply_respip_action(worker, qinfo, cinfo, rep, repinfo, alias_rrset,
- &encode_rep)) {
+ if((worker->daemon->use_response_ip || worker->daemon->use_rpz) &&
+ !partial_rep && !apply_respip_action(worker, qinfo, cinfo, rep,
+ repinfo, alias_rrset,
+ &encode_rep, worker->env.auth_zones)) {
goto bail_out;
} else if(partial_rep &&
!respip_merge_cname(partial_rep, qinfo, rep, cinfo,
- must_validate, &encode_rep, worker->scratchpad)) {
+ must_validate, &encode_rep, worker->scratchpad,
+ worker->env.auth_zones)) {
goto bail_out;
}
if(encode_rep != rep)
secure = 0; /* if rewritten, it can't be considered "secure" */
if(!encode_rep || *alias_rrset) {
- sldns_buffer_clear(repinfo->c->buffer);
- sldns_buffer_flip(repinfo->c->buffer);
if(!encode_rep)
*need_drop = 1;
else {
* being deferred). */
static void
reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
- uint16_t flags, struct comm_reply* repinfo, time_t leeway)
+ uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply)
{
/* first send answer to client to keep its latency
* as small as a cachereply */
- if(sldns_buffer_limit(repinfo->c->buffer) != 0)
+ if(!noreply) {
+ if(repinfo->c->tcp_req_info) {
+ sldns_buffer_copy(
+ repinfo->c->tcp_req_info->spool_buffer,
+ repinfo->c->buffer);
+ }
comm_point_send_reply(repinfo);
+ }
server_stats_prefetch(&worker->stats, worker);
/* create the prefetch in the mesh as a normal lookup without
/* If we may apply IP-based actions to the answer, build the client
* information. As this can be expensive, skip it if there is
* absolutely no possibility of it. */
- if(worker->daemon->use_response_ip &&
+ if((worker->daemon->use_response_ip || worker->daemon->use_rpz) &&
(qinfo.qtype == LDNS_RR_TYPE_A ||
qinfo.qtype == LDNS_RR_TYPE_AAAA ||
qinfo.qtype == LDNS_RR_TYPE_ANY)) {
lock_rw_unlock(&e->lock);
reply_and_prefetch(worker, lookup_qinfo,
sldns_buffer_read_u16_at(c->buffer, 2),
- repinfo, leeway);
+ repinfo, leeway,
+ (partial_rep || need_drop));
if(!partial_rep) {
rc = 0;
regional_free_all(worker->scratchpad);
int
context_finalize(struct ub_ctx* ctx)
{
+ int is_rpz;
struct config_file* cfg = ctx->env->cfg;
verbosity = cfg->verbosity;
if(ctx->logfile_override)
return UB_NOMEM;
if(!local_zones_apply_cfg(ctx->local_zones, cfg))
return UB_INITFAIL;
- if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1))
+ if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1i, &is_rpz))
return UB_INITFAIL;
if(!slabhash_is_size(ctx->env->msg_cache, cfg->msg_cache_size,
cfg->msg_cache_slabs)) {
#include "config.h"
#include "services/localzone.h"
+#include "services/authzone.h"
#include "services/cache/dns.h"
#include "sldns/str2wire.h"
#include "util/config_file.h"
#include "services/view.h"
#include "sldns/rrdef.h"
-/**
- * Conceptual set of IP addresses for response AAAA or A records that should
- * trigger special actions.
- */
-struct respip_set {
- struct regional* region;
- struct rbtree_type ip_tree;
- char* const* tagname; /* shallow copy of tag names, for logging */
- int num_tags; /* number of tagname entries */
-};
-
-/** An address span with response control information */
-struct resp_addr {
- /** node in address tree */
- struct addr_tree_node node;
- /** tag bitlist */
- uint8_t* taglist;
- /** length of the taglist (in bytes) */
- size_t taglen;
- /** action for this address span */
- enum respip_action action;
- /** "local data" for this node */
- struct ub_packed_rrset_key* data;
-};
/** Subset of resp_addr.node, used for inform-variant logging */
struct respip_addr_info {
return NULL;
}
addr_tree_init(&set->ip_tree);
+ lock_rw_init(&set->lock);
return set;
}
{
if(!set)
return;
+ lock_rw_destroy(&set->lock);
regional_destroy(set->region);
free(set);
}
return &set->ip_tree;
}
-/** returns the node in the address tree for the specified netblock string;
- * non-existent node will be created if 'create' is true */
-static struct resp_addr*
-respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
+struct resp_addr*
+respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr,
+ socklen_t addrlen, int net, int create, const char* ipstr)
{
struct resp_addr* node;
- struct sockaddr_storage addr;
- int net;
- socklen_t addrlen;
-
- if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) {
- log_err("cannot parse netblock: '%s'", ipstr);
- return NULL;
- }
- node = (struct resp_addr*)addr_tree_find(&set->ip_tree, &addr, addrlen, net);
+ node = (struct resp_addr*)addr_tree_find(&set->ip_tree, addr, addrlen, net);
if(!node && create) {
node = regional_alloc_zero(set->region, sizeof(*node));
if(!node) {
log_err("out of memory");
return NULL;
}
+ lock_rw_init(&node->lock);
node->action = respip_none;
- if(!addr_tree_insert(&set->ip_tree, &node->node, &addr,
+ if(!addr_tree_insert(&set->ip_tree, &node->node, addr,
addrlen, net)) {
/* We know we didn't find it, so this should be
* impossible. */
return node;
}
+void
+respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node)
+{
+ struct resp_addr* prev;
+ prev = (struct resp_addr*)rbtree_previous((struct rbnode_type*)node);
+ lock_rw_destroy(&node->lock);
+ rbtree_delete(&set->ip_tree, node);
+ /* no free'ing, all allocated in region */
+ if(!prev)
+ addr_tree_init_parents((rbtree_type*)set);
+ else
+ addr_tree_init_parents_node(&prev->node);
+}
+
+/** returns the node in the address tree for the specified netblock string;
+ * non-existent node will be created if 'create' is true */
+static struct resp_addr*
+respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
+{
+ struct sockaddr_storage addr;
+ int net;
+ socklen_t addrlen;
+
+ if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) {
+ log_err("cannot parse netblock: '%s'", ipstr);
+ return NULL;
+ }
+ return respip_sockaddr_find_or_create(set, &addr, addrlen, net, create,
+ ipstr);
+}
+
static int
respip_tag_cfg(struct respip_set* set, const char* ipstr,
const uint8_t* taglist, size_t taglen)
action = respip_always_refuse;
else if(strcmp(actnstr, "always_nxdomain") == 0)
action = respip_always_nxdomain;
+ else if(strcmp(actnstr, "always_nodata") == 0)
+ action = respip_always_nodata;
else {
log_err("unknown response-ip action %s", actnstr);
return 0;
}
/** enter local data as resource records into a response-ip node */
-static int
+
+int
respip_enter_rr(struct regional* region, struct resp_addr* raddr,
+ uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata,
+ size_t rdata_len, const char* rrstr, const char* netblockstr)
+{
+ struct packed_rrset_data* pd;
+ struct sockaddr* sa;
+ sa = (struct sockaddr*)&raddr->node.addr;
+ if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) {
+ log_err("CNAME response-ip data (%s) can not co-exist with other "
+ "response-ip data for netblock %s", rrstr, netblockstr);
+ return 0;
+ } else if (raddr->data &&
+ raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
+ log_err("response-ip data (%s) can not be added; CNAME response-ip "
+ "data already in place for netblock %s", rrstr, netblockstr);
+ return 0;
+ } else if((rrtype != LDNS_RR_TYPE_CNAME) &&
+ ((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) ||
+ (sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) {
+ log_err("response-ip data %s record type does not correspond "
+ "to netblock %s address family", rrstr, netblockstr);
+ return 0;
+ }
+
+ if(!raddr->data) {
+ raddr->data = new_rrset(region, rrtype, rrclass);
+ if(!raddr->data)
+ return 0;
+ }
+ pd = raddr->data->entry.data;
+ return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr);
+}
+
+static int
+respip_enter_rrstr(struct regional* region, struct resp_addr* raddr,
const char* rrstr, const char* netblock)
{
uint8_t* nm;
size_t rdata_len = 0;
char buf[65536];
char bufshort[64];
- struct packed_rrset_data* pd;
- struct sockaddr* sa;
int ret;
if(raddr->action != respip_redirect
&& raddr->action != respip_inform_redirect) {
return 0;
}
free(nm);
- sa = (struct sockaddr*)&raddr->node.addr;
- if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) {
- log_err("CNAME response-ip data (%s) can not co-exist with other "
- "response-ip data for netblock %s", rrstr, netblock);
- return 0;
- } else if (raddr->data &&
- raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
- log_err("response-ip data (%s) can not be added; CNAME response-ip "
- "data already in place for netblock %s", rrstr, netblock);
- return 0;
- } else if((rrtype != LDNS_RR_TYPE_CNAME) &&
- ((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) ||
- (sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) {
- log_err("response-ip data %s record type does not correspond "
- "to netblock %s address family", rrstr, netblock);
- return 0;
- }
-
- if(!raddr->data) {
- raddr->data = new_rrset(region, rrtype, rrclass);
- if(!raddr->data)
- return 0;
- }
- pd = raddr->data->entry.data;
- return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr);
+ return respip_enter_rr(region, raddr, rrtype, rrclass, ttl, rdata,
+ rdata_len, rrstr, netblock);
}
static int
"response-ip node for %s not found", rrstr, ipstr);
return 0;
}
- return respip_enter_rr(set->region, node, rrstr, ipstr);
+ return respip_enter_rrstr(set->region, node, rrstr, ipstr);
}
static int
free(pd);
pd = np;
}
+ addr_tree_init_parents(&set->ip_tree);
return 1;
}
* rep->rrsets for the RRset that contains the matching IP address record
* (the index is normally 0, but can be larger than that if this is a CNAME
* chain or type-ANY response).
+ * Returns resp_addr holding read lock.
*/
-static const struct resp_addr*
-respip_addr_lookup(const struct reply_info *rep, struct rbtree_type* iptree,
+static struct resp_addr*
+respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs,
size_t* rrset_id)
{
size_t i;
struct sockaddr_storage ss;
socklen_t addrlen;
+ lock_rw_rdlock(&rs->lock);
for(i=0; i<rep->an_numrrsets; i++) {
size_t j;
const struct packed_rrset_data* rd;
for(j = 0; j < rd->count; j++) {
if(!rdata2sockaddr(rd, rtype, j, &ss, &addrlen))
continue;
- ra = (struct resp_addr*)addr_tree_lookup(iptree, &ss,
- addrlen);
+ ra = (struct resp_addr*)addr_tree_lookup(&rs->ip_tree,
+ &ss, addrlen);
if(ra) {
*rrset_id = i;
+ lock_rw_rdlock(&ra->lock);
+ lock_rw_unlock(&rs->lock);
return ra;
}
}
}
-
+ lock_rw_unlock(&rs->lock);
return NULL;
}
return 1;
} else if(action == respip_static || action == respip_redirect ||
action == respip_always_nxdomain ||
+ action == respip_always_nodata ||
action == respip_inform_redirect) {
/* Since we don't know about other types of the owner name,
* we generally return NOERROR/NODATA unless an NXDOMAIN action
const struct respip_client_info* cinfo, const struct reply_info* rep,
struct reply_info** new_repp, struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset, int search_only,
- struct regional* region)
+ struct regional* region, struct auth_zones* az)
{
const uint8_t* ctaglist;
size_t ctaglen;
size_t rrset_id = 0;
enum respip_action action = respip_none;
int tag = -1;
- const struct resp_addr* raddr = NULL;
+ struct resp_addr* raddr = NULL;
int ret = 1;
struct ub_packed_rrset_key* redirect_rrset = NULL;
+ struct rpz* r;
if(!cinfo)
goto done;
lock_rw_rdlock(&view->lock);
if(view->respip_set) {
if((raddr = respip_addr_lookup(rep,
- &view->respip_set->ip_tree, &rrset_id))) {
+ view->respip_set, &rrset_id))) {
/** for per-view respip directives the action
* can only be direct (i.e. not tag-based) */
action = raddr->action;
if(!raddr && !view->isfirst)
goto done;
}
- if(!raddr && ipset && (raddr = respip_addr_lookup(rep, &ipset->ip_tree,
+ if(!raddr && ipset && (raddr = respip_addr_lookup(rep, ipset,
&rrset_id))) {
action = (enum respip_action)local_data_find_tag_action(
raddr->taglist, raddr->taglen, ctaglist, ctaglen,
(enum localzone_type)raddr->action, &tag,
ipset->tagname, ipset->num_tags);
}
+ lock_rw_rdlock(&az->rpz_lock);
+ for(r = az->rpz_first; r && !raddr; r = r->next) {
+ if(!r->taglist || taglist_intersect(r->taglist,
+ r->taglistlen, ctaglist, ctaglen)) {
+ if((raddr = respip_addr_lookup(rep,
+ r->respip_set, &rrset_id))) {
+ action = raddr->action;
+ }
+ }
+ }
+ lock_rw_unlock(&az->rpz_lock);
if(raddr && !search_only) {
int result = 0;
if(action != respip_always_refuse
&& action != respip_always_transparent
&& action != respip_always_nxdomain
+ && action != respip_always_nodata
&& (result = respip_data_answer(raddr, action,
qinfo->qtype, rep, rrset_id, new_repp, tag, tag_datas,
tag_datas_size, ipset->tagname, ipset->num_tags,
ret = populate_action_info(actinfo, action, raddr,
redirect_rrset, tag, ipset, search_only, region);
}
+ if(raddr)
+ lock_rw_unlock(&raddr->lock);
return ret;
}
if(!respip_rewrite_reply(&qstate->qinfo,
qstate->client_info, qstate->return_msg->rep,
&new_rep, &actinfo, &alias_rrset, 0,
- qstate->region)) {
+ qstate->region, qstate->env->auth_zones)) {
goto servfail;
}
if(actinfo.action != respip_none) {
respip_merge_cname(struct reply_info* base_rep,
const struct query_info* qinfo, const struct reply_info* tgt_rep,
const struct respip_client_info* cinfo, int must_validate,
- struct reply_info** new_repp, struct regional* region)
+ struct reply_info** new_repp, struct regional* region,
+ struct auth_zones* az)
{
struct reply_info* new_rep;
struct reply_info* tmp_rep = NULL; /* just a placeholder */
/* see if the target reply would be subject to a response-ip action. */
if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo,
- &alias_rrset, 1, region))
+ &alias_rrset, 1, region, az))
return 0;
if(actinfo.action != respip_none) {
log_info("CNAME target of redirect response-ip action would "
if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo,
qstate->return_msg->rep, super->client_info,
- super->env->need_to_validate, &new_rep, super->region))
+ super->env->need_to_validate, &new_rep, super->region,
+ qstate->env->auth_zones))
goto fail;
super->return_msg->rep = new_rep;
return;
#include "util/module.h"
#include "services/localzone.h"
+#include "util/locks.h"
/**
- * Set of response IP addresses with associated actions and tags.
- * Forward declaration only here. Actual definition is hidden within the
- * module.
+ * Conceptual set of IP addresses for response AAAA or A records that should
+ * trigger special actions.
*/
-struct respip_set;
+struct respip_set {
+ struct regional* region;
+ struct rbtree_type ip_tree;
+ lock_rw_type lock; /* lock on the respip tree */
+ char* const* tagname; /* shallow copy of tag names, for logging */
+ int num_tags; /* number of tagname entries */
+};
+
+
+/** An address span with response control information */
+struct resp_addr {
+ /** node in address tree */
+ struct addr_tree_node node;
+ /** lock on the node item */
+ lock_rw_type lock;
+ /** tag bitlist */
+ uint8_t* taglist;
+ /** length of the taglist (in bytes) */
+ size_t taglen;
+ /** action for this address span */
+ enum respip_action action;
+ /** "local data" for this node */
+ struct ub_packed_rrset_key* data;
+};
-/**
- * Forward declaration for the structure that represents a node in the
- * respip_set address tree
- */
-struct resp_addr;
/**
* Forward declaration for the structure that represents a tree of view data.
* @param new_repp: pointer placeholder for the merged reply. will be intact
* on error.
* @param region: allocator to build *new_repp.
+ * @param az: auth zones containing RPZ information.
* @return 1 on success, 0 on error.
*/
int respip_merge_cname(struct reply_info* base_rep,
const struct query_info* qinfo, const struct reply_info* tgt_rep,
const struct respip_client_info* cinfo, int must_validate,
- struct reply_info** new_repp, struct regional* region);
+ struct reply_info** new_repp, struct regional* region,
+ struct auth_zones* az);
/**
* See if any IP-based action should apply to any IP address of AAAA/A answer
* @param alias_rrset: must not be NULL.
* @param search_only: if true, only check if an action would apply. actionp
* will be set (or intact) accordingly but the modified reply won't be built.
+ * @param az: auth zones containing RPZ information.
* @param region: allocator to build *new_repp.
* @return 1 on success, 0 on error.
*/
const struct reply_info *rep, struct reply_info** new_repp,
struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset,
- int search_only, struct regional* region);
+ int search_only, struct regional* region, struct auth_zones* az);
/**
* Get the response-ip function block.
uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias,
struct comm_reply* repinfo);
+/**
+ * Find resp_addr in tree, create and add to tree if it does not exist.
+ * @param set: struct containing the tree and region to alloc new node on.
+ * should hold write lock.
+ * @param addr: address to look up.
+ * @param addrlen: length of addr.
+ * @param net: netblock to lookup.
+ * @param create: create node if it does not exist when 1.
+ * @param ipstr: human redable ip string, for logging.
+ * @return newly created of found node, not holding lock.
+ */
+struct resp_addr*
+respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr,
+ socklen_t addrlen, int net, int create, const char* ipstr);
+
+/**
+ * Add RR to resp_addr's RRset. Create RRset is not existing.
+ * @param region: region to alloc RR(set).
+ * @param raddr: resp_addr containing RRset. Must hold write lock.
+ * @param rrtype: RR type.
+ * @param rrclass: RR class.
+ * @param ttl: TTL.
+ * @param rdata: RDATA.
+ * @param rdata_len: length of rdata.
+ * @param rrstr: RR as string, for logging
+ * @param netblockstr: netblock as string, for logging
+ * @return 0 on error
+ */
+int
+respip_enter_rr(struct regional* region, struct resp_addr* raddr,
+ uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata,
+ size_t rdata_len, const char* rrstr, const char* netblockstr);
+
+/**
+ * Delete resp_addr node from tree.
+ * @param set: struct containing tree. Must hold write lock.
+ * @param node: node to delete. Must hold write lock.
+ */
+void
+respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node);
#endif /* RESPIP_RESPIP_H */
/* clear the data tree */
traverse_postorder(&z->data, auth_data_del, NULL);
rbtree_init(&z->data, &auth_data_cmp);
- /* clear the RPZ local_zone tree */
+ /* clear the RPZ policies */
if(z->rpz)
- rpz_clear_lz(z->rpz);
+ rpz_clear(z->rpz);
memset(&state, 0, sizeof(state));
/* default TTL to 3600 */
return 0;
}
fclose(in);
+
+ if(z->rpz)
+ rpz_finish_config(z->rpz);
return 1;
}
}
int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
- int setup)
+ int setup, int* is_rpz)
{
struct config_auth* p;
az_setall_deleted(az);
log_warn("auth-zone without a name, skipped");
continue;
}
+ *is_rpz = (*is_rpz || p->isrpz);
if(!auth_zones_cfg(az, p)) {
log_err("cannot config auth zone %s", p->name);
return 0;
/* clear the data tree */
traverse_postorder(&z->data, auth_data_del, NULL);
rbtree_init(&z->data, &auth_data_cmp);
- /* clear the RPZ local_zone tree */
+ /* clear the RPZ policies */
if(z->rpz)
- rpz_clear_lz(z->rpz);
+ rpz_clear(z->rpz);
xfr->have_zone = 0;
xfr->serial = 0;
/* clear the data tree */
traverse_postorder(&z->data, auth_data_del, NULL);
rbtree_init(&z->data, &auth_data_cmp);
- /* clear the RPZ local_zone tree */
+ /* clear the RPZ policies */
if(z->rpz)
- rpz_clear_lz(z->rpz);
+ rpz_clear(z->rpz);
xfr->have_zone = 0;
xfr->serial = 0;
if(xfr->have_zone)
xfr->lease_time = *env->now;
+ if(z->rpz)
+ rpz_finish_config(z->rpz);
+
/* unlock */
lock_rw_unlock(&z->lock);
* @param az: auth zones structure
* @param cfg: config to apply.
* @param setup: if true, also sets up values in the auth zones structure
+ * @param is_rpz: set to 1 if at least one RPZ zone is configured.
* @return false on failure.
*/
int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
- int setup);
+ int setup, int* iz_rpz);
/** initial pick up of worker timeouts, ties events to worker event loop
* @param az: auth zones structure
respip_transparent = local_zone_transparent,
/** gives response data (if any), else nodata answer. */
respip_typetransparent = local_zone_typetransparent,
+ /** type invalid */
+ respip_invalid = local_zone_invalid,
};
int
}
}
+static 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_deny;
+ case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
+ case RPZ_LOCAL_DATA_ACTION:
+ case RPZ_CNAME_OVERRIDE_ACTION:
+ return respip_redirect;
+ case RPZ_INVALID_ACTION:
+ case RPZ_TCP_ONLY_ACTION:
+ default:
+ return respip_invalid;
+ }
+}
+
static enum rpz_action
localzone_type_to_rpz_action(enum localzone_type lzt)
{
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);
}
int
-rpz_clear_lz(struct rpz* r)
+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 rpz* r = calloc(1, sizeof(*r));
if(!r)
- return 0;
+ goto err;
r->region = regional_create_custom(sizeof(struct regional));
if(!r->region) {
- free(r);
- return 0;
+ goto err;
}
if(!(r->local_zones = local_zones_create())){
- free(r);
- return 0;
+ 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_cname) {
log_err("RPZ override with cname action found, but not "
"rpz-cname-override configured");
- free(r);
- return 0;
+ goto err;
}
if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) {
log_err("cannot parse RPZ cname override: %s",
p->rpz_cname);
- free(r);
- return 0;
+ goto err;
}
r->cname_override = new_cname_override(r->region, nm, nmlen);
if(!r->cname_override) {
- free(r);
- return 0;
+ goto err;
}
}
r->log = p->rpz_log;
if(p->rpz_log_name)
r->log_name = strdup(p->rpz_log_name);
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 */
return 1;
}
-/** Insert RR into RPZ's ACL tree */
+/** Insert RR into RPZ's respip_set */
static int
-rpz_insert_client_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname,
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)
{
- /* TODO: remove void casts */
- (void)r;
- (void)dnamelen;
- (void)rrtype;
- (void)rrclass;
- (void)ttl;
- (void)rdata;
- (void)rdata_len;
- (void)rr;
- (void)rr_len;
-
- if(a == RPZ_DROP_ACTION) {
- /* insert into r->acl tree */
- struct sockaddr_storage addr;
- char str[INET6_ADDRSTRLEN];
- socklen_t addrlen;
- int net, af;
- if(!netblockdnametoaddr(dname, &addr, &addrlen, &net, &af))
- return 0;
- /* TODO insert into acl tree */
-#if 0
- if((inet_ntop(af,
- (af == AF_INET) ?
- (void*)&((struct sockaddr_in*)&addr)->sin_addr :
- (void*)&((struct sockaddr_in6*)&addr)->sin6_addr,
- str, INET6_ADDRSTRLEN)))
- log_info("rpz %s/%d\n", str, net);
-#endif
- } else {
+ struct resp_addr* node;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int net, af;
+ char* rrstr = sldns_wire2str_rr(rr, rr_len);
+ 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;
}
- return 0;
+
+ if(!netblockdnametoaddr(dname, &addr, &addrlen, &net, &af))
+ return 0;
+
+ lock_rw_wrlock(&r->respip_set->lock);
+ if(!(node=respip_sockaddr_find_or_create(r->respip_set, &addr, addrlen,
+ net, 1, rrstr))) {
+ lock_rw_unlock(&r->respip_set->lock);
+ 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);
+ return 1;
}
void
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
}
- else if(t == RPZ_CLIENT_IP_TRIGGER) {
- rpz_insert_client_ip_trigger(r, policydname, policydnamelen,
+ else if(t == RPZ_RESPONSE_IP_TRIGGER) {
+ rpz_insert_response_ip_trigger(r, policydname,
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
}
return 1;
}
-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)
+/**
+ * Remove RR from RPZ's respip set
+ * @param raddr: respip node
+ * @param rdata: rdata of RR to remove
+ * @param rdatalen: length of rdata
+ * @param region: RPZ's repsip_set region
+ * @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, struct regional* region)
+{
+ 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) {
+ struct packed_rrset_data* new;
+ new = packed_rrset_remove_rr(d, index, region);
+ if(!new)
+ return 0;
+ raddr->data->entry.data = new;
+ }
+ }
+ 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 */);
+ 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);
+ }
+ return;
+}
+
+static void
+rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, 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, &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, r->respip_set->region);
+ }
+ if(delete_respip) {
+ /* delete + reset parent pointers */
+ respip_sockaddr_delete(r->respip_set, node);
+ } else {
+ lock_rw_unlock(&node->lock);
+ }
+ 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;
/* 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;
- int delete_zone = 1;
a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
+ if(a == RPZ_INVALID_ACTION)
+ return;
if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
policydname))) {
free(policydname);
return;
}
t = rpz_dname_to_trigger(policydname);
- if(a != RPZ_INVALID_ACTION && t == RPZ_QNAME_TRIGGER) {
- z = rpz_find_zone(r, policydname, policydnamelen, rr_class,
- 1 /* only exact */, 1 /* wr lock */);
- if(!z) {
- verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, "
- "RPZ domain not found");
- free(policydname);
- return;
- }
- if(a == RPZ_LOCAL_DATA_ACTION)
- delete_zone = rpz_data_delete_rr(z, policydname,
- policydnamelen, rr_type, rdatawl, rdatalen);
- else if(a != localzone_type_to_rpz_action(z->type)) {
- free(policydname);
- return;
- }
- lock_rw_unlock(&z->lock);
- if(delete_zone) {
- local_zones_del_zone(r->local_zones, z);
- }
+ 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, a, rr_type,
+ rdatawl, rdatalen);
}
free(policydname);
}
#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.
*/
struct rpz {
struct local_zones* local_zones;
+ struct respip_set* respip_set;
uint8_t* taglist;
size_t taglistlen;
enum rpz_action action_override;
void rpz_delete(struct rpz* r);
/**
- * Clear local-zones in RPZ, used after reloading file or AXFR/HTTP transfer.
+ * Clear local-zones and respip data in RPZ, used after reloading file or
+ * AXFR/HTTP transfer.
* @param r: RPZ to use
*/
-int rpz_clear_lz(struct rpz* r);
+int rpz_clear(struct rpz* r);
/**
* Create RPZ. RPZ must be added to linked list after creation.
*/
const char* rpz_action_to_string(enum rpz_action a);
+/**
+ * Prepare RPZ after procesing feed content.
+ * @param r: RPZ to use
+ */
+void rpz_finish_config(struct rpz* r);
+
#endif /* SERVICES_RPZ_H */
static void
check_auth(struct config_file* cfg)
{
+ int is_rpz;
struct auth_zones* az = auth_zones_create();
- if(!az || !auth_zones_apply_cfg(az, cfg, 0)) {
+ if(!az || !auth_zones_apply_cfg(az, cfg, 0i, &is_rpz)) {
fatal_exit("Could not setup authority zones");
}
auth_zones_delete(az);
return strdup(buf);
}
-int taglist_intersect(uint8_t* list1, size_t list1len, uint8_t* list2,
+int taglist_intersect(uint8_t* list1, size_t list1len, const uint8_t* list2,
size_t list2len)
{
size_t i;
* @param list2len: length in bytes of second list.
* @return true if there are tags in common, 0 if not.
*/
-int taglist_intersect(uint8_t* list1, size_t list1len, uint8_t* list2,
+int taglist_intersect(uint8_t* list1, size_t list1len, const uint8_t* list2,
size_t list2len);
/**
int netblockdnametoaddr(uint8_t* dname, struct sockaddr_storage* addr,
socklen_t* addrlen, int* net, int* af)
{
- int e;
char buff[3 /* 3 digit netblock */ + 1];
if(*dname > 3)
/* netblock invalid */
return 0;
memcpy(buff, dname+1, *dname);
- buff[(*dname)+1] = '\0';
+ buff[*dname] = '\0';
*net = atoi(buff);
dname += *dname;
dname++;
return rbtree_insert(tree, &node->node) != NULL;
}
-void addr_tree_init_parents(rbtree_type* tree)
+void addr_tree_init_parents_node(struct addr_tree_node* node)
{
- struct addr_tree_node* node, *prev = NULL, *p;
+ struct addr_tree_node* prev = NULL, *p;
int m;
- RBTREE_FOR(node, struct addr_tree_node*, tree) {
+ for(; (rbnode_type*)node != RBTREE_NULL;
+ node = (struct addr_tree_node*)rbtree_next((rbnode_type*)node)) {
node->parent = NULL;
if(!prev || prev->addrlen != node->addrlen) {
prev = node;
}
}
+void addr_tree_init_parents(rbtree_type* tree)
+{
+ addr_tree_init_parents_node(
+ (struct addr_tree_node*)rbtree_first(tree));
+}
+
void name_tree_init_parents(rbtree_type* tree)
{
struct name_tree_node* node, *prev = NULL, *p;
*/
void addr_tree_init_parents(rbtree_type* tree);
+/**
+ * Initialize parent pointers in partial addr tree.
+ * Reinitialize pointer for part of tree, used after node deletion
+ * @param node: node to start parent pointer initialization for.
+ */
+void addr_tree_init_parents_node(struct addr_tree_node* node);
+
/**
* Lookup closest encloser in addr tree.
* @param tree: addr tree