diff options
Diffstat (limited to 'iterator/iterator.c')
| -rw-r--r-- | iterator/iterator.c | 259 | 
1 files changed, 163 insertions, 96 deletions
diff --git a/iterator/iterator.c b/iterator/iterator.c index 33095b2b5c45..1548dfcaee62 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -255,7 +255,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)  				log_err("out of memory adding missing");  		}  		delegpt_mark_neg(dpns, qstate->qinfo.qtype); -		if((dpns->got4 == 2 || !ie->supports_ipv4) && +		if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&  			(dpns->got6 == 2 || !ie->supports_ipv6)) {  			dpns->resolved = 1; /* mark as failed */  			target_count_increase_nx(super_iq, 1); @@ -302,81 +302,65 @@ error_response(struct module_qstate* qstate, int id, int rcode)  static int  error_response_cache(struct module_qstate* qstate, int id, int rcode)  { -	if(!qstate->no_cache_store) { -		/* store in cache */ -		struct reply_info err; -		if(qstate->prefetch_leeway > NORR_TTL) { -			verbose(VERB_ALGO, "error response for prefetch in cache"); -			/* attempt to adjust the cache entry prefetch */ -			if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo, -				NORR_TTL, qstate->query_flags)) -				return error_response(qstate, id, rcode); -			/* if that fails (not in cache), fall through to store err */ +	struct reply_info err; +	struct msgreply_entry* msg; +	if(qstate->no_cache_store) { +		return error_response(qstate, id, rcode); +	} +	if(qstate->prefetch_leeway > NORR_TTL) { +		verbose(VERB_ALGO, "error response for prefetch in cache"); +		/* attempt to adjust the cache entry prefetch */ +		if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo, +			NORR_TTL, qstate->query_flags)) +			return error_response(qstate, id, rcode); +		/* if that fails (not in cache), fall through to store err */ +	} +	if((msg=msg_cache_lookup(qstate->env, +		qstate->qinfo.qname, qstate->qinfo.qname_len, +		qstate->qinfo.qtype, qstate->qinfo.qclass, +		qstate->query_flags, 0, +		qstate->env->cfg->serve_expired_ttl_reset)) != NULL) { +		struct reply_info* rep = (struct reply_info*)msg->entry.data; +		if(qstate->env->cfg->serve_expired && +			qstate->env->cfg->serve_expired_ttl_reset && rep && +			*qstate->env->now + qstate->env->cfg->serve_expired_ttl +			> rep->serve_expired_ttl) { +			verbose(VERB_ALGO, "reset serve-expired-ttl for " +				"response in cache"); +			rep->serve_expired_ttl = *qstate->env->now + +				qstate->env->cfg->serve_expired_ttl;  		} -		if(qstate->env->cfg->serve_expired) { -			/* if serving expired contents, and such content is -			 * already available, don't overwrite this servfail */ -			struct msgreply_entry* msg; -			if((msg=msg_cache_lookup(qstate->env, -				qstate->qinfo.qname, qstate->qinfo.qname_len, -				qstate->qinfo.qtype, qstate->qinfo.qclass, -				qstate->query_flags, 0, -				qstate->env->cfg->serve_expired_ttl_reset)) -				!= NULL) { -				if(qstate->env->cfg->serve_expired_ttl_reset) { -					struct reply_info* rep = -						(struct reply_info*)msg->entry.data; -					if(rep && *qstate->env->now + -						qstate->env->cfg->serve_expired_ttl  > -						rep->serve_expired_ttl) { -						rep->serve_expired_ttl = -							*qstate->env->now + -							qstate->env->cfg->serve_expired_ttl; -					} -				} -				lock_rw_unlock(&msg->entry.lock); -				return error_response(qstate, id, rcode); -			} -			/* serving expired contents, but nothing is cached -			 * at all, so the servfail cache entry is useful -			 * (stops waste of time on this servfail NORR_TTL) */ -		} else { -			/* don't overwrite existing (non-expired) data in -			 * cache with a servfail */ -			struct msgreply_entry* msg; -			if((msg=msg_cache_lookup(qstate->env, -				qstate->qinfo.qname, qstate->qinfo.qname_len, -				qstate->qinfo.qtype, qstate->qinfo.qclass, -				qstate->query_flags, *qstate->env->now, 0)) -				!= NULL) { -				struct reply_info* rep = (struct reply_info*) -					msg->entry.data; -				if(FLAGS_GET_RCODE(rep->flags) == -					LDNS_RCODE_NOERROR || -					FLAGS_GET_RCODE(rep->flags) == -					LDNS_RCODE_NXDOMAIN) { -					/* we have a good entry, -					 * don't overwrite */ -					lock_rw_unlock(&msg->entry.lock); -					return error_response(qstate, id, rcode); -				} -				lock_rw_unlock(&msg->entry.lock); -			} -			 +		if(rep && (FLAGS_GET_RCODE(rep->flags) == +			LDNS_RCODE_NOERROR || +			FLAGS_GET_RCODE(rep->flags) == +			LDNS_RCODE_NXDOMAIN || +			FLAGS_GET_RCODE(rep->flags) == +			LDNS_RCODE_YXDOMAIN) && +			(qstate->env->cfg->serve_expired || +			*qstate->env->now <= rep->ttl)) { +			/* we have a good entry, don't overwrite */ +			lock_rw_unlock(&msg->entry.lock); +			return error_response(qstate, id, rcode);  		} -		memset(&err, 0, sizeof(err)); -		err.flags = (uint16_t)(BIT_QR | BIT_RA); -		FLAGS_SET_RCODE(err.flags, rcode); -		err.qdcount = 1; -		err.ttl = NORR_TTL; -		err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl); -		err.serve_expired_ttl = NORR_TTL; -		/* do not waste time trying to validate this servfail */ -		err.security = sec_status_indeterminate; -		verbose(VERB_ALGO, "store error response in message cache"); -		iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL, -			qstate->query_flags, qstate->qstarttime); -	} +		lock_rw_unlock(&msg->entry.lock); +		/* nothing interesting is cached (already error response or +		 * expired good record when we don't serve expired), so this +		 * servfail cache entry is useful (stops waste of time on this +		 * servfail NORR_TTL) */ +	} +	/* store in cache */ +	memset(&err, 0, sizeof(err)); +	err.flags = (uint16_t)(BIT_QR | BIT_RA); +	FLAGS_SET_RCODE(err.flags, rcode); +	err.qdcount = 1; +	err.ttl = NORR_TTL; +	err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl); +	err.serve_expired_ttl = NORR_TTL; +	/* do not waste time trying to validate this servfail */ +	err.security = sec_status_indeterminate; +	verbose(VERB_ALGO, "store error response in message cache"); +	iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL, +		qstate->query_flags, qstate->qstarttime);  	return error_response(qstate, id, rcode);  } @@ -590,6 +574,54 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,  	return 1;  } +/** fill fail address for later recovery */ +static void +fill_fail_addr(struct iter_qstate* iq, struct sockaddr_storage* addr, +	socklen_t addrlen) +{ +	if(addrlen == 0) { +		iq->fail_addr_type = 0; +		return; +	} +	if(((struct sockaddr_in*)addr)->sin_family == AF_INET) { +		iq->fail_addr_type = 4; +		memcpy(&iq->fail_addr.in, +			&((struct sockaddr_in*)addr)->sin_addr, +			sizeof(iq->fail_addr.in)); +	} +#ifdef AF_INET6 +	else if(((struct sockaddr_in*)addr)->sin_family == AF_INET6) { +		iq->fail_addr_type = 6; +		memcpy(&iq->fail_addr.in6, +			&((struct sockaddr_in6*)addr)->sin6_addr, +			sizeof(iq->fail_addr.in6)); +	} +#endif +	else { +		iq->fail_addr_type = 0; +	} +} + +/** print fail addr to string */ +static void +print_fail_addr(struct iter_qstate* iq, char* buf, size_t len) +{ +	if(iq->fail_addr_type == 4) { +		if(inet_ntop(AF_INET, &iq->fail_addr.in, buf, +			(socklen_t)len) == 0) +			(void)strlcpy(buf, "(inet_ntop error)", len); +	} +#ifdef AF_INET6 +	else if(iq->fail_addr_type == 6) { +		if(inet_ntop(AF_INET6, &iq->fail_addr.in6, buf, +			(socklen_t)len) == 0) +			(void)strlcpy(buf, "(inet_ntop error)", len); +	} +#endif +	else +		(void)strlcpy(buf, "", len); +} +  /** add response specific error information for log servfail */  static void  errinf_reply(struct module_qstate* qstate, struct iter_qstate* iq) @@ -597,16 +629,14 @@ errinf_reply(struct module_qstate* qstate, struct iter_qstate* iq)  	if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail)  		return;  	if((qstate->reply && qstate->reply->remote_addrlen != 0) || -		(iq->fail_reply && iq->fail_reply->remote_addrlen != 0)) { +		(iq->fail_addr_type != 0)) {  		char from[256], frm[512];  		if(qstate->reply && qstate->reply->remote_addrlen != 0)  			addr_to_str(&qstate->reply->remote_addr,  				qstate->reply->remote_addrlen, from,  				sizeof(from));  		else -			addr_to_str(&iq->fail_reply->remote_addr, -				iq->fail_reply->remote_addrlen, from, -				sizeof(from)); +			print_fail_addr(iq, from, sizeof(from));  		snprintf(frm, sizeof(frm), "from %s", from);  		errinf(qstate, frm);  	} @@ -1137,7 +1167,7 @@ generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq,   * Generate a NS check request to obtain authoritative information   * on an NS rrset.   * - * @param qstate: the qtstate that triggered the need to prime. + * @param qstate: the qstate that triggered the need to prime.   * @param iq: iterator query state.   * @param id: module id.   */ @@ -1451,6 +1481,19 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,  			errinf(qstate, "malloc failure for forward zone");  			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);  		} +		if((qstate->query_flags&BIT_RD)==0) { +			/* If the server accepts RD=0 queries and forwards +			 * with RD=1, then if the server is listed as an NS +			 * entry, it starts query loops. Stop that loop by +			 * disallowing the query. The RD=0 was previously used +			 * to check the cache with allow_snoop. For stubs, +			 * the iterator pass would have primed the stub and +			 * then cached information can be used for further +			 * queries. */ +			verbose(VERB_ALGO, "cannot forward RD=0 query, to stop query loops"); +			errinf(qstate, "cannot forward RD=0 query"); +			return error_response(qstate, id, LDNS_RCODE_SERVFAIL); +		}  		iq->refetch_glue = 0;  		iq->minimisation_state = DONOT_MINIMISE_STATE;  		/* the request has been forwarded. @@ -1560,18 +1603,19 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,  		/* see if this dp not useless.  		 * It is useless if: -		 *	o all NS items are required glue.  +		 *	o all NS items are required glue.  		 *	  or the query is for NS item that is required glue.  		 *	o no addresses are provided.  		 *	o RD qflag is on.  		 * Instead, go up one level, and try to get even further -		 * If the root was useless, use safety belt information.  +		 * If the root was useless, use safety belt information.  		 * Only check cache returns, because replies for servers  		 * could be useless but lead to loops (bumping into the  		 * same server reply) if useless-checked.  		 */ -		if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,  -			iq->dp, ie->supports_ipv4, ie->supports_ipv6)) { +		if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, +			iq->dp, ie->supports_ipv4, ie->supports_ipv6, +			ie->use_nat64)) {  			struct delegpt* retdp = NULL;  			if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &retdp)) {  				if(retdp) { @@ -1932,7 +1976,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,  				break;  		}  		/* Send the A request. */ -		if(ie->supports_ipv4 && +		if((ie->supports_ipv4 || ie->use_nat64) &&  			((ns->lame && !ns->done_pside4) ||  			(!ns->lame && !ns->got4))) {  			if(!generate_target_query(qstate, iq, id,  @@ -2085,14 +2129,14 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,  		/* if this nameserver is at a delegation point, but that  		 * delegation point is a stub and we cannot go higher, skip*/  		if( ((ie->supports_ipv6 && !ns->done_pside6) || -		    (ie->supports_ipv4 && !ns->done_pside4)) && +		    ((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) &&  		    !can_have_last_resort(qstate->env, ns->name, ns->namelen,  			iq->qchase.qclass, NULL)) {  			log_nametypeclass(VERB_ALGO, "cannot pside lookup ns "  				"because it is also a stub/forward,",  				ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);  			if(ie->supports_ipv6) ns->done_pside6 = 1; -			if(ie->supports_ipv4) ns->done_pside4 = 1; +			if(ie->supports_ipv4 || ie->use_nat64) ns->done_pside4 = 1;  			continue;  		}  		/* query for parent-side A and AAAA for nameservers */ @@ -2117,7 +2161,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,  				return 0;  			}  		} -		if(ie->supports_ipv4 && !ns->done_pside4) { +		if((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4) {  			/* Send the A request. */  			if(!generate_parentside_target_query(qstate, iq, id,   				ns->name, ns->namelen,  @@ -2259,6 +2303,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,  	int tf_policy;  	struct delegpt_addr* target;  	struct outbound_entry* outq; +	struct sockaddr_storage real_addr; +	socklen_t real_addrlen;  	int auth_fallback = 0;  	uint8_t* qout_orig = NULL;  	size_t qout_orig_len = 0; @@ -2384,7 +2430,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,  	}  	if(!ie->supports_ipv6)  		delegpt_no_ipv6(iq->dp); -	if(!ie->supports_ipv4) +	if(!ie->supports_ipv4 && !ie->use_nat64)  		delegpt_no_ipv4(iq->dp);  	delegpt_log(VERB_ALGO, iq->dp); @@ -2805,12 +2851,24 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,  	/* We have a valid target. */  	if(verbosity >= VERB_QUERY) {  		log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out); -		log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name,  +		log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name,  			&target->addr, target->addrlen);  		verbose(VERB_ALGO, "dnssec status: %s%s",  			iq->dnssec_expected?"expected": "not expected",  			iq->dnssec_lame_query?" but lame_query anyway": "");  	} + +	real_addr = target->addr; +	real_addrlen = target->addrlen; + +	if(ie->use_nat64 && target->addr.ss_family == AF_INET) { +		addr_to_nat64(&target->addr, &ie->nat64_prefix_addr, +			ie->nat64_prefix_addrlen, ie->nat64_prefix_net, +			&real_addr, &real_addrlen); +		log_name_addr(VERB_QUERY, "applied NAT64:", +			iq->dp->name, &real_addr, real_addrlen); +	} +  	fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));  	outq = (*qstate->env->send_query)(&iq->qinfo_out,  		iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), @@ -2821,7 +2879,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,  		!qstate->blacklist&&(!iter_qname_indicates_dnssec(qstate->env,  		&iq->qinfo_out)||target->attempts==1)?0:BIT_CD),  		iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted( -		ie, iq), sq_check_ratelimit, &target->addr, target->addrlen, +		ie, iq), sq_check_ratelimit, &real_addr, real_addrlen,  		iq->dp->name, iq->dp->namelen,  		(iq->dp->tcp_upstream || qstate->env->cfg->tcp_upstream),  		(iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream), @@ -2838,7 +2896,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,  			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);  		}  		log_addr(VERB_QUERY, "error sending query to auth server", -			&target->addr, target->addrlen); +			&real_addr, real_addrlen);  		if(qstate->env->cfg->qname_minimisation)  			iq->minimisation_state = SKIP_MINIMISE_STATE;  		return next_state(iq, QUERYTARGETS_STATE); @@ -2882,7 +2940,7 @@ static int  processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,  	struct iter_env* ie, int id)  { -	int dnsseclame = 0; +	int dnsseclame = 0, origtypecname = 0;  	enum response_type type;  	iq->num_current_queries--; @@ -2965,6 +3023,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,  		/* YXDOMAIN is a permanent error, no need to retry */  		type = RESPONSE_TYPE_ANSWER;  	} +	if(type == RESPONSE_TYPE_CNAME) +		origtypecname = 1;  	if(type == RESPONSE_TYPE_CNAME && iq->response->rep->an_numrrsets >= 1  		&& ntohs(iq->response->rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_DNAME) {  		uint8_t* sname = NULL; @@ -3050,11 +3110,14 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,  				iq->minimisation_state = DONOT_MINIMISE_STATE;  			}  			if(FLAGS_GET_RCODE(iq->response->rep->flags) == -				LDNS_RCODE_NXDOMAIN) { +				LDNS_RCODE_NXDOMAIN && !origtypecname) {  				/* Stop resolving when NXDOMAIN is DNSSEC  				 * signed. Based on assumption that nameservers  				 * serving signed zones do not return NXDOMAIN  				 * for empty-non-terminals. */ +				/* If this response is actually a CNAME type, +				 * the nxdomain rcode may not be for the qname, +				 * and so it is not the final response. */  				if(iq->dnssec_expected)  					return final_state(iq);  				/* Make subrequest to validate intermediate @@ -3182,7 +3245,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,  		(*qstate->env->detach_subs)(qstate);  		iq->num_target_queries = 0;  		iq->response = NULL; -		iq->fail_reply = NULL; +		iq->fail_addr_type = 0;  		verbose(VERB_ALGO, "cleared outbound list for next round");  		return next_state(iq, QUERYTARGETS_STATE);  	} else if(type == RESPONSE_TYPE_CNAME) { @@ -3564,7 +3627,7 @@ processTargetResponse(struct module_qstate* qstate, int id,  	} else {  		verbose(VERB_ALGO, "iterator TargetResponse failed");  		delegpt_mark_neg(dpns, qstate->qinfo.qtype); -		if((dpns->got4 == 2 || !ie->supports_ipv4) && +		if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&  			(dpns->got6 == 2 || !ie->supports_ipv6)) {  			dpns->resolved = 1; /* fail the target */  			/* do not count cached answers */ @@ -3809,6 +3872,9 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,  	/* make sure QR flag is on */  	iq->response->rep->flags |= BIT_QR; +	/* explicitly set the EDE string to NULL */ +	iq->response->rep->reason_bogus_str = NULL; +  	/* we have finished processing this query */  	qstate->ext_state[id] = module_finished; @@ -3987,7 +4053,8 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,  	}  	/* parse message */ -	iq->fail_reply = qstate->reply; +	fill_fail_addr(iq, &qstate->reply->remote_addr, +		qstate->reply->remote_addrlen);  	prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,   		sizeof(struct msg_parse));  	if(!prs) {  | 
