]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/unbound/iterator/iter_resptype.c
unbound: Vendor import 1.19.0
[FreeBSD/FreeBSD.git] / contrib / unbound / iterator / iter_resptype.c
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 
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 
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 }