]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Merge branch 'rpz' of https://github.com/magenbluten/unbound into magenbluten-rpz
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 14 Jan 2021 11:11:29 +0000 (12:11 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 14 Jan 2021 11:11:29 +0000 (12:11 +0100)
Conflict fixed for rpz.disabled check added.

1  2 
daemon/worker.c
respip/respip.c
services/localzone.c
services/localzone.h
services/mesh.c
services/rpz.c
services/rpz.h
util/data/msgreply.c
util/data/msgreply.h

diff --cc daemon/worker.c
Simple merge
diff --cc respip/respip.c
Simple merge
index ed0d2c56558790206ef372e9bbeab66fd9612af5,5e2104a7cf923365ca839e409cd375cb9c4b7e08..2458842aa641175c92672c808ecf7ff85457da2a
@@@ -1802,8 -1775,8 +1815,9 @@@ const char* local_zone_type2str(enum lo
                case local_zone_always_nxdomain: return "always_nxdomain";
                case local_zone_always_nodata: return "always_nodata";
                case local_zone_always_deny: return "always_deny";
 +              case local_zone_always_null: return "always_null";
                case local_zone_noview: return "noview";
+               case local_zone_truncate: return "truncate";
                case local_zone_invalid: return "invalid";
        }
        return "badtyped"; 
@@@ -1839,10 -1812,10 +1853,12 @@@ int local_zone_str2type(const char* typ
                *t = local_zone_always_nodata;
        else if(strcmp(type, "always_deny") == 0)
                *t = local_zone_always_deny;
 +      else if(strcmp(type, "always_null") == 0)
 +              *t = local_zone_always_null;
        else if(strcmp(type, "noview") == 0)
                *t = local_zone_noview;
+       else if(strcmp(type, "truncate") == 0)
+               *t = local_zone_truncate;
        else if(strcmp(type, "nodefault") == 0)
                *t = local_zone_nodefault;
        else return 0;
index 492629936dad04152b0e39c26fde3c37851f365d,39517c36a7fd36387c101052c8ca3d97904ce024..de2a7d9a39f264a8fbb6d3b570585acb769dcf2e
@@@ -96,11 -96,10 +96,13 @@@ enum localzone_type 
        local_zone_always_nodata,
        /** drop query, even when there is local data */
        local_zone_always_deny,
 +      /** answer with 0.0.0.0 or ::0 or noerror/nodata, even when there is
 +       * local data */
 +      local_zone_always_null,
        /** answer not from the view, but global or no-answer */
        local_zone_noview,
+       /** truncate the response; client should retry via tcp */
+       local_zone_truncate,
        /** Invalid type, cannot be used to generate answer */
        local_zone_invalid
  };
diff --cc services/mesh.c
Simple merge
diff --cc services/rpz.c
index d7dd17f7e454c2312d844bb283d8d139ad46af72,777007b4234fce33259c872a82b16ab5e9269cb1..0d825238f1ae7a49eae478023b81875652d5d2fe
@@@ -963,50 -1344,320 +1344,324 @@@ rpz_resolve_client_action_and_zone(stru
        for(a = az->rpz_first; a; a = a->rpz_az_next) {
                lock_rw_rdlock(&a->lock);
                r = a->rpz;
-               if(!r->disabled && (!r->taglist || taglist_intersect(r->taglist,
-                       r->taglistlen, taglist, taglen))) {
-                       z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len,
-                               qinfo->qclass, 0, 0, 0);
-                       if(z && r->action_override == RPZ_DISABLED_ACTION) {
-                               if(r->log)
-                                       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;
++              if(r->disabled) {
++                      lock_rw_unlock(&a->lock);
++                      continue;
++              }
+               if(r->taglist && !taglist_intersect(r->taglist,
+                                       r->taglistlen, taglist, taglen)) {
+                       lock_rw_unlock(&a->lock);
+                       continue;
+               }
+               z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len,
+                       qinfo->qclass, 0, 0, 0);
+               node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen);
+               if(z && r->action_override == RPZ_DISABLED_ACTION) {
+                       if(r->log)
+                               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;
+               }
+               if(z) {
+                       break;
+               } else {
+                       if(node != NULL) {
+                               lock_rw_unlock(&node->lock);
+                               node = NULL;
                        }
-                       if(z)
-                               break;
                }
-               lock_rw_unlock(&a->lock); /* not found in this auth_zone */
+               /* not found in this auth_zone */
+               lock_rw_unlock(&a->lock);
        }
        lock_rw_unlock(&az->rpz_lock);
-       if(!z)
-               return 0; /* not holding auth_zone.lock anymore */
  
-       log_assert(r);
-       if(r->action_override == RPZ_NO_OVERRIDE_ACTION)
-               lzt = z->type;
-       else
-               lzt = rpz_action_to_localzone_type(r->action_override);
+       *r_out = r;
+       *a_out = a;
+       *z_out = z;
+       return node;
+ }
+ static inline int
+ rpz_is_udp_query(struct comm_reply* repinfo) {
+       return repinfo != NULL
+                       ? (repinfo->c != NULL
+                               ? repinfo->c->type == comm_udp
+                               : 0)
+                       : 0;
+ }
+ /** encode answer consisting of 1 rrset */
+ static int
+ rpz_local_encode(struct query_info* qinfo,struct edns_data* edns, sldns_buffer* buf,
+       struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, int rcode)
+ {
+       struct reply_info rep;
+       uint16_t udpsize;
+       memset(&rep, 0, sizeof(rep));
+       rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode);
+       rep.qdcount = 1;
+       rep.rrset_count = ansec;
+       if(ansec > 0) {
+               rep.an_numrrsets = 1;
+               rep.rrsets = &rrset;
+               rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0];
+       }
+       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(!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 struct local_rrset*
+ 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(qtype) == packed_rrset->type) {
+                       return cursor;
+               }
+               cursor = cursor->next;
+       }
+       return NULL;
+ }
+ static void
+ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr,
+       struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
+       struct regional* temp)
+ {
+       struct local_rrset* rrset;
+       enum rpz_action action = RPZ_INVALID_ACTION;
+       struct ub_packed_rrset_key* rp = NULL;
+       int rcode = LDNS_RCODE_NOERROR|BIT_AA;
+       int rrset_count = 1;
+       verbose(VERB_ALGO, "rpz: apply client ip trigger: found=%d action=%s",
+               raddr != NULL, rpz_action_to_string(action));
+       /* prepare synthesized answer for client */
+       action = raddr->action;
+       if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) {
+               verbose(VERB_ALGO, "rpz: bug: local-data action but no local data");
+               return;
+       }
+       /* check query type / rr type */
+       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;
+               goto nodata;
+       }
+       rp = respip_copy_rrset(rrset->rrset, temp);
+       if(!rp) {
+               verbose(VERB_ALGO, "rpz: local data action: out of memory");
+               return;
+       }
+       rp->rk.flags |= PACKED_RRSET_FIXEDTTL;
+       rp->rk.dname = qinfo->qname;
+       rp->rk.dname_len = qinfo->qname_len;
+ nodata:
+       rpz_local_encode(qinfo, edns, buf, temp, rp, rrset_count, rcode);
+ }
+ static inline struct dns_msg*
+ rpz_dns_msg_new(struct regional* region)
+ {
+       struct dns_msg* msg =
+                       (struct dns_msg*)regional_alloc(region,
+                                                       sizeof(struct dns_msg));
+       if(msg == NULL) { return NULL; }
+       memset(msg, 0, sizeof(struct dns_msg));
+       return msg;
+ }
+ static inline struct dns_msg*
+ rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms)
+ {
+       struct dns_msg* msg = rpz_dns_msg_new(ms->region);
+       if(msg == NULL) { return msg; }
+       msg->qinfo = ms->qinfo;
+       msg->rep = construct_reply_info_base(ms->region,
+                                            LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+                                            1, //qd
+                                            0, //ttl
+                                            0, //prettl
+                                            0, //expttl
+                                            0, //an
+                                            0, //ns
+                                            0, //ar
+                                            0, //total
+                                            sec_status_secure);
+       return msg;
+ }
+ static inline struct dns_msg*
+ rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms)
+ {
+       struct dns_msg* msg = rpz_dns_msg_new(ms->region);
+       if(msg == NULL) { return msg; }
+       msg->qinfo = ms->qinfo;
+       msg->rep = construct_reply_info_base(ms->region,
+                                            LDNS_RCODE_NXDOMAIN | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+                                            1, //qd
+                                            0, //ttl
+                                            0, //prettl
+                                            0, //expttl
+                                            0, //an
+                                            0, //ns
+                                            0, //ar
+                                            0, //total
+                                            sec_status_secure);
+       return msg;
+ }
+ static inline struct dns_msg*
+ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
+       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;
+       msg = rpz_dns_msg_new(ms->region);
+       if(msg == NULL) { return NULL; }
+       // XXX: use ttl etc from rpz zone?
+         new_reply_info = construct_reply_info_base(ms->region,
+                                                    LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+                                                    1, //qd
+                                                    0, //ttl
+                                                    0, //prettl
+                                                    0, //expttl
+                                                    1, //an
+                                                    0, //ns
+                                                    0, //ar
+                                                    1, //total
+                                                    sec_status_secure);
+       if(new_reply_info == NULL) {
+               log_err("out of memory");
+               return NULL;
+       }
+       rp = respip_copy_rrset(rrset->rrset, ms->region);
+       if(rp == NULL) {
+               log_err("out of memory");
+               return NULL;
+       }
+       rp->rk.dname = qi->qname;
+       rp->rk.dname_len = qi->qname_len;
+       new_reply_info->rrsets[0] = rp;
+       msg->rep = new_reply_info;
+       return msg;
+ }
+ static inline struct dns_msg*
+ rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms,
+       struct clientip_synthesized_rr* data)
+ {
+       struct query_info* qi = &ms->qinfo;
+       struct local_rrset* rrset;
+       rrset = rpz_find_synthesized_rrset(qi->qtype, data);
+       if(rrset == NULL) {
+               verbose(VERB_ALGO, "rpz: nsip: no matching local data found");
+               return NULL;
+       }
+       return rpz_synthesize_localdata_from_rrset(r, ms, rrset);
+ }
+ // copy'n'paste from localzone.c
+ static struct local_rrset*
+ local_data_find_type(struct local_data* data, uint16_t type, int alias_ok)
+ {
+       struct local_rrset* p;
+       type = htons(type);
+       for(p = data->rrsets; p; p = p->next) {
+               verbose(VERB_ALGO, "type=%d (%d)", type, p->rrset->rk.type);
+               if(p->rrset->rk.type == type)
+                       return p;
+               if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME))
+                       return p;
+       }
+       return NULL;
+ }
+ // based on localzone.c:local_data_answer()
+ static inline struct dns_msg*
+ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms,
+       struct local_zone* z, struct matched_delegation_point const* match)
+ {
+       struct local_data key;
+       struct local_data* ld;
+       struct local_rrset* rrset;
+       if(match->dname == NULL) { return NULL; }
+       key.node.key = &key;
+       key.name = match->dname;
+       key.namelen = match->dname_len;
+       key.namelabs = dname_count_labels(match->dname);
+       rpz_log_dname("nsdname local data", key.name, key.namelen);
+       ld = (struct local_data*)rbtree_search(&z->data, &key.node);
+       if(ld == NULL) {
+               verbose(VERB_ALGO, "rpz: nsdname: impossible: qname not found");
+               return NULL;
+       }
+       rrset = local_data_find_type(ld, ms->qinfo.qtype, 1);
+       if(rrset == NULL) {
+               verbose(VERB_ALGO, "rpz: nsdname: no matching local data found");
+               return NULL;
+       }
  
+       return rpz_synthesize_localdata_from_rrset(r, ms, rrset);
+ }
+ static int
+ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r,
+       struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo,
+       struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
+       struct comm_reply* repinfo, struct ub_server_stats* stats)
+ {
+       struct local_data* ld = NULL;
+       int ret = 0;
        if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
-               qinfo->local_alias =
-                       regional_alloc_zero(temp, sizeof(struct local_rrset));
-               if(!qinfo->local_alias) {
-                       lock_rw_unlock(&z->lock);
-                       lock_rw_unlock(&a->lock);
+               qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset));
+               if(qinfo->local_alias == NULL) {
                        return 0; /* out of memory */
                }
-               qinfo->local_alias->rrset =
-                       regional_alloc_init(temp, r->cname_override,
-                               sizeof(*r->cname_override));
-               if(!qinfo->local_alias->rrset) {
-                       lock_rw_unlock(&z->lock);
-                       lock_rw_unlock(&a->lock);
+               qinfo->local_alias->rrset = regional_alloc_init(temp, r->cname_override,
+                                                               sizeof(*r->cname_override));
+               if(qinfo->local_alias->rrset == NULL) {
                        return 0; /* out of memory */
                }
                qinfo->local_alias->rrset->rk.dname = qinfo->qname;
diff --cc services/rpz.h
Simple merge
Simple merge
Simple merge