1/*
2 * iterator/iter_resptype.c - response type information and classification.
3 *
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36/**
37 * \file
38 *
39 * This file defines the response type. DNS Responses can be classified as
40 * one of the response types.
41 */
42#include "config.h"
43#include <ldns/packet.h>
44#include "iterator/iter_resptype.h"
45#include "iterator/iter_delegpt.h"
46#include "services/cache/dns.h"
47#include "util/net_help.h"
48#include "util/data/dname.h"
49
50enum response_type
51response_type_from_cache(struct dns_msg* msg,
52	struct query_info* request)
53{
54	/* If the message is NXDOMAIN, then it is an ANSWER. */
55	if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN)
56		return RESPONSE_TYPE_ANSWER;
57	if(request->qtype == LDNS_RR_TYPE_ANY)
58		return RESPONSE_TYPE_ANSWER;
59
60	/* First we look at the answer section. This can tell us if this is
61	 * CNAME or positive ANSWER. */
62	if(msg->rep->an_numrrsets > 0) {
63		/* Now look at the answer section first. 3 states:
64		 *	o our answer is there directly,
65		 *	o our answer is there after a cname,
66		 *	o or there is just a cname. */
67		uint8_t* mname = request->qname;
68		size_t mname_len = request->qname_len;
69		size_t i;
70		for(i=0; i<msg->rep->an_numrrsets; i++) {
71			struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
72
73			/* If we have encountered an answer (before or
74			 * after a CNAME), then we are done! Note that
75			 * if qtype == CNAME then this will be noted as
76			 * an ANSWER before it gets treated as a CNAME,
77			 * as it should */
78			if(ntohs(s->rk.type) == request->qtype &&
79				ntohs(s->rk.rrset_class) == request->qclass &&
80				query_dname_compare(mname, s->rk.dname) == 0) {
81				return RESPONSE_TYPE_ANSWER;
82			}
83
84			/* If we have encountered a CNAME, make sure that
85			 * it is relevant. */
86			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
87				query_dname_compare(mname, s->rk.dname) == 0) {
88				get_cname_target(s, &mname, &mname_len);
89			}
90		}
91
92		/* if we encountered a CNAME (or a bunch of CNAMEs), and
93		 * still got to here, then it is a CNAME response. (i.e.,
94		 * the CNAME chain didn't terminate in an answer rrset.) */
95		if(mname != request->qname) {
96			return RESPONSE_TYPE_CNAME;
97		}
98	}
99
100	/* At this point, since we don't need to detect REFERRAL or LAME
101	 * messages, it can only be an ANSWER. */
102	return RESPONSE_TYPE_ANSWER;
103}
104
105enum response_type
106response_type_from_server(int rdset,
107	struct dns_msg* msg, struct query_info* request, struct delegpt* dp)
108{
109	uint8_t* origzone = (uint8_t*)"\000"; /* the default */
110	struct ub_packed_rrset_key* s;
111	size_t i;
112
113	if(!msg || !request)
114		return RESPONSE_TYPE_THROWAWAY;
115
116	/* If the message is NXDOMAIN, then it answers the question. */
117	if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) {
118		/* make sure its not recursive when we don't want it to */
119		if( (msg->rep->flags&BIT_RA) &&
120			!(msg->rep->flags&BIT_AA) && !rdset)
121				return RESPONSE_TYPE_REC_LAME;
122		/* it could be a CNAME with NXDOMAIN rcode */
123		for(i=0; i<msg->rep->an_numrrsets; i++) {
124			s = msg->rep->rrsets[i];
125			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
126				query_dname_compare(request->qname,
127				s->rk.dname) == 0) {
128				return RESPONSE_TYPE_CNAME;
129			}
130		}
131		return RESPONSE_TYPE_ANSWER;
132	}
133
134	/* Other response codes mean (so far) to throw the response away as
135	 * meaningless and move on to the next nameserver. */
136	if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR)
137		return RESPONSE_TYPE_THROWAWAY;
138
139	/* Note: TC bit has already been handled */
140
141	if(dp) {
142		origzone = dp->name;
143	}
144
145	/* First we look at the answer section. This can tell us if this is a
146	 * CNAME or ANSWER or (provisional) ANSWER. */
147	if(msg->rep->an_numrrsets > 0) {
148		uint8_t* mname = request->qname;
149		size_t mname_len = request->qname_len;
150
151		/* Now look at the answer section first. 3 states: our
152		 * answer is there directly, our answer is there after
153		 * a cname, or there is just a cname. */
154		for(i=0; i<msg->rep->an_numrrsets; i++) {
155			s = msg->rep->rrsets[i];
156
157			/* if the answer section has NS rrset, and qtype ANY
158		 	 * and the delegation is lower, and no CNAMEs followed,
159		 	 * this is a referral where the NS went to AN section */
160			if((request->qtype == LDNS_RR_TYPE_ANY ||
161				request->qtype == LDNS_RR_TYPE_NS) &&
162				ntohs(s->rk.type) == LDNS_RR_TYPE_NS &&
163				ntohs(s->rk.rrset_class) == request->qclass &&
164				dname_strict_subdomain_c(s->rk.dname,
165				origzone)) {
166				if((msg->rep->flags&BIT_AA))
167					return RESPONSE_TYPE_ANSWER;
168				return RESPONSE_TYPE_REFERRAL;
169			}
170
171			/* If we have encountered an answer (before or
172			 * after a CNAME), then we are done! Note that
173			 * if qtype == CNAME then this will be noted as an
174			 * ANSWER before it gets treated as a CNAME, as
175			 * it should. */
176			if(ntohs(s->rk.type) == request->qtype &&
177				ntohs(s->rk.rrset_class) == request->qclass &&
178				query_dname_compare(mname, s->rk.dname) == 0) {
179				if((msg->rep->flags&BIT_AA))
180					return RESPONSE_TYPE_ANSWER;
181				/* If the AA bit isn't on, and we've seen
182				 * the answer, we only provisionally say
183				 * 'ANSWER' -- it very well could be a
184				 * REFERRAL. */
185				break;
186			}
187
188			/* If we have encountered a CNAME, make sure that
189			 * it is relevant. */
190			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
191				query_dname_compare(mname, s->rk.dname) == 0) {
192				get_cname_target(s, &mname, &mname_len);
193			}
194		}
195		/* not a referral, and qtype any, thus an answer */
196		if(request->qtype == LDNS_RR_TYPE_ANY)
197			return RESPONSE_TYPE_ANSWER;
198		/* if we encountered a CNAME (or a bunch of CNAMEs), and
199		 * still got to here, then it is a CNAME response.
200		 * (This is regardless of the AA bit at this point) */
201		if(mname != request->qname) {
202			return RESPONSE_TYPE_CNAME;
203		}
204	}
205
206	/* Looking at the authority section, we just look and see if
207	 * there is a SOA record, that means a NOERROR/NODATA */
208	for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
209		msg->rep->ns_numrrsets); i++) {
210		s = msg->rep->rrsets[i];
211
212		/* The normal way of detecting NOERROR/NODATA. */
213		if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
214			dname_subdomain_c(request->qname, s->rk.dname)) {
215			/* we do our own recursion, thank you */
216			if( (msg->rep->flags&BIT_RA) &&
217				!(msg->rep->flags&BIT_AA) && !rdset)
218				return RESPONSE_TYPE_REC_LAME;
219			return RESPONSE_TYPE_ANSWER;
220		}
221	}
222	/* Looking at the authority section, we just look and see if
223	 * there is a delegation NS set, turning it into a delegation.
224	 * Otherwise, we will have to conclude ANSWER (either it is
225	 * NOERROR/NODATA, or an non-authoritative answer). */
226	for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
227		msg->rep->ns_numrrsets); i++) {
228		s = msg->rep->rrsets[i];
229
230		/* Detect REFERRAL/LAME/ANSWER based on the relationship
231		 * of the NS set to the originating zone name. */
232		if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) {
233			/* If we are getting an NS set for the zone we
234			 * thought we were contacting, then it is an answer.*/
235			if(query_dname_compare(s->rk.dname, origzone) == 0) {
236				/* see if mistakenly a recursive server was
237				 * deployed and is responding nonAA */
238				if( (msg->rep->flags&BIT_RA) &&
239					!(msg->rep->flags&BIT_AA) && !rdset)
240					return RESPONSE_TYPE_REC_LAME;
241				/* Or if a lame server is deployed,
242				 * which gives ns==zone delegation from cache
243				 * without AA bit as well, with nodata nosoa*/
244				/* real answer must be +AA and SOA RFC(2308),
245				 * so this is wrong, and we SERVFAIL it if
246				 * this is the only possible reply, if it
247				 * is misdeployed the THROWAWAY makes us pick
248				 * the next server from the selection */
249				if(msg->rep->an_numrrsets==0 &&
250					!(msg->rep->flags&BIT_AA) && !rdset)
251					return RESPONSE_TYPE_THROWAWAY;
252				return RESPONSE_TYPE_ANSWER;
253			}
254			/* If we are getting a referral upwards (or to
255			 * the same zone), then the server is 'lame'. */
256			if(dname_subdomain_c(origzone, s->rk.dname)) {
257				if(rdset) /* forward or reclame not LAME */
258					return RESPONSE_TYPE_THROWAWAY;
259				return RESPONSE_TYPE_LAME;
260			}
261			/* If the NS set is below the delegation point we
262			 * are on, and it is non-authoritative, then it is
263			 * a referral, otherwise it is an answer. */
264			if(dname_subdomain_c(s->rk.dname, origzone)) {
265				/* NOTE: I no longer remember in what case
266				 * we would like this to be an answer.
267				 * NODATA should have a SOA or nothing,
268				 * not an NS rrset.
269				 * True, referrals should not have the AA
270				 * bit set, but... */
271
272				/* if((msg->rep->flags&BIT_AA))
273					return RESPONSE_TYPE_ANSWER; */
274				return RESPONSE_TYPE_REFERRAL;
275			}
276			/* Otherwise, the NS set is irrelevant. */
277		}
278	}
279
280	/* If we've gotten this far, this is NOERROR/NODATA (which could
281	 * be an entirely empty message) */
282	/* check if recursive answer; saying it has empty cache */
283	if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset)
284		return RESPONSE_TYPE_REC_LAME;
285	return RESPONSE_TYPE_ANSWER;
286}
287