Beispiel #1
0
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;
}
Beispiel #2
0
/**
 * 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;
}
Beispiel #3
0
/**
 * 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;
}