From: W.C.A. Wijngaards Date: Fri, 19 Mar 2021 16:31:44 +0000 (+0100) Subject: - rpz triggers, implement qname trigger after cname. X-Git-Tag: release-1.14.0rc1~62^2~37 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7f39003c04c2f663f5e1697b7be22724076b5e5b;p=thirdparty%2Funbound.git - rpz triggers, implement qname trigger after cname. --- diff --git a/iterator/iterator.c b/iterator/iterator.c index 1ebbfe277..63f8cbd56 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -3013,6 +3013,38 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, /* set the current request's qname to the new value. */ iq->qchase.qname = sname; iq->qchase.qname_len = snamelen; + if(qstate->env->auth_zones) { + /* apply rpz qname triggers after cname */ + struct dns_msg* forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + while(forged_response && reply_find_rrset_section_an( + forged_response->rep, iq->qchase.qname, + iq->qchase.qname_len, LDNS_RR_TYPE_CNAME, + iq->qchase.qclass)) { + /* another cname to follow */ + if(!handle_cname_response(qstate, iq, forged_response, + &sname, &snamelen)) { + errinf(qstate, "malloc failure, CNAME info"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + iq->qchase.qname = sname; + iq->qchase.qname_len = snamelen; + forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + } + if(forged_response != NULL) { + qstate->ext_state[id] = module_finished; + qstate->return_rcode = FLAGS_GET_RCODE(forged_response->rep->flags); + qstate->return_msg = forged_response; + next_state(iq, FINISHED_STATE); + if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { + log_err("rpz, prepend rrsets: out of memory"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + qstate->return_msg->qinfo = qstate->qinfo; + return 0; + } + } /* Clear the query state, since this is a query restart. */ iq->deleg_msg = NULL; iq->dp = NULL; diff --git a/services/rpz.c b/services/rpz.c index 3988bba46..538429e51 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1607,10 +1607,9 @@ rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms) static inline struct dns_msg* rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, - struct local_rrset* rrset) + struct query_info* qi, struct local_rrset* rrset) { struct dns_msg* msg = NULL; - struct query_info* qi = &ms->qinfo; struct reply_info* new_reply_info; struct ub_packed_rrset_key* rp; @@ -1659,7 +1658,7 @@ rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, rrset); + return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset); } // copy'n'paste from localzone.c @@ -1708,7 +1707,32 @@ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, rrset); + return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset); +} + +/* like local_data_answer for qname triggers after a cname */ +static struct dns_msg* +rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms, + struct query_info* qinfo, struct local_zone* z) +{ + struct local_data key; + struct local_data* ld; + struct local_rrset* rrset; + key.node.key = &key; + key.name = qinfo->qname; + key.namelen = qinfo->qname_len; + key.namelabs = dname_count_labels(qinfo->qname); + ld = (struct local_data*)rbtree_search(&z->data, &key.node); + if(ld == NULL) { + verbose(VERB_ALGO, "rpz: qname after cname: name not found"); + return NULL; + } + rrset = local_data_find_type(ld, qinfo->qtype, 1); + if(rrset == NULL) { + verbose(VERB_ALGO, "rpz: qname after cname: type not found"); + return NULL; + } + return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset); } static int @@ -1965,6 +1989,86 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* else { return NULL; } } +struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, + struct iter_qstate* is) +{ + struct auth_zones* az; + struct auth_zone* a = NULL; + struct rpz* r = NULL; + struct local_zone* z = NULL; + enum localzone_type lzt; + struct dns_msg* ret = NULL; + + if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } + az = ms->env->auth_zones; + + lock_rw_rdlock(&az->rpz_lock); + + for(a = az->rpz_first; a; a = a->rpz_az_next) { + lock_rw_rdlock(&a->lock); + r = a->rpz; + if(r->disabled) { + lock_rw_unlock(&a->lock); + continue; + } + z = rpz_find_zone(r->local_zones, is->qchase.qname, + is->qchase.qname_len, is->qchase.qclass, 0, 0, 0); + if(z && r->action_override == RPZ_DISABLED_ACTION) { + lock_rw_unlock(&z->lock); + z = NULL; + } + if(z) { + break; + } + /* not found in this auth_zone */ + lock_rw_unlock(&a->lock); + } + lock_rw_unlock(&az->rpz_lock); + + if(z == NULL) + return NULL; + if(r->action_override == RPZ_NO_OVERRIDE_ACTION) { + lzt = z->type; + } else { + lzt = rpz_action_to_localzone_type(r->action_override); + } + + verbose(VERB_ALGO, "rpz: qname trigger after cname, with action=%s", + rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + switch(localzone_type_to_rpz_action(lzt)) { + case RPZ_NXDOMAIN_ACTION: + ret = rpz_synthesize_nxdomain(r, ms); + break; + case RPZ_NODATA_ACTION: + ret = rpz_synthesize_nodata(r, ms); + break; + case RPZ_TCP_ONLY_ACTION: + /* basically a passthru here but the tcp-only will be + * honored before the query gets sent. */ + ms->respip_action_info->action = respip_truncate; + ret = NULL; + break; + case RPZ_DROP_ACTION: + ret = rpz_synthesize_nodata(r, ms); + ms->is_drop = 1; + break; + case RPZ_LOCAL_DATA_ACTION: + ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } + break; + case RPZ_PASSTHRU_ACTION: + ret = NULL; + break; + default: + verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'", + rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + ret = NULL; + } + lock_rw_unlock(&z->lock); + lock_rw_unlock(&a->lock); + return ret; +} + static int rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, diff --git a/services/rpz.h b/services/rpz.h index 62c345f29..74a8d09e2 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -183,6 +183,8 @@ int rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* e struct iter_qstate; struct dns_msg* rpz_callback_from_iterator_module(struct module_qstate*, struct iter_qstate*); +struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate*, struct iter_qstate*); + /** * Delete RPZ * @param r: RPZ struct to delete diff --git a/testdata/rpz_qname.rpl b/testdata/rpz_qname.rpl index dd47f188a..2bc038c37 100644 --- a/testdata/rpz_qname.rpl +++ b/testdata/rpz_qname.rpl @@ -126,6 +126,16 @@ SECTION ANSWER something.e.b.example. IN TXT "*.b.example. answer from upstream ns" ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +f.example. IN TXT +SECTION ANSWER +f.example. IN CNAME d. +ENTRY_END + RANGE_END ; tcp. @@ -367,5 +377,24 @@ SECTION ANSWER tcp. IN TXT "tcp. answer from upstream ns" ENTRY_END +; check if the name after the CNAME has the qname trigger applied to it. +STEP 100 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +f.example. IN TXT +ENTRY_END + +STEP 101 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +f.example. IN TXT +SECTION ANSWER +f.example. IN CNAME d. +d. IN TXT "local data 2nd zone" +ENTRY_END + ; no answer is checked at exit of testbound. SCENARIO_END