]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
RPZ: towards client ip trigger and local data action
authormb <mb@64k.by>
Fri, 6 Nov 2020 11:54:32 +0000 (12:54 +0100)
committermb <mb@64k.by>
Fri, 6 Nov 2020 11:54:32 +0000 (12:54 +0100)
respip/respip.c
respip/respip.h
services/rpz.c
testdata/rpz_clientip.rpl

index 82ca37d50b01209933624aa207e5ee828e731bda..75400bb4d994a53cf99a89cbdc6b1d8d66170eb2 100644 (file)
@@ -483,8 +483,8 @@ respip_views_apply_cfg(struct views* vs, struct config_file* cfg,
  * This function returns the copied rrset key on success, and NULL on memory
  * allocation failure.
  */
-static struct ub_packed_rrset_key*
-copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region)
+struct ub_packed_rrset_key*
+respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region)
 {
        struct ub_packed_rrset_key* ck = regional_alloc(region,
                sizeof(struct ub_packed_rrset_key));
@@ -730,7 +730,7 @@ respip_data_answer(enum respip_action action,
                                "response-ip redirect with tag data [%d] %s",
                                tag, (tag<num_tags?tagname[tag]:"null"));
                        /* use copy_rrset() to 'normalize' memory layout */
-                       rp = copy_rrset(&r, region);
+                       rp = respip_copy_rrset(&r, region);
                        if(!rp)
                                return -1;
                }
@@ -743,7 +743,7 @@ respip_data_answer(enum respip_action action,
         * rename the dname for other actions than redirect.  This is because
         * response-ip-data isn't associated to any specific name. */
        if(rp == data) {
-               rp = copy_rrset(rp, region);
+               rp = respip_copy_rrset(rp, region);
                if(!rp)
                        return -1;
                rp->rk.dname = rep->rrsets[rrset_id]->rk.dname;
@@ -1208,7 +1208,7 @@ respip_merge_cname(struct reply_info* base_rep,
        if(!new_rep)
                return 0;
        for(i=0,j=base_rep->an_numrrsets; i<tgt_rep->an_numrrsets; i++,j++) {
-               new_rep->rrsets[j] = copy_rrset(tgt_rep->rrsets[i], region);
+               new_rep->rrsets[j] = respip_copy_rrset(tgt_rep->rrsets[i], region);
                if(!new_rep->rrsets[j])
                        return 0;
        }
index bbd471421c1ee0faad8a1cc3451a7306b0196245..3dfb4e9f01c755ab9f054a99ef8867eb1fa44911 100644 (file)
@@ -294,4 +294,7 @@ respip_enter_rr(struct regional* region, struct resp_addr* raddr,
  */
 void
 respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node);
+
+struct ub_packed_rrset_key*
+respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region);
 #endif /* RESPIP_RESPIP_H */
index f8fa803de62327c3ca08bfcec8357f1f262a85f8..4bf845547762da7152133e2a9ec5be91ea4cb57f 100644 (file)
@@ -50,6 +50,7 @@
 #include "util/data/dname.h"
 #include "util/locks.h"
 #include "util/regional.h"
+#include "util/data/msgencode.h"
 
 typedef struct resp_addr rpz_aclnode_type;
 
@@ -1096,27 +1097,7 @@ rpz_resolve_final_localzone_action(struct rpz* r, struct local_zone* z, enum rpz
 {
        enum localzone_type lzt;
        if(r->action_override == RPZ_NO_OVERRIDE_ACTION) {
-               switch (client_action) {
-               case RPZ_NODATA_ACTION:
-               case RPZ_NXDOMAIN_ACTION:
-               case RPZ_DROP_ACTION:
-               case RPZ_TCP_ONLY_ACTION:
-               case RPZ_PASSTHRU_ACTION:
-                       lzt = rpz_action_to_localzone_type(client_action);
-                       break;
-               case RPZ_LOCAL_DATA_ACTION:
-                       verbose(VERB_ALGO,
-                               "RPZ: client ip trigger with local-data unimplemented:"
-                               " defaulting to rpz-passthru");
-                       lzt = rpz_action_to_localzone_type(RPZ_PASSTHRU_ACTION);
-                       break;
-               case RPZ_INVALID_ACTION:
-                       lzt = z->type;
-                       break;
-               default:
-                       lzt = z->type;
-                       break;
-               }
+               lzt = z->type;
        } else {
                lzt = rpz_action_to_localzone_type(r->action_override);
        }
@@ -1132,6 +1113,88 @@ rpz_is_udp_query(struct comm_reply* repinfo) {
                        : 0;
 }
 
+/** encode answer consisting of 1 rrset */
+static int
+rpz_local_encode(struct query_info* qinfo, struct module_env* env,
+       struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
+       struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec,
+       int rcode)
+{
+       struct reply_info rep;
+       uint16_t udpsize;
+       /* make answer with time=0 for fixed TTL values */
+       memset(&rep, 0, sizeof(rep));
+       rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode);
+       rep.qdcount = 1;
+       if(ansec)
+               rep.an_numrrsets = 1;
+       else    rep.ns_numrrsets = 1;
+       rep.rrset_count = 1;
+       rep.rrsets = &rrset;
+
+       udpsize = edns->udp_size;
+       edns->edns_version = EDNS_ADVERTISED_VERSION;
+       edns->udp_size = EDNS_ADVERTISED_SIZE;
+       edns->ext_rcode = 0;
+       edns->bits &= EDNS_DO;
+       //!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,repinfo, temp) ||
+       if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,repinfo, temp) ||!reply_info_answer_encode(qinfo, &rep,
+               *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2),
+               buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) {
+               error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
+                       *(uint16_t*)sldns_buffer_begin(buf),
+                       sldns_buffer_read_u16_at(buf, 2), edns);
+       }
+       return 1;
+}
+
+static void
+rpz_apply_clientip_localdata_action(struct rpz* r, struct module_env* env,
+       struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo,
+       sldns_buffer* buf, struct regional* temp)
+{
+       struct resp_addr* raddr = NULL;
+       enum rpz_action action = RPZ_INVALID_ACTION;
+       struct sockaddr_storage* addr = &repinfo->addr;
+       socklen_t addrlen = repinfo->addrlen;
+       struct ub_packed_rrset_key* rp = NULL;
+       int rcode = LDNS_RCODE_NOERROR|BIT_AA;
+
+       verbose(VERB_ALGO, "RPZ: apply client ip trigger: found=%d action=%s",
+               raddr != NULL, rpz_action_to_string(action));
+
+       lock_rw_rdlock(&r->client_set->lock);
+
+       raddr = (struct resp_addr*)addr_tree_lookup(&r->client_set->ip_tree,
+                       addr, addrlen);
+       if(raddr == NULL) {
+               lock_rw_unlock(&r->client_set->lock);
+               return;
+       }
+
+       /* prepare synthesized answer for the matching client */
+
+       action = respip_action_to_rpz_action(raddr->action);
+       if(action != RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) {
+               verbose(VERB_ALGO, "RPZ: bug: local-data action and no local data");
+               goto done;
+       }
+       rp = respip_copy_rrset(raddr->data, temp);
+       if(!rp) {
+               verbose(VERB_ALGO, "RPZ: local-data action: out of memory");
+               goto done;
+       }
+       rp->rk.flags |= PACKED_RRSET_FIXEDTTL;
+       rp->rk.dname = qinfo->qname;
+       rp->rk.dname_len = qinfo->qname_len;
+       rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, 1, rcode);
+
+done:
+       lock_rw_unlock(&raddr->lock);
+       lock_rw_unlock(&r->client_set->lock);
+
+}
+
 int
 rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
        struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
