]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
rpz: continue work on the nsip trigger
authormb <mb@64k.by>
Mon, 16 Nov 2020 11:42:23 +0000 (12:42 +0100)
committermb <mb@64k.by>
Mon, 16 Nov 2020 11:42:23 +0000 (12:42 +0100)
respip/respip.c
services/rpz.c
testdata/rpz_nsip.rpl
util/data/msgreply.c
util/data/msgreply.h

index 75400bb4d994a53cf99a89cbdc6b1d8d66170eb2..40f8396451cb4ad61103fe3208339db8ab58a0d7 100644 (file)
@@ -635,43 +635,6 @@ respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs,
        return NULL;
 }
 
-/*
- * Create a new reply_info based on 'rep'.  The new info is based on
- * the passed 'rep', but ignores any rrsets except for the first 'an_numrrsets'
- * RRsets in the answer section.  These answer rrsets are copied to the
- * new info, up to 'copy_rrsets' rrsets (which must not be larger than
- * 'an_numrrsets').  If an_numrrsets > copy_rrsets, the remaining rrsets array
- * entries will be kept empty so the caller can fill them later.  When rrsets
- * are copied, they are shallow copied.  The caller must ensure that the
- * copied rrsets are valid throughout its lifetime and must provide appropriate
- * mutex if it can be shared by multiple threads.
- */
-static struct reply_info *
-make_new_reply_info(const struct reply_info* rep, struct regional* region,
-       size_t an_numrrsets, size_t copy_rrsets)
-{
-       struct reply_info* new_rep;
-       size_t i;
-
-       /* create a base struct.  we specify 'insecure' security status as
-        * the modified response won't be DNSSEC-valid.  In our faked response
-        * the authority and additional sections will be empty (except possible
-        * EDNS0 OPT RR in the additional section appended on sending it out),
-        * so the total number of RRsets is an_numrrsets. */
-       new_rep = construct_reply_info_base(region, rep->flags,
-               rep->qdcount, rep->ttl, rep->prefetch_ttl,
-               rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets,
-               sec_status_insecure);
-       if(!new_rep)
-               return NULL;
-       if(!reply_info_alloc_rrset_keys(new_rep, NULL, region))
-               return NULL;
-       for(i=0; i<copy_rrsets; i++)
-               new_rep->rrsets[i] = rep->rrsets[i];
-
-       return new_rep;
-}
-
 /**
  * See if response-ip or tag data should override the original answer rrset
  * (which is rep->rrsets[rrset_id]) and if so override it.
index 9b1d23884d7ac7e8acb4704024b18531fa29d7b4..eae3feeeb36feeae146bb903777d4626f946d1dd 100644 (file)
@@ -1361,11 +1361,11 @@ rpz_local_encode(struct query_info* qinfo, struct module_env* env,
 }
 
 static struct local_rrset*
-rpz_clientip_find_rrset(struct query_info* qinfo, struct clientip_synthesized_rr* data) {
+rpz_find_synthesized_rrset(int qtype, struct clientip_synthesized_rr* data) {
        struct local_rrset* cursor = data->data;
        while( cursor != NULL) {
                struct packed_rrset_key* packed_rrset = &cursor->rrset->rk;
-               if(htons(qinfo->qtype) == packed_rrset->type) {
+               if(htons(qtype) == packed_rrset->type) {
                        return cursor;
                }
                cursor = cursor->next;
@@ -1397,7 +1397,7 @@ rpz_apply_clientip_localdata_action(struct rpz* r, struct clientip_synthesized_r
 
        /* check query type / rr type */
 
-       rrset = rpz_clientip_find_rrset(qinfo, raddr);
+       rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr);
        if(rrset == NULL) {
                verbose(VERB_ALGO, "rpz: unable to find local-data for query");
                rrset_count = 0;
@@ -1420,19 +1420,82 @@ nodata:
        rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, rrset_count, rcode);
 }
 
