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