@@ -1152,7 +1215,8 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
        verbose(VERB_ALGO, "RPZ: qname trigger: client action=%s",
                rpz_action_to_string(client_action));
 
-       if(!z) {
+       if(z == NULL || (client_action != RPZ_INVALID_ACTION &&
+                        client_action != RPZ_PASSTHRU_ACTION)) {
                verbose(VERB_ALGO, "RPZ: client action without zone");
                if(client_action == RPZ_PASSTHRU_ACTION
                        || client_action == RPZ_INVALID_ACTION
@@ -1160,11 +1224,16 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
                                && !rpz_is_udp_query(repinfo))) {
                        return 0;
                }
-               // XXX: log_rpz_apply not possbile because no zone
                stats->rpz_action[client_action]++;
-               local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns,
-                                       repinfo, buf, temp, 0 /* no local data used */,
-                                       rpz_action_to_localzone_type(client_action));
+               if(client_action == RPZ_LOCAL_DATA_ACTION) {
+                       rpz_apply_clientip_localdata_action(r, env, qinfo, edns,
+                               repinfo, buf, temp);
+               } else {
+                       // XXX: log_rpz_apply not possbile because no zone
+                       local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns,
+                               repinfo, buf, temp, 0 /* no local data used */,
+                               rpz_action_to_localzone_type(client_action));
+               }
                return 1;
        }
 
index bd559871bbfe43bf938eb2eeb54329f4046336a1..24256cbf39921f4f47d6e5d6262cb441c7f41b14 100644 (file)
@@ -21,6 +21,7 @@ $ORIGIN rpz.example.com.
 24.0.2.0.192.rpz-client-ip CNAME rpz-drop.
 24.0.3.0.192.rpz-client-ip CNAME rpz-passthru.
 24.0.4.0.192.rpz-client-ip CNAME rpz-tcp-only.
+24.0.5.0.192.rpz-client-ip A 127.0.0.1
 TEMPFILE_END
 
 stub-zone:
@@ -168,6 +169,25 @@ SECTION ANSWER
 a.a.  IN TXT "upstream txt rr a.a."
 ENTRY_END
 
+; should be synthesized
+
+STEP 60 QUERY ADDRESS 192.0.5.1
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.a.  IN TXT
+ENTRY_END
+
+STEP 61 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR AA RD RA NOERROR
+SECTION QUESTION
+a.a.  IN A
+SECTION ANSWER
+a.a.  IN A 127.0.0.1
+ENTRY_END
+
 ; should be DROPPED
 
 STEP 90 QUERY ADDRESS 192.0.2.1