+static inline void
+rpz_patch_nodata(struct reply_info* ri)
+{
+       FLAGS_SET_RCODE(ri->flags, LDNS_RCODE_NOERROR);
+       ri->flags |= BIT_QR | BIT_AA | BIT_RA;
+       ri->an_numrrsets = 0;
+       ri->ns_numrrsets = 0;
+       ri->ar_numrrsets = 0;
+       ri->authoritative = 0;
+       ri->rrset_count = 1;
+       ri->qdcount = 1;
+}
+
+static inline void
+rpz_patch_nxdomain(struct reply_info* ri)
+{
+       FLAGS_SET_RCODE(ri->flags, LDNS_RCODE_NXDOMAIN);
+       ri->flags |= BIT_QR | BIT_AA | BIT_RA;
+       ri->an_numrrsets = 0;
+       ri->ns_numrrsets = 0;
+       ri->ar_numrrsets = 0;
+       ri->authoritative = 0;
+       ri->rrset_count = 1;
+       ri->qdcount = 1;
+}
+
+static inline int
+rpz_patch_localdata(struct dns_msg* response, struct clientip_synthesized_rr* data,
+       struct regional* region)
+{
+       struct query_info* qi = &response->qinfo;
+       struct ub_packed_rrset_key* rp;
+       struct local_rrset* rrset;
+       struct reply_info* new_reply_info;
+       struct reply_info* ri = response->rep;
+
+       rrset = rpz_find_synthesized_rrset(qi->qtype, data);
+       if(rrset == NULL) {
+               verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata");
+               rpz_patch_nodata(ri);
+               return 1;
+       }
+       new_reply_info = make_new_reply_info(ri, region, 0, 0);
+       if(new_reply_info == NULL) {
+               log_err("out of memory");
+               rpz_patch_nodata(ri);
+               return 1;
+       }
+       rp = respip_copy_rrset(rrset->rrset, region);
+       if(rp == NULL) {
+               log_err("out of memory");
+               rpz_patch_nodata(ri);
+               return 1;
+       }
+       new_reply_info->rrsets = regional_alloc(region, sizeof(*new_reply_info->rrsets));
+       if(new_reply_info->rrsets == NULL) {
+               log_err("out of memory");
+               rpz_patch_nodata(ri);
+               return 1;
+       }
+       rp->rk.dname = qi->qname;
+       rp->rk.dname_len = qi->qname_len;
+       new_reply_info->rrset_count = 1;
+       new_reply_info->an_numrrsets = 1;
+       new_reply_info->rrsets[0] = rp;
+       response->rep = new_reply_info;
+       return 1;
+}
+
 int
 rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is)
 {
        struct auth_zones* az = ms->env->auth_zones;
        struct auth_zone* a;
        struct clientip_synthesized_rr* raddr;
-       struct local_rrset* rrset;
        enum rpz_action action = RPZ_INVALID_ACTION;
-       struct sockaddr_storage* addr = &ms->reply->addr;
-       socklen_t addrlen = ms->reply->addrlen;
-       struct ub_packed_rrset_key* rp = NULL;
-       int rcode = LDNS_RCODE_NOERROR|BIT_AA;
-       int rrset_count = 1;
        struct rpz* r;
        int ret = 0;
 
@@ -1466,39 +1529,29 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is)
 
        switch(action) {
        case RPZ_NXDOMAIN_ACTION:
-               FLAGS_SET_RCODE(is->response->rep->flags, LDNS_RCODE_NXDOMAIN);
-               is->response->rep->flags |= BIT_QR | BIT_AA | BIT_RA;
-               is->response->rep->an_numrrsets = 0;
-               is->response->rep->ns_numrrsets = 0;
-               is->response->rep->ar_numrrsets = 0;
-               is->response->rep->authoritative = 1;
-               is->response->rep->qdcount = 1;
+               rpz_patch_nxdomain(is->response->rep);
                ret = 1;
                break;
        case RPZ_NODATA_ACTION:
-               FLAGS_SET_RCODE(is->response->rep->flags, LDNS_RCODE_NOERROR);
-               is->response->rep->flags |= BIT_QR | BIT_AA | BIT_RA;
-               is->response->rep->an_numrrsets = 0;
-               is->response->rep->ns_numrrsets = 0;
-               is->response->rep->ar_numrrsets = 0;
-               is->response->rep->authoritative = 1;
-               is->response->rep->qdcount = 1;
+               rpz_patch_nodata(is->response->rep);
                ret = 1;
                break;
+       case RPZ_TCP_ONLY_ACTION:
+               log_err("rpz: nsip: tcp-only trigger unimplemented; resorting to passthru");
+               ret = 0;
+               break;
        case RPZ_PASSTHRU_ACTION:
                ret = 0;
                break;
+       case RPZ_LOCAL_DATA_ACTION:
+               ret = rpz_patch_localdata(is->response, raddr, ms->region);
+               break;
        default:
+               verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'",
+                       rpz_action_to_string(action));
                ret = 0;
        }
 
-       //rrset = rpz_clientip_find_rrset(qinfo, raddr);
-       //if(rrset == NULL) {
-       //      verbose(VERB_ALGO, "rpz: unable to find local-data for query");
-       //      rrset_count = 0;
-       //      goto nodata;
-       //}
-
 done:
        lock_rw_unlock(&raddr->lock);
        return ret;
index 1d4462df09ca82215f1456d92f332ea451517645..ac9e80b80ea32e263da9c2fa6d1e4752cc5f7bc8 100644 (file)
@@ -119,6 +119,18 @@ SECTION ADDITIONAL
 ns1.ee. IN A 8.8.5.8
 ENTRY_END
 
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+ff. IN A
+SECTION AUTHORITY
+ff. IN NS ns1.ff.
+SECTION ADDITIONAL
+ns1.ff. IN A 8.8.6.8
+ENTRY_END
+
 RANGE_END
 
 ; com. -----------------------------------------------------------------------
