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 117 /* If the message is NXDOMAIN, then it answers the question. */ 118 if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) { 119 /* make sure its not recursive when we don't want it to */ 120 if( (msg->rep->flags&BIT_RA) && 121 !(msg->rep->flags&BIT_AA) && !rdset) 122 return RESPONSE_TYPE_REC_LAME; 123 /* it could be a CNAME with NXDOMAIN rcode */ 124 for(i=0; i<msg->rep->an_numrrsets; i++) { 125 s = msg->rep->rrsets[i]; 126 if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 127 query_dname_compare(request->qname, 128 s->rk.dname) == 0) { 129 return RESPONSE_TYPE_CNAME; 130 } 131 } 132 return RESPONSE_TYPE_ANSWER; 133 } 134 135 /* Other response codes mean (so far) to throw the response away as 136 * meaningless and move on to the next nameserver. */ 137 if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) 138 return RESPONSE_TYPE_THROWAWAY; 139 140 /* Note: TC bit has already been handled */ 141 142 if(dp) { 143 origzone = dp->name; 144 } 145 146 /* First we look at the answer section. This can tell us if this is a 147 * CNAME or ANSWER or (provisional) ANSWER. */ 148 if(msg->rep->an_numrrsets > 0) { 149 uint8_t* mname = request->qname; 150 size_t mname_len = request->qname_len; 151 152 /* Now look at the answer section first. 3 states: our 153 * answer is there directly, our answer is there after 154 * a cname, or there is just a cname. */ 155 for(i=0; i<msg->rep->an_numrrsets; i++) { 156 s = msg->rep->rrsets[i]; 157 158 /* if the answer section has NS rrset, and qtype ANY 159 * and the delegation is lower, and no CNAMEs followed, 160 * this is a referral where the NS went to AN section */ 161 if((request->qtype == LDNS_RR_TYPE_ANY || 162 request->qtype == LDNS_RR_TYPE_NS) && 163 ntohs(s->rk.type) == LDNS_RR_TYPE_NS && 164 ntohs(s->rk.rrset_class) == request->qclass && 165 dname_strict_subdomain_c(s->rk.dname, 166 origzone)) { 167 if((msg->rep->flags&BIT_AA)) 168 return RESPONSE_TYPE_ANSWER; 169 return RESPONSE_TYPE_REFERRAL; 170 } 171 172 /* If we have encountered an answer (before or 173 * after a CNAME), then we are done! Note that 174 * if qtype == CNAME then this will be noted as an 175 * ANSWER before it gets treated as a CNAME, as 176 * it should. */ 177 if(ntohs(s->rk.type) == request->qtype && 178 ntohs(s->rk.rrset_class) == request->qclass && 179 query_dname_compare(mname, s->rk.dname) == 0) { 180 if((msg->rep->flags&BIT_AA)) 181 return RESPONSE_TYPE_ANSWER; 182 /* If the AA bit isn't on, and we've seen 183 * the answer, we only provisionally say 184 * 'ANSWER' -- it very well could be a 185 * REFERRAL. */ 186 break; 187 } 188 189 /* If we have encountered a CNAME, make sure that 190 * it is relevant. */ 191 if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 192 query_dname_compare(mname, s->rk.dname) == 0) { 193 get_cname_target(s, &mname, &mname_len); 194 } 195 } 196 /* not a referral, and qtype any, thus an answer */ 197 if(request->qtype == LDNS_RR_TYPE_ANY) 198 return RESPONSE_TYPE_ANSWER; 199 /* if we encountered a CNAME (or a bunch of CNAMEs), and 200 * still got to here, then it is a CNAME response. 201 * (This is regardless of the AA bit at this point) */ 202 if(mname != request->qname) { 203 return RESPONSE_TYPE_CNAME; 204 } 205 } 206 207 /* Looking at the authority section, we just look and see if 208 * there is a SOA record, that means a NOERROR/NODATA */ 209 for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets + 210 msg->rep->ns_numrrsets); i++) { 211 s = msg->rep->rrsets[i]; 212 213 /* The normal way of detecting NOERROR/NODATA. */ 214 if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA && 215 dname_subdomain_c(request->qname, s->rk.dname)) { 216 /* we do our own recursion, thank you */ 217 if( (msg->rep->flags&BIT_RA) && 218 !(msg->rep->flags&BIT_AA) && !rdset) 219 return RESPONSE_TYPE_REC_LAME; 220 return RESPONSE_TYPE_ANSWER; 221 } 222 } 223 /* Looking at the authority section, we just look and see if 224 * there is a delegation NS set, turning it into a delegation. 225 * Otherwise, we will have to conclude ANSWER (either it is 226 * NOERROR/NODATA, or an non-authoritative answer). */ 227 for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets + 228 msg->rep->ns_numrrsets); i++) { 229 s = msg->rep->rrsets[i]; 230 231 /* Detect REFERRAL/LAME/ANSWER based on the relationship 232 * of the NS set to the originating zone name. */ 233 if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) { 234 /* If we are getting an NS set for the zone we 235 * thought we were contacting, then it is an answer.*/ 236 if(query_dname_compare(s->rk.dname, origzone) == 0) { 237 /* see if mistakenly a recursive server was 238 * deployed and is responding nonAA */ 239 if( (msg->rep->flags&BIT_RA) && 240 !(msg->rep->flags&BIT_AA) && !rdset) 241 return RESPONSE_TYPE_REC_LAME; 242 /* Or if a lame server is deployed, 243 * which gives ns==zone delegation from cache 244 * without AA bit as well, with nodata nosoa*/ 245 /* real answer must be +AA and SOA RFC(2308), 246 * so this is wrong, and we SERVFAIL it if 247 * this is the only possible reply, if it 248 * is misdeployed the THROWAWAY makes us pick 249 * the next server from the selection */ 250 if(msg->rep->an_numrrsets==0 && 251 !(msg->rep->flags&BIT_AA) && !rdset) 252 return RESPONSE_TYPE_THROWAWAY; 253 return RESPONSE_TYPE_ANSWER; 254 } 255 /* If we are getting a referral upwards (or to 256 * the same zone), then the server is 'lame'. */ 257 if(dname_subdomain_c(origzone, s->rk.dname)) { 258 if(rdset) /* forward or reclame not LAME */ 259 return RESPONSE_TYPE_THROWAWAY; 260 return RESPONSE_TYPE_LAME; 261 } 262 /* If the NS set is below the delegation point we 263 * are on, and it is non-authoritative, then it is 264 * a referral, otherwise it is an answer. */ 265 if(dname_subdomain_c(s->rk.dname, origzone)) { 266 /* NOTE: I no longer remember in what case 267 * we would like this to be an answer. 268 * NODATA should have a SOA or nothing, 269 * not an NS rrset. 270 * True, referrals should not have the AA 271 * bit set, but... */ 272 273 /* if((msg->rep->flags&BIT_AA)) 274 return RESPONSE_TYPE_ANSWER; */ 275 return RESPONSE_TYPE_REFERRAL; 276 } 277 /* Otherwise, the NS set is irrelevant. */ 278 } 279 } 280 281 /* If we've gotten this far, this is NOERROR/NODATA (which could 282 * be an entirely empty message) */ 283 /* check if recursive answer; saying it has empty cache */ 284 if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset) 285 return RESPONSE_TYPE_REC_LAME; 286 return RESPONSE_TYPE_ANSWER; 287 } 288