int respip_merge_cname(struct reply_info* base_rep, const struct query_info* qinfo, const struct reply_info* tgt_rep, const struct respip_client_info* cinfo, int must_validate, struct reply_info** new_repp, struct regional* region) { struct reply_info* new_rep; struct reply_info* tmp_rep = NULL; /* just a placeholder */ struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */ uint16_t tgt_rcode; size_t i, j; struct respip_action_info actinfo = {respip_none, NULL}; /* If the query for the CNAME target would result in an unusual rcode, * we generally translate it as a failure for the base query * (which would then be translated into SERVFAIL). The only exception * is NXDOMAIN and YXDOMAIN, which are passed to the end client(s). * The YXDOMAIN case would be rare but still possible (when * DNSSEC-validated DNAME has been cached but synthesizing CNAME * can't be generated due to length limitation) */ tgt_rcode = FLAGS_GET_RCODE(tgt_rep->flags); if((tgt_rcode != LDNS_RCODE_NOERROR && tgt_rcode != LDNS_RCODE_NXDOMAIN && tgt_rcode != LDNS_RCODE_YXDOMAIN) || (must_validate && tgt_rep->security <= sec_status_bogus)) { return 0; } /* see if the target reply would be subject to a response-ip action. */ if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo, &alias_rrset, 1, region)) return 0; if(actinfo.action != respip_none) { log_info("CNAME target of redirect response-ip action would " "be subject to response-ip action, too; stripped"); *new_repp = base_rep; return 1; } /* Append target reply to the base. Since we cannot assume * tgt_rep->rrsets is valid throughout the lifetime of new_rep * or it can be safely shared by multiple threads, we need to make a * deep copy. */ new_rep = make_new_reply_info(base_rep, region, base_rep->an_numrrsets + tgt_rep->an_numrrsets, base_rep->an_numrrsets); 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); if(!new_rep->rrsets[j]) return 0; } FLAGS_SET_RCODE(new_rep->flags, tgt_rcode); *new_repp = new_rep; return 1; }
/** * apply response ip action in case where no action data is provided. * this is similar to localzone.c:lz_zone_answer() but simplified due to * the characteristics of response ip: * - 'deny' variants will be handled at the caller side * - no specific processing for 'transparent' variants: unlike local zones, * there is no such a case of 'no data but name existing'. so all variants * just mean 'transparent if no data'. * @param qtype: query type * @param action: found action * @param rep: * @param new_repp * @param rrset_id * @param region: region for building new reply * @return 1 on success, 0 on error. */ static int respip_nodata_answer(uint16_t qtype, enum respip_action action, const struct reply_info *rep, size_t rrset_id, struct reply_info** new_repp, struct regional* region) { struct reply_info* new_rep; if(action == respip_refuse || action == respip_always_refuse) { new_rep = make_new_reply_info(rep, region, 0, 0); if(!new_rep) return 0; FLAGS_SET_RCODE(new_rep->flags, LDNS_RCODE_REFUSED); *new_repp = new_rep; return 1; } else if(action == respip_static || action == respip_redirect || action == respip_always_nxdomain || action == respip_inform_redirect) { /* Since we don't know about other types of the owner name, * we generally return NOERROR/NODATA unless an NXDOMAIN action * is explicitly specified. */ int rcode = (action == respip_always_nxdomain)? LDNS_RCODE_NXDOMAIN:LDNS_RCODE_NOERROR; /* We should empty the answer section except for any preceding * CNAMEs (in that case rrset_id > 0). Type-ANY case is * special as noted in respip_data_answer(). */ if(qtype == LDNS_RR_TYPE_ANY) rrset_id = 0; new_rep = make_new_reply_info(rep, region, rrset_id, rrset_id); if(!new_rep) return 0; FLAGS_SET_RCODE(new_rep->flags, rcode); *new_repp = new_rep; return 1; } return 1; }
/** * See if response-ip or tag data should override the original answer rrset * (which is rep->rrsets[rrset_id]) and if so override it. * This is (mostly) equivalent to localzone.c:local_data_answer() but for * response-ip actions. * Note that this function distinguishes error conditions from "success but * not overridden". This is because we want to avoid accidentally applying * the "no data" action in case of error. * @param raddr: address span that requires an action * @param action: action to apply * @param qtype: original query type * @param rep: original reply message * @param rrset_id: the rrset ID in 'rep' to which the action should apply * @param new_repp: see respip_rewrite_reply * @param tag: if >= 0 the tag ID used to determine the action and data * @param tag_datas: data corresponding to 'tag'. * @param tag_datas_size: size of 'tag_datas' * @param tagname: array of tag names, used for logging * @param num_tags: size of 'tagname', used for logging * @param redirect_rrsetp: ptr to redirect record * @param region: region for building new reply * @return 1 if overridden, 0 if not overridden, -1 on error. */ static int respip_data_answer(const struct resp_addr* raddr, enum respip_action action, uint16_t qtype, const struct reply_info* rep, size_t rrset_id, struct reply_info** new_repp, int tag, struct config_strlist** tag_datas, size_t tag_datas_size, char* const* tagname, int num_tags, struct ub_packed_rrset_key** redirect_rrsetp, struct regional* region) { struct ub_packed_rrset_key* rp = raddr->data; struct reply_info* new_rep; *redirect_rrsetp = NULL; if(action == respip_redirect && tag != -1 && (size_t)tag<tag_datas_size && tag_datas[tag]) { struct query_info dataqinfo; struct ub_packed_rrset_key r; /* Extract parameters of the original answer rrset that can be * rewritten below, in the form of query_info. Note that these * can be different from the info of the original query if the * rrset is a CNAME target.*/ memset(&dataqinfo, 0, sizeof(dataqinfo)); dataqinfo.qname = rep->rrsets[rrset_id]->rk.dname; dataqinfo.qname_len = rep->rrsets[rrset_id]->rk.dname_len; dataqinfo.qtype = ntohs(rep->rrsets[rrset_id]->rk.type); dataqinfo.qclass = ntohs(rep->rrsets[rrset_id]->rk.rrset_class); memset(&r, 0, sizeof(r)); if(local_data_find_tag_datas(&dataqinfo, tag_datas[tag], &r, region)) { verbose(VERB_ALGO, "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); if(!rp) return -1; } } if(!rp) return 0; /* If we are using response-ip-data, we need to make a copy of rrset * to replace the rrset's dname. Note that, unlike local data, we * rename the dname for other actions than redirect. This is because * response-ip-data isn't associated to any specific name. */ if(rp == raddr->data) { rp = copy_rrset(rp, region); if(!rp) return -1; rp->rk.dname = rep->rrsets[rrset_id]->rk.dname; rp->rk.dname_len = rep->rrsets[rrset_id]->rk.dname_len; } /* Build a new reply with redirect rrset. We keep any preceding CNAMEs * and replace the address rrset that triggers the action. If it's * type ANY query, however, no other answer records should be kept * (note that it can't be a CNAME chain in this case due to * sanitizing). */ if(qtype == LDNS_RR_TYPE_ANY) rrset_id = 0; new_rep = make_new_reply_info(rep, region, rrset_id + 1, rrset_id); if(!new_rep) return -1; rp->rk.flags |= PACKED_RRSET_FIXEDTTL; /* avoid adjusting TTL */ new_rep->rrsets[rrset_id] = rp; *redirect_rrsetp = rp; *new_repp = new_rep; return 1; }