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 50enum response_type 51response_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 105enum response_type 106response_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} 287