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