From: W.C.A. Wijngaards Date: Thu, 14 Jan 2021 11:11:29 +0000 (+0100) Subject: Merge branch 'rpz' of https://github.com/magenbluten/unbound into magenbluten-rpz X-Git-Tag: release-1.14.0rc1~62^2~53^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cdb60adcdce937fe92084b2093a7fb7d9cb19c0c;p=thirdparty%2Funbound.git Merge branch 'rpz' of https://github.com/magenbluten/unbound into magenbluten-rpz Conflict fixed for rpz.disabled check added. --- cdb60adcdce937fe92084b2093a7fb7d9cb19c0c diff --cc services/localzone.c index ed0d2c565,5e2104a7c..2458842aa --- a/services/localzone.c +++ b/services/localzone.c @@@ -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; diff --cc services/localzone.h index 492629936,39517c36a..de2a7d9a3 --- a/services/localzone.h +++ b/services/localzone.h @@@ -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/rpz.c index d7dd17f7e,777007b42..0d825238f --- a/services/rpz.c +++ b/services/rpz.c @@@ -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;