]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/unbound/iterator/iter_resptype.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 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
50 enum response_type 
51 response_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
105 enum response_type 
106 response_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 }