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 } 310