diff --git a/respip/respip.c b/respip/respip.c index 75400bb4d..40f839645 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -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; irrsets[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. diff --git a/services/rpz.c b/services/rpz.c index 9b1d23884..eae3feeeb 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -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; diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index 1d4462df0..ac9e80b80 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -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 diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 927bf09a2..fed6e5345 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -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; irrsets[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) diff --git a/util/data/msgreply.h b/util/data/msgreply.h index 385780268..64ff4dfbe 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -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