@@ -192,7 +204,7 @@ REPLY QR NOERROR
 SECTION QUESTION
 bb. IN NS
 SECTION ANSWER
-bb. IN NS ns1.aa.
+bb. IN NS ns1.bb.
 SECTION ADDITIONAL
 ns1.bb. IN A 8.8.1.8
 ENTRY_END
@@ -211,6 +223,36 @@ ENTRY_END
 
 RANGE_END
 
+; ff. ------------------------------------------------------------------------
+RANGE_BEGIN 0 100
+       ADDRESS 8.8.6.8
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ff. IN NS
+SECTION ANSWER
+ff. IN NS ns1.ff.
+SECTION ADDITIONAL
+ns1.ff. IN A 8.8.6.8
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham.ff. IN A
+SECTION AUTHORITY
+gotham.ff. IN NS ns1.gotham.ff.
+SECTION ADDITIONAL
+ns1.gotham.ff. IN A 192.0.5.1
+ENTRY_END
+
+RANGE_END
+
 ; ns1.gotham.com. ------------------------------------------------------------
 RANGE_BEGIN 0 100
        ADDRESS 192.0.6.1
@@ -259,6 +301,24 @@ ENTRY_END
 
 RANGE_END
 
+; ns1.gotham.ff. -------------------------------------------------------------
+RANGE_BEGIN 0 100
+       ADDRESS 192.0.5.1
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+gotham.ff. IN A
+SECTION ANSWER
+gotham.ff. IN A 192.0.5.2
+ENTRY_END
+
+RANGE_END
+
+; ----------------------------------------------------------------------------
+
 STEP 1 QUERY
 ENTRY_BEGIN
 REPLY RD
@@ -308,4 +368,21 @@ gotham.bb. IN A
 SECTION ANSWER
 ENTRY_END
 
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+gotham.ff. IN A
+ENTRY_END
+
+STEP 31 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+gotham.ff. IN A
+SECTION ANSWER
+gotham.ff. IN A 127.0.0.1
+ENTRY_END
+
 SCENARIO_END
index 927bf09a29d8f8372e19c700966c2cdef2a1e4fd..fed6e5345478d90888e57b4e07dececa157272b0 100644 (file)
@@ -164,6 +164,32 @@ reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc,
        return 1;
 }
 
+struct reply_info *
+make_new_reply_info(const struct reply_info* rep, struct regional* region,
+       size_t an_numrrsets, size_t copy_rrsets)
+{
+       struct reply_info* new_rep;
+       size_t i;
+
+       /* create a base struct.  we specify 'insecure' security status as
+        * the modified response won't be DNSSEC-valid.  In our faked response
+        * the authority and additional sections will be empty (except possible
+        * EDNS0 OPT RR in the additional section appended on sending it out),
+        * so the total number of RRsets is an_numrrsets. */
+       new_rep = construct_reply_info_base(region, rep->flags,
+               rep->qdcount, rep->ttl, rep->prefetch_ttl,
+               rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets,
+               sec_status_insecure);
+       if(!new_rep)
+               return NULL;
+       if(!reply_info_alloc_rrset_keys(new_rep, NULL, region))
+               return NULL;
+       for(i=0; i<copy_rrsets; i++)
+               new_rep->rrsets[i] = rep->rrsets[i];
+
+       return new_rep;
+}
+
 /** find the minimumttl in the rdata of SOA record */
 static time_t
 soa_find_minttl(struct rr_parse* rr)
index 385780268a3ca28e6fe0c15e0aed4a83dac81647..64ff4dfbe4b50832ae4bc74fb8b430cdfce98502 100644 (file)
@@ -382,6 +382,21 @@ struct reply_info* reply_info_copy(struct reply_info* rep,
 int reply_info_alloc_rrset_keys(struct reply_info* rep,
        struct alloc_cache* alloc, struct regional* region);
 
+/*
+ * Create a new reply_info based on 'rep'.  The new info is based on
+ * the passed 'rep', but ignores any rrsets except for the first 'an_numrrsets'
+ * RRsets in the answer section.  These answer rrsets are copied to the
+ * new info, up to 'copy_rrsets' rrsets (which must not be larger than
+ * 'an_numrrsets').  If an_numrrsets > copy_rrsets, the remaining rrsets array
+ * entries will be kept empty so the caller can fill them later.  When rrsets
+ * are copied, they are shallow copied.  The caller must ensure that the
+ * copied rrsets are valid throughout its lifetime and must provide appropriate
+ * mutex if it can be shared by multiple threads.
+ */
+struct reply_info *
+make_new_reply_info(const struct reply_info* rep, struct regional* region,
+       size_t an_numrrsets, size_t copy_rrsets);
+
 /**
  * Copy a parsed rrset into given key, decompressing and allocating rdata.
  * @param pkt: packet for decompression