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