]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- rpz triggers, implement qname trigger after cname.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 19 Mar 2021 16:31:44 +0000 (17:31 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 19 Mar 2021 16:31:44 +0000 (17:31 +0100)
iterator/iterator.c
services/rpz.c
services/rpz.h
testdata/rpz_qname.rpl

index 1ebbfe27732f9c70895334bf64eaea434c68d6ec..63f8cbd563963f09fac58822845d0503069945be 100644 (file)
@@ -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;
index 3988bba46eef6ddb8bd8b7853b78fe7107a15c66..538429e515c463387d5884f2ae66e8484e5aad88 100644 (file)
@@ -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,
index 62c345f293ba8f7cf0de5d68ffe6ab209b324743..74a8d09e21b81b2ed3283e7153c751fc8d751cde 100644 (file)
@@ -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
index dd47f188a104914ddbff29ea36675724790878b0..2bc038c3702cfcaf2d6972acd8b48ff29942cb09 100644 (file)
@@ -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