1238106Sdes/*
2238106Sdes * iterator/iter_resptype.c - response type information and classification.
3238106Sdes *
4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved.
5238106Sdes *
6238106Sdes * This software is open source.
7238106Sdes *
8238106Sdes * Redistribution and use in source and binary forms, with or without
9238106Sdes * modification, are permitted provided that the following conditions
10238106Sdes * are met:
11238106Sdes *
12238106Sdes * Redistributions of source code must retain the above copyright notice,
13238106Sdes * this list of conditions and the following disclaimer.
14238106Sdes *
15238106Sdes * Redistributions in binary form must reproduce the above copyright notice,
16238106Sdes * this list of conditions and the following disclaimer in the documentation
17238106Sdes * and/or other materials provided with the distribution.
18238106Sdes *
19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may
20238106Sdes * be used to endorse or promote products derived from this software without
21238106Sdes * specific prior written permission.
22238106Sdes *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24238106Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25238106Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26238106Sdes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27238106Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28238106Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31238106Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33238106Sdes * POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes *
39238106Sdes * This file defines the response type. DNS Responses can be classified as
40238106Sdes * one of the response types.
41238106Sdes */
42238106Sdes#include "config.h"
43238106Sdes#include <ldns/packet.h>
44238106Sdes#include "iterator/iter_resptype.h"
45238106Sdes#include "iterator/iter_delegpt.h"
46238106Sdes#include "services/cache/dns.h"
47238106Sdes#include "util/net_help.h"
48238106Sdes#include "util/data/dname.h"
49238106Sdes
50238106Sdesenum response_type
51238106Sdesresponse_type_from_cache(struct dns_msg* msg,
52238106Sdes	struct query_info* request)
53238106Sdes{
54238106Sdes	/* If the message is NXDOMAIN, then it is an ANSWER. */
55238106Sdes	if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN)
56238106Sdes		return RESPONSE_TYPE_ANSWER;
57238106Sdes	if(request->qtype == LDNS_RR_TYPE_ANY)
58238106Sdes		return RESPONSE_TYPE_ANSWER;
59238106Sdes
60238106Sdes	/* First we look at the answer section. This can tell us if this is
61238106Sdes	 * CNAME or positive ANSWER. */
62238106Sdes	if(msg->rep->an_numrrsets > 0) {
63238106Sdes		/* Now look at the answer section first. 3 states:
64238106Sdes		 *	o our answer is there directly,
65238106Sdes		 *	o our answer is there after a cname,
66238106Sdes		 *	o or there is just a cname. */
67238106Sdes		uint8_t* mname = request->qname;
68238106Sdes		size_t mname_len = request->qname_len;
69238106Sdes		size_t i;
70238106Sdes		for(i=0; i<msg->rep->an_numrrsets; i++) {
71238106Sdes			struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
72238106Sdes
73238106Sdes			/* If we have encountered an answer (before or
74238106Sdes			 * after a CNAME), then we are done! Note that
75238106Sdes			 * if qtype == CNAME then this will be noted as
76238106Sdes			 * an ANSWER before it gets treated as a CNAME,
77238106Sdes			 * as it should */
78238106Sdes			if(ntohs(s->rk.type) == request->qtype &&
79238106Sdes				ntohs(s->rk.rrset_class) == request->qclass &&
80238106Sdes				query_dname_compare(mname, s->rk.dname) == 0) {
81238106Sdes				return RESPONSE_TYPE_ANSWER;
82238106Sdes			}
83238106Sdes
84238106Sdes			/* If we have encountered a CNAME, make sure that
85238106Sdes			 * it is relevant. */
86238106Sdes			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
87238106Sdes				query_dname_compare(mname, s->rk.dname) == 0) {
88238106Sdes				get_cname_target(s, &mname, &mname_len);
89238106Sdes			}
90238106Sdes		}
91238106Sdes
92238106Sdes		/* if we encountered a CNAME (or a bunch of CNAMEs), and
93238106Sdes		 * still got to here, then it is a CNAME response. (i.e.,
94238106Sdes		 * the CNAME chain didn't terminate in an answer rrset.) */
95238106Sdes		if(mname != request->qname) {
96238106Sdes			return RESPONSE_TYPE_CNAME;
97238106Sdes		}
98238106Sdes	}
99238106Sdes
100238106Sdes	/* At this point, since we don't need to detect REFERRAL or LAME
101238106Sdes	 * messages, it can only be an ANSWER. */
102238106Sdes	return RESPONSE_TYPE_ANSWER;
103238106Sdes}
104238106Sdes
105238106Sdesenum response_type
106238106Sdesresponse_type_from_server(int rdset,
107238106Sdes	struct dns_msg* msg, struct query_info* request, struct delegpt* dp)
108238106Sdes{
109238106Sdes	uint8_t* origzone = (uint8_t*)"\000"; /* the default */
110238106Sdes	struct ub_packed_rrset_key* s;
111238106Sdes	size_t i;
112238106Sdes
113238106Sdes	if(!msg || !request)
114238106Sdes		return RESPONSE_TYPE_THROWAWAY;
115238106Sdes
116238106Sdes	/* If the message is NXDOMAIN, then it answers the question. */
117238106Sdes	if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) {
118238106Sdes		/* make sure its not recursive when we don't want it to */
119238106Sdes		if( (msg->rep->flags&BIT_RA) &&
120238106Sdes			!(msg->rep->flags&BIT_AA) && !rdset)
121238106Sdes				return RESPONSE_TYPE_REC_LAME;
122238106Sdes		/* it could be a CNAME with NXDOMAIN rcode */
123238106Sdes		for(i=0; i<msg->rep->an_numrrsets; i++) {
124238106Sdes			s = msg->rep->rrsets[i];
125238106Sdes			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
126238106Sdes				query_dname_compare(request->qname,
127238106Sdes				s->rk.dname) == 0) {
128238106Sdes				return RESPONSE_TYPE_CNAME;
129238106Sdes			}
130238106Sdes		}
131238106Sdes		return RESPONSE_TYPE_ANSWER;
132238106Sdes	}
133238106Sdes
134238106Sdes	/* Other response codes mean (so far) to throw the response away as
135238106Sdes	 * meaningless and move on to the next nameserver. */
136238106Sdes	if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR)
137238106Sdes		return RESPONSE_TYPE_THROWAWAY;
138238106Sdes
139238106Sdes	/* Note: TC bit has already been handled */
140238106Sdes
141238106Sdes	if(dp) {
142238106Sdes		origzone = dp->name;
143238106Sdes	}
144238106Sdes
145238106Sdes	/* First we look at the answer section. This can tell us if this is a
146238106Sdes	 * CNAME or ANSWER or (provisional) ANSWER. */
147238106Sdes	if(msg->rep->an_numrrsets > 0) {
148238106Sdes		uint8_t* mname = request->qname;
149238106Sdes		size_t mname_len = request->qname_len;
150238106Sdes
151238106Sdes		/* Now look at the answer section first. 3 states: our
152238106Sdes		 * answer is there directly, our answer is there after
153238106Sdes		 * a cname, or there is just a cname. */
154238106Sdes		for(i=0; i<msg->rep->an_numrrsets; i++) {
155238106Sdes			s = msg->rep->rrsets[i];
156238106Sdes
157238106Sdes			/* if the answer section has NS rrset, and qtype ANY
158238106Sdes		 	 * and the delegation is lower, and no CNAMEs followed,
159238106Sdes		 	 * this is a referral where the NS went to AN section */
160238106Sdes			if((request->qtype == LDNS_RR_TYPE_ANY ||
161238106Sdes				request->qtype == LDNS_RR_TYPE_NS) &&
162238106Sdes				ntohs(s->rk.type) == LDNS_RR_TYPE_NS &&
163238106Sdes				ntohs(s->rk.rrset_class) == request->qclass &&
164238106Sdes				dname_strict_subdomain_c(s->rk.dname,
165238106Sdes				origzone)) {
166238106Sdes				if((msg->rep->flags&BIT_AA))
167238106Sdes					return RESPONSE_TYPE_ANSWER;
168238106Sdes				return RESPONSE_TYPE_REFERRAL;
169238106Sdes			}
170238106Sdes
171238106Sdes			/* If we have encountered an answer (before or
172238106Sdes			 * after a CNAME), then we are done! Note that
173238106Sdes			 * if qtype == CNAME then this will be noted as an
174238106Sdes			 * ANSWER before it gets treated as a CNAME, as
175238106Sdes			 * it should. */
176238106Sdes			if(ntohs(s->rk.type) == request->qtype &&
177238106Sdes				ntohs(s->rk.rrset_class) == request->qclass &&
178238106Sdes				query_dname_compare(mname, s->rk.dname) == 0) {
179238106Sdes				if((msg->rep->flags&BIT_AA))
180238106Sdes					return RESPONSE_TYPE_ANSWER;
181238106Sdes				/* If the AA bit isn't on, and we've seen
182238106Sdes				 * the answer, we only provisionally say
183238106Sdes				 * 'ANSWER' -- it very well could be a
184238106Sdes				 * REFERRAL. */
185238106Sdes				break;
186238106Sdes			}
187238106Sdes
188238106Sdes			/* If we have encountered a CNAME, make sure that
189238106Sdes			 * it is relevant. */
190238106Sdes			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
191238106Sdes				query_dname_compare(mname, s->rk.dname) == 0) {
192238106Sdes				get_cname_target(s, &mname, &mname_len);
193238106Sdes			}
194238106Sdes		}
195238106Sdes		/* not a referral, and qtype any, thus an answer */
196238106Sdes		if(request->qtype == LDNS_RR_TYPE_ANY)
197238106Sdes			return RESPONSE_TYPE_ANSWER;
198238106Sdes		/* if we encountered a CNAME (or a bunch of CNAMEs), and
199238106Sdes		 * still got to here, then it is a CNAME response.
200238106Sdes		 * (This is regardless of the AA bit at this point) */
201238106Sdes		if(mname != request->qname) {
202238106Sdes			return RESPONSE_TYPE_CNAME;
203238106Sdes		}
204238106Sdes	}
205238106Sdes
206238106Sdes	/* Looking at the authority section, we just look and see if
207238106Sdes	 * there is a SOA record, that means a NOERROR/NODATA */
208238106Sdes	for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
209238106Sdes		msg->rep->ns_numrrsets); i++) {
210238106Sdes		s = msg->rep->rrsets[i];
211238106Sdes
212238106Sdes		/* The normal way of detecting NOERROR/NODATA. */
213238106Sdes		if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
214238106Sdes			dname_subdomain_c(request->qname, s->rk.dname)) {
215238106Sdes			/* we do our own recursion, thank you */
216238106Sdes			if( (msg->rep->flags&BIT_RA) &&
217238106Sdes				!(msg->rep->flags&BIT_AA) && !rdset)
218238106Sdes				return RESPONSE_TYPE_REC_LAME;
219238106Sdes			return RESPONSE_TYPE_ANSWER;
220238106Sdes		}
221238106Sdes	}
222238106Sdes	/* Looking at the authority section, we just look and see if
223238106Sdes	 * there is a delegation NS set, turning it into a delegation.
224238106Sdes	 * Otherwise, we will have to conclude ANSWER (either it is
225238106Sdes	 * NOERROR/NODATA, or an non-authoritative answer). */
226238106Sdes	for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
227238106Sdes		msg->rep->ns_numrrsets); i++) {
228238106Sdes		s = msg->rep->rrsets[i];
229238106Sdes
230238106Sdes		/* Detect REFERRAL/LAME/ANSWER based on the relationship
231238106Sdes		 * of the NS set to the originating zone name. */
232238106Sdes		if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) {
233238106Sdes			/* If we are getting an NS set for the zone we
234238106Sdes			 * thought we were contacting, then it is an answer.*/
235238106Sdes			if(query_dname_compare(s->rk.dname, origzone) == 0) {
236238106Sdes				/* see if mistakenly a recursive server was
237238106Sdes				 * deployed and is responding nonAA */
238238106Sdes				if( (msg->rep->flags&BIT_RA) &&
239238106Sdes					!(msg->rep->flags&BIT_AA) && !rdset)
240238106Sdes					return RESPONSE_TYPE_REC_LAME;
241238106Sdes				/* Or if a lame server is deployed,
242238106Sdes				 * which gives ns==zone delegation from cache
243238106Sdes				 * without AA bit as well, with nodata nosoa*/
244238106Sdes				/* real answer must be +AA and SOA RFC(2308),
245238106Sdes				 * so this is wrong, and we SERVFAIL it if
246238106Sdes				 * this is the only possible reply, if it
247238106Sdes				 * is misdeployed the THROWAWAY makes us pick
248238106Sdes				 * the next server from the selection */
249238106Sdes				if(msg->rep->an_numrrsets==0 &&
250238106Sdes					!(msg->rep->flags&BIT_AA) && !rdset)
251238106Sdes					return RESPONSE_TYPE_THROWAWAY;
252238106Sdes				return RESPONSE_TYPE_ANSWER;
253238106Sdes			}
254238106Sdes			/* If we are getting a referral upwards (or to
255238106Sdes			 * the same zone), then the server is 'lame'. */
256238106Sdes			if(dname_subdomain_c(origzone, s->rk.dname)) {
257238106Sdes				if(rdset) /* forward or reclame not LAME */
258238106Sdes					return RESPONSE_TYPE_THROWAWAY;
259238106Sdes				return RESPONSE_TYPE_LAME;
260238106Sdes			}
261238106Sdes			/* If the NS set is below the delegation point we
262238106Sdes			 * are on, and it is non-authoritative, then it is
263238106Sdes			 * a referral, otherwise it is an answer. */
264238106Sdes			if(dname_subdomain_c(s->rk.dname, origzone)) {
265238106Sdes				/* NOTE: I no longer remember in what case
266238106Sdes				 * we would like this to be an answer.
267238106Sdes				 * NODATA should have a SOA or nothing,
268238106Sdes				 * not an NS rrset.
269238106Sdes				 * True, referrals should not have the AA
270238106Sdes				 * bit set, but... */
271238106Sdes
272238106Sdes				/* if((msg->rep->flags&BIT_AA))
273238106Sdes					return RESPONSE_TYPE_ANSWER; */
274238106Sdes				return RESPONSE_TYPE_REFERRAL;
275238106Sdes			}
276238106Sdes			/* Otherwise, the NS set is irrelevant. */
277238106Sdes		}
278238106Sdes	}
279238106Sdes
280238106Sdes	/* If we've gotten this far, this is NOERROR/NODATA (which could
281238106Sdes	 * be an entirely empty message) */
282238106Sdes	/* check if recursive answer; saying it has empty cache */
283238106Sdes	if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset)
284238106Sdes		return RESPONSE_TYPE_REC_LAME;
285238106Sdes	return RESPONSE_TYPE_ANSWER;
286238106Sdes}
287