1238106Sdes/* 2238106Sdes * iterator/iter_resptype.c - response type information and classification. 3238106Sdes * 4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved. 5238106Sdes * 6238106Sdes * This software is open source. 7238106Sdes * 8238106Sdes * Redistribution and use in source and binary forms, with or without 9238106Sdes * modification, are permitted provided that the following conditions 10238106Sdes * are met: 11238106Sdes * 12238106Sdes * Redistributions of source code must retain the above copyright notice, 13238106Sdes * this list of conditions and the following disclaimer. 14238106Sdes * 15238106Sdes * Redistributions in binary form must reproduce the above copyright notice, 16238106Sdes * this list of conditions and the following disclaimer in the documentation 17238106Sdes * and/or other materials provided with the distribution. 18238106Sdes * 19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may 20238106Sdes * be used to endorse or promote products derived from this software without 21238106Sdes * specific prior written permission. 22238106Sdes * 23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24238106Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25238106Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26238106Sdes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 27238106Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28238106Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31238106Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33238106Sdes * POSSIBILITY OF SUCH DAMAGE. 34238106Sdes */ 35238106Sdes 36238106Sdes/** 37238106Sdes * \file 38238106Sdes * 39238106Sdes * This file defines the response type. DNS Responses can be classified as 40238106Sdes * one of the response types. 41238106Sdes */ 42238106Sdes#include "config.h" 43238106Sdes#include <ldns/packet.h> 44238106Sdes#include "iterator/iter_resptype.h" 45238106Sdes#include "iterator/iter_delegpt.h" 46238106Sdes#include "services/cache/dns.h" 47238106Sdes#include "util/net_help.h" 48238106Sdes#include "util/data/dname.h" 49238106Sdes 50238106Sdesenum response_type 51238106Sdesresponse_type_from_cache(struct dns_msg* msg, 52238106Sdes struct query_info* request) 53238106Sdes{ 54238106Sdes /* If the message is NXDOMAIN, then it is an ANSWER. */ 55238106Sdes if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) 56238106Sdes return RESPONSE_TYPE_ANSWER; 57238106Sdes if(request->qtype == LDNS_RR_TYPE_ANY) 58238106Sdes return RESPONSE_TYPE_ANSWER; 59238106Sdes 60238106Sdes /* First we look at the answer section. This can tell us if this is 61238106Sdes * CNAME or positive ANSWER. */ 62238106Sdes if(msg->rep->an_numrrsets > 0) { 63238106Sdes /* Now look at the answer section first. 3 states: 64238106Sdes * o our answer is there directly, 65238106Sdes * o our answer is there after a cname, 66238106Sdes * o or there is just a cname. */ 67238106Sdes uint8_t* mname = request->qname; 68238106Sdes size_t mname_len = request->qname_len; 69238106Sdes size_t i; 70238106Sdes for(i=0; i<msg->rep->an_numrrsets; i++) { 71238106Sdes struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; 72238106Sdes 73238106Sdes /* If we have encountered an answer (before or 74238106Sdes * after a CNAME), then we are done! Note that 75238106Sdes * if qtype == CNAME then this will be noted as 76238106Sdes * an ANSWER before it gets treated as a CNAME, 77238106Sdes * as it should */ 78238106Sdes if(ntohs(s->rk.type) == request->qtype && 79238106Sdes ntohs(s->rk.rrset_class) == request->qclass && 80238106Sdes query_dname_compare(mname, s->rk.dname) == 0) { 81238106Sdes return RESPONSE_TYPE_ANSWER; 82238106Sdes } 83238106Sdes 84238106Sdes /* If we have encountered a CNAME, make sure that 85238106Sdes * it is relevant. */ 86238106Sdes if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 87238106Sdes query_dname_compare(mname, s->rk.dname) == 0) { 88238106Sdes get_cname_target(s, &mname, &mname_len); 89238106Sdes } 90238106Sdes } 91238106Sdes 92238106Sdes /* if we encountered a CNAME (or a bunch of CNAMEs), and 93238106Sdes * still got to here, then it is a CNAME response. (i.e., 94238106Sdes * the CNAME chain didn't terminate in an answer rrset.) */ 95238106Sdes if(mname != request->qname) { 96238106Sdes return RESPONSE_TYPE_CNAME; 97238106Sdes } 98238106Sdes } 99238106Sdes 100238106Sdes /* At this point, since we don't need to detect REFERRAL or LAME 101238106Sdes * messages, it can only be an ANSWER. */ 102238106Sdes return RESPONSE_TYPE_ANSWER; 103238106Sdes} 104238106Sdes 105238106Sdesenum response_type 106238106Sdesresponse_type_from_server(int rdset, 107238106Sdes struct dns_msg* msg, struct query_info* request, struct delegpt* dp) 108238106Sdes{ 109238106Sdes uint8_t* origzone = (uint8_t*)"\000"; /* the default */ 110238106Sdes struct ub_packed_rrset_key* s; 111238106Sdes size_t i; 112238106Sdes 113238106Sdes if(!msg || !request) 114238106Sdes return RESPONSE_TYPE_THROWAWAY; 115238106Sdes 116238106Sdes /* If the message is NXDOMAIN, then it answers the question. */ 117238106Sdes if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) { 118238106Sdes /* make sure its not recursive when we don't want it to */ 119238106Sdes if( (msg->rep->flags&BIT_RA) && 120238106Sdes !(msg->rep->flags&BIT_AA) && !rdset) 121238106Sdes return RESPONSE_TYPE_REC_LAME; 122238106Sdes /* it could be a CNAME with NXDOMAIN rcode */ 123238106Sdes for(i=0; i<msg->rep->an_numrrsets; i++) { 124238106Sdes s = msg->rep->rrsets[i]; 125238106Sdes if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 126238106Sdes query_dname_compare(request->qname, 127238106Sdes s->rk.dname) == 0) { 128238106Sdes return RESPONSE_TYPE_CNAME; 129238106Sdes } 130238106Sdes } 131238106Sdes return RESPONSE_TYPE_ANSWER; 132238106Sdes } 133238106Sdes 134238106Sdes /* Other response codes mean (so far) to throw the response away as 135238106Sdes * meaningless and move on to the next nameserver. */ 136238106Sdes if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) 137238106Sdes return RESPONSE_TYPE_THROWAWAY; 138238106Sdes 139238106Sdes /* Note: TC bit has already been handled */ 140238106Sdes 141238106Sdes if(dp) { 142238106Sdes origzone = dp->name; 143238106Sdes } 144238106Sdes 145238106Sdes /* First we look at the answer section. This can tell us if this is a 146238106Sdes * CNAME or ANSWER or (provisional) ANSWER. */ 147238106Sdes if(msg->rep->an_numrrsets > 0) { 148238106Sdes uint8_t* mname = request->qname; 149238106Sdes size_t mname_len = request->qname_len; 150238106Sdes 151238106Sdes /* Now look at the answer section first. 3 states: our 152238106Sdes * answer is there directly, our answer is there after 153238106Sdes * a cname, or there is just a cname. */ 154238106Sdes for(i=0; i<msg->rep->an_numrrsets; i++) { 155238106Sdes s = msg->rep->rrsets[i]; 156238106Sdes 157238106Sdes /* if the answer section has NS rrset, and qtype ANY 158238106Sdes * and the delegation is lower, and no CNAMEs followed, 159238106Sdes * this is a referral where the NS went to AN section */ 160238106Sdes if((request->qtype == LDNS_RR_TYPE_ANY || 161238106Sdes request->qtype == LDNS_RR_TYPE_NS) && 162238106Sdes ntohs(s->rk.type) == LDNS_RR_TYPE_NS && 163238106Sdes ntohs(s->rk.rrset_class) == request->qclass && 164238106Sdes dname_strict_subdomain_c(s->rk.dname, 165238106Sdes origzone)) { 166238106Sdes if((msg->rep->flags&BIT_AA)) 167238106Sdes return RESPONSE_TYPE_ANSWER; 168238106Sdes return RESPONSE_TYPE_REFERRAL; 169238106Sdes } 170238106Sdes 171238106Sdes /* If we have encountered an answer (before or 172238106Sdes * after a CNAME), then we are done! Note that 173238106Sdes * if qtype == CNAME then this will be noted as an 174238106Sdes * ANSWER before it gets treated as a CNAME, as 175238106Sdes * it should. */ 176238106Sdes if(ntohs(s->rk.type) == request->qtype && 177238106Sdes ntohs(s->rk.rrset_class) == request->qclass && 178238106Sdes query_dname_compare(mname, s->rk.dname) == 0) { 179238106Sdes if((msg->rep->flags&BIT_AA)) 180238106Sdes return RESPONSE_TYPE_ANSWER; 181238106Sdes /* If the AA bit isn't on, and we've seen 182238106Sdes * the answer, we only provisionally say 183238106Sdes * 'ANSWER' -- it very well could be a 184238106Sdes * REFERRAL. */ 185238106Sdes break; 186238106Sdes } 187238106Sdes 188238106Sdes /* If we have encountered a CNAME, make sure that 189238106Sdes * it is relevant. */ 190238106Sdes if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 191238106Sdes query_dname_compare(mname, s->rk.dname) == 0) { 192238106Sdes get_cname_target(s, &mname, &mname_len); 193238106Sdes } 194238106Sdes } 195238106Sdes /* not a referral, and qtype any, thus an answer */ 196238106Sdes if(request->qtype == LDNS_RR_TYPE_ANY) 197238106Sdes return RESPONSE_TYPE_ANSWER; 198238106Sdes /* if we encountered a CNAME (or a bunch of CNAMEs), and 199238106Sdes * still got to here, then it is a CNAME response. 200238106Sdes * (This is regardless of the AA bit at this point) */ 201238106Sdes if(mname != request->qname) { 202238106Sdes return RESPONSE_TYPE_CNAME; 203238106Sdes } 204238106Sdes } 205238106Sdes 206238106Sdes /* Looking at the authority section, we just look and see if 207238106Sdes * there is a SOA record, that means a NOERROR/NODATA */ 208238106Sdes for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets + 209238106Sdes msg->rep->ns_numrrsets); i++) { 210238106Sdes s = msg->rep->rrsets[i]; 211238106Sdes 212238106Sdes /* The normal way of detecting NOERROR/NODATA. */ 213238106Sdes if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA && 214238106Sdes dname_subdomain_c(request->qname, s->rk.dname)) { 215238106Sdes /* we do our own recursion, thank you */ 216238106Sdes if( (msg->rep->flags&BIT_RA) && 217238106Sdes !(msg->rep->flags&BIT_AA) && !rdset) 218238106Sdes return RESPONSE_TYPE_REC_LAME; 219238106Sdes return RESPONSE_TYPE_ANSWER; 220238106Sdes } 221238106Sdes } 222238106Sdes /* Looking at the authority section, we just look and see if 223238106Sdes * there is a delegation NS set, turning it into a delegation. 224238106Sdes * Otherwise, we will have to conclude ANSWER (either it is 225238106Sdes * NOERROR/NODATA, or an non-authoritative answer). */ 226238106Sdes for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets + 227238106Sdes msg->rep->ns_numrrsets); i++) { 228238106Sdes s = msg->rep->rrsets[i]; 229238106Sdes 230238106Sdes /* Detect REFERRAL/LAME/ANSWER based on the relationship 231238106Sdes * of the NS set to the originating zone name. */ 232238106Sdes if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) { 233238106Sdes /* If we are getting an NS set for the zone we 234238106Sdes * thought we were contacting, then it is an answer.*/ 235238106Sdes if(query_dname_compare(s->rk.dname, origzone) == 0) { 236238106Sdes /* see if mistakenly a recursive server was 237238106Sdes * deployed and is responding nonAA */ 238238106Sdes if( (msg->rep->flags&BIT_RA) && 239238106Sdes !(msg->rep->flags&BIT_AA) && !rdset) 240238106Sdes return RESPONSE_TYPE_REC_LAME; 241238106Sdes /* Or if a lame server is deployed, 242238106Sdes * which gives ns==zone delegation from cache 243238106Sdes * without AA bit as well, with nodata nosoa*/ 244238106Sdes /* real answer must be +AA and SOA RFC(2308), 245238106Sdes * so this is wrong, and we SERVFAIL it if 246238106Sdes * this is the only possible reply, if it 247238106Sdes * is misdeployed the THROWAWAY makes us pick 248238106Sdes * the next server from the selection */ 249238106Sdes if(msg->rep->an_numrrsets==0 && 250238106Sdes !(msg->rep->flags&BIT_AA) && !rdset) 251238106Sdes return RESPONSE_TYPE_THROWAWAY; 252238106Sdes return RESPONSE_TYPE_ANSWER; 253238106Sdes } 254238106Sdes /* If we are getting a referral upwards (or to 255238106Sdes * the same zone), then the server is 'lame'. */ 256238106Sdes if(dname_subdomain_c(origzone, s->rk.dname)) { 257238106Sdes if(rdset) /* forward or reclame not LAME */ 258238106Sdes return RESPONSE_TYPE_THROWAWAY; 259238106Sdes return RESPONSE_TYPE_LAME; 260238106Sdes } 261238106Sdes /* If the NS set is below the delegation point we 262238106Sdes * are on, and it is non-authoritative, then it is 263238106Sdes * a referral, otherwise it is an answer. */ 264238106Sdes if(dname_subdomain_c(s->rk.dname, origzone)) { 265238106Sdes /* NOTE: I no longer remember in what case 266238106Sdes * we would like this to be an answer. 267238106Sdes * NODATA should have a SOA or nothing, 268238106Sdes * not an NS rrset. 269238106Sdes * True, referrals should not have the AA 270238106Sdes * bit set, but... */ 271238106Sdes 272238106Sdes /* if((msg->rep->flags&BIT_AA)) 273238106Sdes return RESPONSE_TYPE_ANSWER; */ 274238106Sdes return RESPONSE_TYPE_REFERRAL; 275238106Sdes } 276238106Sdes /* Otherwise, the NS set is irrelevant. */ 277238106Sdes } 278238106Sdes } 279238106Sdes 280238106Sdes /* If we've gotten this far, this is NOERROR/NODATA (which could 281238106Sdes * be an entirely empty message) */ 282238106Sdes /* check if recursive answer; saying it has empty cache */ 283238106Sdes if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset) 284238106Sdes return RESPONSE_TYPE_REC_LAME; 285238106Sdes return RESPONSE_TYPE_ANSWER; 286238106Sdes} 287