mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-15 19:22:55 -05:00
- Added RPZ response IP support
This commit is contained in:
parent
395d83cfc8
commit
a8d6147ae4
17 changed files with 473 additions and 196 deletions
|
|
@ -616,7 +616,8 @@ daemon_fork(struct daemon* daemon)
|
|||
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 */
|
||||
|
|
@ -628,6 +629,12 @@ daemon_fork(struct daemon* daemon)
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ struct daemon {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -572,7 +572,7 @@ static int
|
|||
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};
|
||||
|
||||
|
|
@ -582,7 +582,7 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
|
|||
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
|
||||
|
|
@ -709,20 +709,20 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
|
|||
(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 {
|
||||
|
|
@ -768,12 +768,18 @@ bail_out:
|
|||
* 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
|
||||
|
|
@ -1445,7 +1451,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
|||
/* 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)) {
|
||||
|
|
@ -1490,7 +1496,8 @@ lookup_cache:
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
int
|
||||
context_finalize(struct ub_ctx* ctx)
|
||||
{
|
||||
int is_rpz;
|
||||
struct config_file* cfg = ctx->env->cfg;
|
||||
verbosity = cfg->verbosity;
|
||||
if(ctx->logfile_override)
|
||||
|
|
@ -69,7 +70,7 @@ context_finalize(struct ub_ctx* ctx)
|
|||
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)) {
|
||||
|
|
|
|||
203
respip/respip.c
203
respip/respip.c
|
|
@ -12,6 +12,7 @@
|
|||
#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"
|
||||
|
|
@ -25,30 +26,6 @@
|
|||
#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 {
|
||||
|
|
@ -88,6 +65,7 @@ respip_set_create(void)
|
|||
return NULL;
|
||||
}
|
||||
addr_tree_init(&set->ip_tree);
|
||||
lock_rw_init(&set->lock);
|
||||
return set;
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +74,7 @@ respip_set_delete(struct respip_set* set)
|
|||
{
|
||||
if(!set)
|
||||
return;
|
||||
lock_rw_destroy(&set->lock);
|
||||
regional_destroy(set->region);
|
||||
free(set);
|
||||
}
|
||||
|
|
@ -108,12 +87,49 @@ respip_set_get_tree(struct respip_set* set)
|
|||
return &set->ip_tree;
|
||||
}
|
||||
|
||||
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;
|
||||
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,
|
||||
addrlen, net)) {
|
||||
/* We know we didn't find it, so this should be
|
||||
* impossible. */
|
||||
log_warn("unexpected: duplicate address: %s", ipstr);
|
||||
}
|
||||
}
|
||||
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 resp_addr* node;
|
||||
struct sockaddr_storage addr;
|
||||
int net;
|
||||
socklen_t addrlen;
|
||||
|
|
@ -122,22 +138,8 @@ respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
|
|||
log_err("cannot parse netblock: '%s'", ipstr);
|
||||
return NULL;
|
||||
}
|
||||
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;
|
||||
}
|
||||
node->action = respip_none;
|
||||
if(!addr_tree_insert(&set->ip_tree, &node->node, &addr,
|
||||
addrlen, net)) {
|
||||
/* We know we didn't find it, so this should be
|
||||
* impossible. */
|
||||
log_warn("unexpected: duplicate address: %s", ipstr);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
return respip_sockaddr_find_or_create(set, &addr, addrlen, net, create,
|
||||
ipstr);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -191,6 +193,8 @@ respip_action_cfg(struct respip_set* set, const char* ipstr,
|
|||
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;
|
||||
|
|
@ -232,8 +236,43 @@ new_rrset(struct regional* region, uint16_t rrtype, uint16_t rrclass)
|
|||
}
|
||||
|
||||
/** 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;
|
||||
|
|
@ -244,8 +283,6 @@ respip_enter_rr(struct regional* region, struct resp_addr* raddr,
|
|||
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) {
|
||||
|
|
@ -265,31 +302,8 @@ respip_enter_rr(struct regional* region, struct resp_addr* raddr,
|
|||
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
|
||||
|
|
@ -303,7 +317,7 @@ respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr)
|
|||
"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
|
||||
|
|
@ -361,6 +375,7 @@ respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags,
|
|||
free(pd);
|
||||
pd = np;
|
||||
}
|
||||
addr_tree_init_parents(&set->ip_tree);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -557,9 +572,10 @@ rdata2sockaddr(const struct packed_rrset_data* rd, uint16_t rtype, size_t i,
|
|||
* 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;
|
||||
|
|
@ -567,6 +583,7 @@ respip_addr_lookup(const struct reply_info *rep, struct rbtree_type* iptree,
|
|||
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;
|
||||
|
|
@ -578,15 +595,17 @@ respip_addr_lookup(const struct reply_info *rep, struct rbtree_type* iptree,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -754,6 +773,7 @@ respip_nodata_answer(uint16_t qtype, enum respip_action action,
|
|||
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
|
||||
|
|
@ -817,7 +837,7 @@ respip_rewrite_reply(const struct query_info* qinfo,
|
|||
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;
|
||||
|
|
@ -830,9 +850,10 @@ respip_rewrite_reply(const struct query_info* qinfo,
|
|||
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;
|
||||
|
|
@ -859,7 +880,7 @@ respip_rewrite_reply(const struct query_info* qinfo,
|
|||
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;
|
||||
|
|
@ -868,7 +889,7 @@ respip_rewrite_reply(const struct query_info* qinfo,
|
|||
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,
|
||||
|
|
@ -876,6 +897,17 @@ respip_rewrite_reply(const struct query_info* qinfo,
|
|||
(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;
|
||||
|
||||
|
|
@ -884,6 +916,7 @@ respip_rewrite_reply(const struct query_info* qinfo,
|
|||
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,
|
||||
|
|
@ -920,6 +953,8 @@ respip_rewrite_reply(const struct query_info* qinfo,
|
|||
ret = populate_action_info(actinfo, action, raddr,
|
||||
redirect_rrset, tag, ipset, search_only, region);
|
||||
}
|
||||
if(raddr)
|
||||
lock_rw_unlock(&raddr->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -981,7 +1016,7 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
|||
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) {
|
||||
|
|
@ -1027,7 +1062,8 @@ 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)
|
||||
{
|
||||
struct reply_info* new_rep;
|
||||
struct reply_info* tmp_rep = NULL; /* just a placeholder */
|
||||
|
|
@ -1053,7 +1089,7 @@ respip_merge_cname(struct reply_info* base_rep,
|
|||
|
||||
/* 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 "
|
||||
|
|
@ -1105,7 +1141,8 @@ respip_inform_super(struct module_qstate* qstate, int id,
|
|||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -14,19 +14,37 @@
|
|||
|
||||
#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.
|
||||
|
|
@ -124,12 +142,14 @@ int respip_views_apply_cfg(struct views* vs, struct config_file* cfg,
|
|||
* @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
|
||||
|
|
@ -148,6 +168,7 @@ int respip_merge_cname(struct reply_info* base_rep,
|
|||
* @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.
|
||||
*/
|
||||
|
|
@ -156,7 +177,7 @@ int respip_rewrite_reply(const struct query_info* qinfo,
|
|||
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.
|
||||
|
|
@ -227,4 +248,44 @@ void respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname,
|
|||
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 */
|
||||
|
|
|
|||
|
|
@ -1542,9 +1542,9 @@ auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg)
|
|||
/* 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 */
|
||||
|
|
@ -1564,6 +1564,9 @@ auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg)
|
|||
return 0;
|
||||
}
|
||||
fclose(in);
|
||||
|
||||
if(z->rpz)
|
||||
rpz_finish_config(z->rpz);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -1926,7 +1929,7 @@ az_delete_deleted_zones(struct auth_zones* az)
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
@ -1935,6 +1938,7 @@ int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
|
|||
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;
|
||||
|
|
@ -4654,9 +4658,9 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
|
|||
/* 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;
|
||||
|
|
@ -4754,9 +4758,9 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
|
|||
/* 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;
|
||||
|
|
@ -4937,6 +4941,9 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
|
|||
if(xfr->have_zone)
|
||||
xfr->lease_time = *env->now;
|
||||
|
||||
if(z->rpz)
|
||||
rpz_finish_config(z->rpz);
|
||||
|
||||
/* unlock */
|
||||
lock_rw_unlock(&z->lock);
|
||||
|
||||
|
|
|
|||
|
|
@ -468,10 +468,11 @@ struct auth_zones* auth_zones_create(void);
|
|||
* @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
|
||||
|
|
|
|||
|
|
@ -555,6 +555,8 @@ enum respip_action {
|
|||
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
|
||||
|
|
|
|||
269
services/rpz.c
269
services/rpz.c
|
|
@ -209,6 +209,24 @@ rpz_action_to_localzone_type(enum rpz_action a)
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -256,6 +274,7 @@ 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);
|
||||
|
|
@ -263,16 +282,28 @@ void rpz_delete(struct rpz* r)
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
@ -323,17 +354,18 @@ rpz_create(struct config_auth* p)
|
|||
{
|
||||
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);
|
||||
|
|
@ -350,26 +382,34 @@ rpz_create(struct config_auth* p)
|
|||
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 */
|
||||
|
|
@ -436,46 +476,46 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
|
|||
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;
|
||||
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_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 {
|
||||
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
|
||||
|
|
@ -501,8 +541,8 @@ rpz_insert_rr(struct rpz* r, size_t aznamelen, uint8_t* dname,
|
|||
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);
|
||||
}
|
||||
|
|
@ -637,46 +677,135 @@ rpz_data_delete_rr(struct local_zone* z, uint8_t* policydname,
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#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.
|
||||
|
|
@ -87,6 +88,7 @@ enum rpz_action {
|
|||
*/
|
||||
struct rpz {
|
||||
struct local_zones* local_zones;
|
||||
struct respip_set* respip_set;
|
||||
uint8_t* taglist;
|
||||
size_t taglistlen;
|
||||
enum rpz_action action_override;
|
||||
|
|
@ -157,10 +159,11 @@ int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
|
|||
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.
|
||||
|
|
@ -175,4 +178,10 @@ struct rpz* rpz_create(struct config_auth* p);
|
|||
*/
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -642,8 +642,9 @@ check_hints(struct config_file* cfg)
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -1900,7 +1900,7 @@ char* config_taglist2str(struct config_file* cfg, uint8_t* taglist,
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -1036,7 +1036,7 @@ char* config_taglist2str(struct config_file* cfg, uint8_t* taglist,
|
|||
* @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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -356,13 +356,12 @@ static int ipdnametoaddr(uint8_t* dname, struct sockaddr_storage* addr,
|
|||
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++;
|
||||
|
|
|
|||
|
|
@ -104,11 +104,12 @@ int addr_tree_insert(rbtree_type* tree, struct addr_tree_node* node,
|
|||
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;
|
||||
|
|
@ -130,6 +131,12 @@ void addr_tree_init_parents(rbtree_type* tree)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -173,6 +173,13 @@ int addr_tree_insert(rbtree_type* tree, struct addr_tree_node* node,
|
|||
*/
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in a new issue