2 * iterator/iter_resptype.c - response type information and classification.
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
6 * This software is open source.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
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.
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.
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.
39 * This file defines the response type. DNS Responses can be classified as
40 * one of the response types.
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"
51 response_type_from_cache(struct dns_msg* msg,
52 struct query_info* request)
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;
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;
70 for(i=0; i<msg->rep->an_numrrsets; i++) {
71 struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
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,
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;
84 /* If we have encountered a CNAME, make sure that
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);
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;
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;
106 response_type_from_server(int rdset,
107 struct dns_msg* msg, struct query_info* request, struct delegpt* dp)
109 uint8_t* origzone = (uint8_t*)"\000"; /* the default */
110 struct ub_packed_rrset_key* s;
114 return RESPONSE_TYPE_THROWAWAY;
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,
128 return RESPONSE_TYPE_CNAME;
131 return RESPONSE_TYPE_ANSWER;
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;
139 /* Note: TC bit has already been handled */
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;
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];
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,
166 if((msg->rep->flags&BIT_AA))
167 return RESPONSE_TYPE_ANSWER;
168 return RESPONSE_TYPE_REFERRAL;
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
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
188 /* If we have encountered a CNAME, make sure that
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);
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;
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];
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;
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];
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;
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;
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,
269 * True, referrals should not have the AA
272 /* if((msg->rep->flags&BIT_AA))
273 return RESPONSE_TYPE_ANSWER; */
274 return RESPONSE_TYPE_REFERRAL;
276 /* Otherwise, the NS set is irrelevant. */
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;