diff --git a/daemon/stats.c b/daemon/stats.c index 504b0efcc..3cf0d2b8d 100644 --- a/daemon/stats.c +++ b/daemon/stats.c @@ -273,6 +273,8 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset) s->svr.ans_rcode_nodata += (long long)worker->env.mesh->ans_nodata; for(i=0; i<16; i++) s->svr.ans_rcode[i] += (long long)worker->env.mesh->ans_rcode[i]; + for(i=0; isvr.rpz_action[i] += (long long)worker->env.mesh->rpz_action[i]; timehist_export(worker->env.mesh->histogram, s->svr.hist, NUM_BUCKETS_HIST); /* values from outside network */ @@ -446,6 +448,8 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a) total->svr.ans_rcode[i] += a->svr.ans_rcode[i]; for(i=0; isvr.hist[i] += a->svr.hist[i]; + for(i=0; isvr.rpz_action[i] += a->svr.rpz_action[i]; } total->mesh_num_states += a->mesh_num_states; diff --git a/daemon/worker.c b/daemon/worker.c index 547a7610b..2cfc96845 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -61,6 +61,7 @@ #include "services/authzone.h" #include "services/mesh.h" #include "services/localzone.h" +#include "services/rpz.h" #include "util/data/msgparse.h" #include "util/data/msgencode.h" #include "util/data/dname.h" @@ -574,7 +575,8 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo, struct comm_reply* repinfo, struct ub_packed_rrset_key** alias_rrset, struct reply_info** encode_repp, struct auth_zones* az) { - struct respip_action_info actinfo = {respip_none, NULL}; + struct respip_action_info actinfo = {0}; + actinfo.action = respip_none; if(qinfo->qtype != LDNS_RR_TYPE_A && qinfo->qtype != LDNS_RR_TYPE_AAAA && @@ -595,9 +597,18 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo, /* If address info is returned, it means the action should be an * 'inform' variant and the information should be logged. */ if(actinfo.addrinfo) { - respip_inform_print(actinfo.addrinfo, qinfo->qname, + respip_inform_print(&actinfo, qinfo->qname, qinfo->qtype, qinfo->qclass, qinfo->local_alias, repinfo); + + if(worker->stats.extended && actinfo.rpz_used) { + /* TODO: does not work for disabled (override) actions */ + if(actinfo.rpz_cname_override) + worker->stats.rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; + else + worker->stats.rpz_action[ + respip_action_to_rpz_action(actinfo.action)]++; + } } return 1; diff --git a/respip/respip.c b/respip/respip.c index 176abea82..a529ccf62 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -656,6 +656,7 @@ make_new_reply_info(const struct reply_info* rep, struct regional* region, * the "no data" action in case of error. * @param raddr: address span that requires an action * @param action: action to apply + * @param data: RRset to use for override * @param qtype: original query type * @param rep: original reply message * @param rrset_id: the rrset ID in 'rep' to which the action should apply @@ -671,13 +672,14 @@ make_new_reply_info(const struct reply_info* rep, struct regional* region, */ static int respip_data_answer(const struct resp_addr* raddr, enum respip_action action, + struct ub_packed_rrset_key* data, uint16_t qtype, const struct reply_info* rep, size_t rrset_id, struct reply_info** new_repp, int tag, struct config_strlist** tag_datas, size_t tag_datas_size, char* const* tagname, int num_tags, struct ub_packed_rrset_key** redirect_rrsetp, struct regional* region) { - struct ub_packed_rrset_key* rp = raddr->data; + struct ub_packed_rrset_key* rp = data; struct reply_info* new_rep; *redirect_rrsetp = NULL; @@ -715,7 +717,7 @@ respip_data_answer(const struct resp_addr* raddr, enum respip_action action, * to replace the rrset's dname. Note that, unlike local data, we * rename the dname for other actions than redirect. This is because * response-ip-data isn't associated to any specific name. */ - if(rp == raddr->data) { + if(rp == data) { rp = copy_rrset(rp, region); if(!rp) return -1; @@ -807,16 +809,22 @@ populate_action_info(struct respip_action_info* actinfo, enum respip_action action, const struct resp_addr* raddr, const struct ub_packed_rrset_key* ATTR_UNUSED(rrset), int ATTR_UNUSED(tag), const struct respip_set* ATTR_UNUSED(ipset), - int ATTR_UNUSED(action_only), struct regional* region) + int ATTR_UNUSED(action_only), struct regional* region, int rpz_used, + int rpz_log, char* log_name, int rpz_cname_override) { if(action == respip_none || !raddr) return 1; actinfo->action = action; + actinfo->rpz_used = 1; + actinfo->rpz_log = rpz_log; + actinfo->log_name = log_name; + actinfo->rpz_cname_override = rpz_cname_override; /* for inform variants, make a copy of the matched address block for * later logging. We make a copy to proactively avoid disruption if * and when we allow a dynamic update to the respip tree. */ - if(action == respip_inform || action == respip_inform_deny) { + if(action == respip_inform || action == respip_inform_deny || + rpz_log) { struct respip_addr_info* a = regional_alloc_zero(region, sizeof(*a)); if(!a) { @@ -832,6 +840,30 @@ populate_action_info(struct respip_action_info* actinfo, return 1; } +static int +respip_use_rpz(struct resp_addr* raddr, struct rpz* r, + enum respip_action* action, + struct ub_packed_rrset_key** data, int* rpz_log, char** log_name, + int* rpz_cname_override, struct regional* region) +{ + if(r->action_override == RPZ_DISABLED_ACTION) { + return 0; + } + else if(r->action_override == RPZ_NO_OVERRIDE_ACTION) + *action = raddr->action; + else + *action = rpz_action_to_respip_action(r->action_override); + if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION && + r->cname_override) { + *data = r->cname_override; + *rpz_cname_override = 1; + } + *rpz_log = r->log; + if(r->log_name) + *log_name = regional_strdup(region, r->log_name); + return 1; +} + int respip_rewrite_reply(const struct query_info* qinfo, const struct respip_client_info* cinfo, const struct reply_info* rep, @@ -854,6 +886,11 @@ respip_rewrite_reply(const struct query_info* qinfo, int ret = 1; struct ub_packed_rrset_key* redirect_rrset = NULL; struct rpz* r; + struct ub_packed_rrset_key* data = NULL; + int rpz_used = 0; + int rpz_log = 0; + int rpz_cname_override = 0; + char* log_name = NULL; if(!cinfo) goto done; @@ -903,7 +940,15 @@ respip_rewrite_reply(const struct query_info* qinfo, r->taglistlen, ctaglist, ctaglen)) { if((raddr = respip_addr_lookup(rep, r->respip_set, &rrset_id))) { - action = raddr->action; + } + if(raddr) { + if(!respip_use_rpz(raddr, r, &action, &data, + &rpz_log, &log_name, &rpz_cname_override, + region)) { + lock_rw_unlock(&raddr->lock); + raddr = NULL; + } + rpz_used = 1; } } } @@ -918,9 +963,10 @@ respip_rewrite_reply(const struct query_info* qinfo, && 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, - &redirect_rrset, region)) < 0) { + (data) ? data : raddr->data, qinfo->qtype, rep, + rrset_id, new_repp, tag, tag_datas, tag_datas_size, + ipset->tagname, ipset->num_tags, &redirect_rrset, + region)) < 0) { ret = 0; goto done; } @@ -951,7 +997,8 @@ respip_rewrite_reply(const struct query_info* qinfo, *alias_rrset = redirect_rrset; /* on success, populate respip result structure */ ret = populate_action_info(actinfo, action, raddr, - redirect_rrset, tag, ipset, search_only, region); + redirect_rrset, tag, ipset, search_only, region, + rpz_used, rpz_log, log_name, rpz_cname_override); } if(raddr) lock_rw_unlock(&raddr->lock); @@ -1009,9 +1056,10 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id, qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA || qstate->qinfo.qtype == LDNS_RR_TYPE_ANY) && qstate->return_msg && qstate->return_msg->rep) { - struct respip_action_info actinfo = {respip_none, NULL}; struct reply_info* new_rep = qstate->return_msg->rep; struct ub_packed_rrset_key* alias_rrset = NULL; + struct respip_action_info actinfo = {0}; + actinfo.action = respip_none; if(!respip_rewrite_reply(&qstate->qinfo, qstate->client_info, qstate->return_msg->rep, @@ -1070,7 +1118,8 @@ respip_merge_cname(struct reply_info* base_rep, struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */ uint16_t tgt_rcode; size_t i, j; - struct respip_action_info actinfo = {respip_none, NULL}; + struct respip_action_info actinfo = {0}; + actinfo.action = respip_none; /* If the query for the CNAME target would result in an unusual rcode, * we generally translate it as a failure for the base query @@ -1201,12 +1250,15 @@ respip_set_is_empty(const struct respip_set* set) } void -respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname, +respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname, uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias, struct comm_reply* repinfo) { char srcip[128], respip[128], txt[512]; unsigned port; + struct respip_addr_info* respip_addr = respip_actinfo->addrinfo; + size_t txtlen = 0; + char* actionstr = NULL; if(local_alias) qname = local_alias->rrset->rk.dname; @@ -1216,7 +1268,25 @@ respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname, addr_to_str(&repinfo->addr, repinfo->addrlen, srcip, sizeof(srcip)); addr_to_str(&respip_addr->addr, respip_addr->addrlen, respip, sizeof(respip)); - snprintf(txt, sizeof(txt), "%s/%d inform %s@%u", respip, - respip_addr->net, srcip, port); + if(respip_actinfo->rpz_log) { + txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, "%s", + "RPZ applied "); + if(respip_actinfo->rpz_cname_override) + actionstr = strdup( + rpz_action_to_string(RPZ_CNAME_OVERRIDE_ACTION)); + else + actionstr = strdup(rpz_action_to_string( + respip_action_to_rpz_action( + respip_actinfo->action))); + } + if(respip_actinfo->log_name) { + txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, + "[%s] ", respip_actinfo->log_name); + } + txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, + "%s/%d %s %s@%u", respip, respip_addr->net, + (actionstr) ? actionstr : "inform", srcip, port); + if(actionstr) + free(actionstr); log_nametypeclass(0, txt, qname, qtype, qclass); } diff --git a/respip/respip.h b/respip/respip.h index d2d39872c..4fd3a08d9 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -49,6 +49,7 @@ struct resp_addr { /** * Forward declaration for the structure that represents a tree of view data. */ + struct views; struct respip_addr_info; @@ -78,6 +79,10 @@ struct respip_client_info { */ struct respip_action_info { enum respip_action action; + int rpz_used; + int rpz_log; + char* log_name; + int rpz_cname_override; struct respip_addr_info* addrinfo; /* set only for inform variants */ }; @@ -234,7 +239,7 @@ int respip_set_is_empty(const struct respip_set* set); /** * print log information for a query subject to an inform or inform-deny * response-ip action. - * @param respip_addr: response-ip information that causes the action + * @param respip_actinfo: response-ip information that causes the action * @param qname: query name in the context, will be ignored if local_alias is * non-NULL. * @param qtype: query type, in host byte order. @@ -244,9 +249,9 @@ int respip_set_is_empty(const struct respip_set* set); * query name. * @param repinfo: reply info containing the client's source address and port. */ -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); +void respip_inform_print(struct respip_action_info* respip_actinfo, + 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. diff --git a/services/mesh.c b/services/mesh.c index bee0f76a4..65f26390e 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1180,10 +1180,19 @@ void mesh_query_done(struct mesh_state* mstate) * information should be logged for each client. */ if(mstate->s.respip_action_info && mstate->s.respip_action_info->addrinfo) { - respip_inform_print(mstate->s.respip_action_info->addrinfo, + respip_inform_print(mstate->s.respip_action_info, r->qname, mstate->s.qinfo.qtype, mstate->s.qinfo.qclass, r->local_alias, &r->query_reply); + if(mstate->s.env->cfg->stat_extended && + mstate->s.respip_action_info->rpz_used) { + /* TODO: does not work for disabled (override) actions */ + if(mstate->s.respip_action_info->rpz_cname_override) + mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; + else + mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action( + mstate->s.respip_action_info->action)]++; + } } /* if this query is determined to be dropped during the @@ -1580,6 +1589,7 @@ mesh_stats_clear(struct mesh_area* mesh) mesh->ans_secure = 0; mesh->ans_bogus = 0; memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*16); + memset(&mesh->rpz_action[0], 0, sizeof(size_t)*10); mesh->ans_nodata = 0; } diff --git a/services/mesh.h b/services/mesh.h index a2622844b..c28176465 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -51,6 +51,7 @@ #include "util/data/msgparse.h" #include "util/module.h" #include "services/modstack.h" +#include "services/rpz.h" struct sldns_buffer; struct mesh_state; struct mesh_reply; @@ -124,6 +125,8 @@ struct mesh_area { size_t ans_rcode[16]; /** (extended stats) rcode nodata in replies */ size_t ans_nodata; + /** (extended stats) type of applied RPZ action */ + size_t rpz_action[10]; /** backup of query if other operations recurse and need the * network buffers */ diff --git a/services/rpz.c b/services/rpz.c index ecf32f430..543ab148b 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -209,7 +209,7 @@ rpz_action_to_localzone_type(enum rpz_action a) } } -static enum respip_action +enum respip_action rpz_action_to_respip_action(enum rpz_action a) { switch(a) { @@ -242,6 +242,21 @@ localzone_type_to_rpz_action(enum localzone_type lzt) } } +enum rpz_action +respip_action_to_rpz_action(enum respip_action a) +{ + switch(a) { + case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION; + case respip_always_nodata: return RPZ_NODATA_ACTION; + case respip_deny: return RPZ_DROP_ACTION; + case respip_always_transparent: return RPZ_PASSTHRU_ACTION; + case respip_redirect: return RPZ_LOCAL_DATA_ACTION; + case respip_invalid: + default: + return RPZ_INVALID_ACTION; + } +} + /** * Get RPZ trigger for dname * @param dname: dname containing RPZ trigger @@ -861,6 +876,8 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, log_rpz_apply(z->name, r->action_override, qinfo, repinfo, r->log_name); + /* TODO only register stats when stats_extended? + * */ stats->rpz_action[r->action_override]++; lock_rw_unlock(&z->lock); z = NULL; diff --git a/services/rpz.h b/services/rpz.h index a5b9e67ab..042773f33 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -178,10 +178,21 @@ struct rpz* rpz_create(struct config_auth* p); */ const char* rpz_action_to_string(enum rpz_action a); +enum rpz_action +respip_action_to_rpz_action(enum respip_action a); + /** * Prepare RPZ after procesing feed content. * @param r: RPZ to use */ void rpz_finish_config(struct rpz* r); +/** + * Classify respip action for RPZ action + * @param a: RPZ action + * @return: the respip action + */ +enum respip_action +rpz_action_to_respip_action(enum rpz_action a); + #endif /* SERVICES_RPZ_H */