From owner-svn-src-vendor@FreeBSD.ORG Fri Jan 2 17:33:31 2015 Return-Path: Delivered-To: svn-src-vendor@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 3A8874A1; Fri, 2 Jan 2015 17:33:31 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 25A106635C; Fri, 2 Jan 2015 17:33:31 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t02HXV6G028589; Fri, 2 Jan 2015 17:33:31 GMT (envelope-from des@FreeBSD.org) Received: (from des@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t02HXVfB028587; Fri, 2 Jan 2015 17:33:31 GMT (envelope-from des@FreeBSD.org) Message-Id: <201501021733.t02HXVfB028587@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: des set sender to des@FreeBSD.org using -f From: Dag-Erling Smørgrav Date: Fri, 2 Jan 2015 17:33:31 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-vendor@freebsd.org Subject: svn commit: r276543 - in vendor/unbound/1.5.0: . iterator X-SVN-Group: vendor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-vendor@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for the vendor work area tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 02 Jan 2015 17:33:31 -0000 Author: des Date: Fri Jan 2 17:33:30 2015 New Revision: 276543 URL: https://svnweb.freebsd.org/changeset/base/276543 Log: tag unbound 1.5.0 Added: vendor/unbound/1.5.0/ - copied from r276541, vendor/unbound/dist/ Replaced: vendor/unbound/1.5.0/iterator/iterator.c - copied unchanged from r276542, vendor/unbound/dist/iterator/iterator.c vendor/unbound/1.5.0/iterator/iterator.h - copied unchanged from r276542, vendor/unbound/dist/iterator/iterator.h Copied: vendor/unbound/1.5.0/iterator/iterator.c (from r276542, vendor/unbound/dist/iterator/iterator.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor/unbound/1.5.0/iterator/iterator.c Fri Jan 2 17:33:30 2015 (r276543, copy of r276542, vendor/unbound/dist/iterator/iterator.c) @@ -0,0 +1,2996 @@ +/* + * iterator/iterator.c - iterative resolver DNS query response module + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file contains a module that performs recusive iterative DNS query + * processing. + */ + +#include "config.h" +#include "iterator/iterator.h" +#include "iterator/iter_utils.h" +#include "iterator/iter_hints.h" +#include "iterator/iter_fwd.h" +#include "iterator/iter_donotq.h" +#include "iterator/iter_delegpt.h" +#include "iterator/iter_resptype.h" +#include "iterator/iter_scrub.h" +#include "iterator/iter_priv.h" +#include "validator/val_neg.h" +#include "services/cache/dns.h" +#include "services/cache/infra.h" +#include "util/module.h" +#include "util/netevent.h" +#include "util/net_help.h" +#include "util/regional.h" +#include "util/data/dname.h" +#include "util/data/msgencode.h" +#include "util/fptr_wlist.h" +#include "util/config_file.h" +#include "ldns/rrdef.h" +#include "ldns/wire2str.h" +#include "ldns/parseutil.h" +#include "ldns/sbuffer.h" + +int +iter_init(struct module_env* env, int id) +{ + struct iter_env* iter_env = (struct iter_env*)calloc(1, + sizeof(struct iter_env)); + if(!iter_env) { + log_err("malloc failure"); + return 0; + } + env->modinfo[id] = (void*)iter_env; + if(!iter_apply_cfg(iter_env, env->cfg)) { + log_err("iterator: could not apply configuration settings."); + return 0; + } + return 1; +} + +void +iter_deinit(struct module_env* env, int id) +{ + struct iter_env* iter_env; + if(!env || !env->modinfo[id]) + return; + iter_env = (struct iter_env*)env->modinfo[id]; + free(iter_env->target_fetch_policy); + priv_delete(iter_env->priv); + donotq_delete(iter_env->donotq); + free(iter_env); + env->modinfo[id] = NULL; +} + +/** new query for iterator */ +static int +iter_new(struct module_qstate* qstate, int id) +{ + struct iter_qstate* iq = (struct iter_qstate*)regional_alloc( + qstate->region, sizeof(struct iter_qstate)); + qstate->minfo[id] = iq; + if(!iq) + return 0; + memset(iq, 0, sizeof(*iq)); + iq->state = INIT_REQUEST_STATE; + iq->final_state = FINISHED_STATE; + iq->an_prepend_list = NULL; + iq->an_prepend_last = NULL; + iq->ns_prepend_list = NULL; + iq->ns_prepend_last = NULL; + iq->dp = NULL; + iq->depth = 0; + iq->num_target_queries = 0; + iq->num_current_queries = 0; + iq->query_restart_count = 0; + iq->referral_count = 0; + iq->sent_count = 0; + iq->wait_priming_stub = 0; + iq->refetch_glue = 0; + iq->dnssec_expected = 0; + iq->dnssec_lame_query = 0; + iq->chase_flags = qstate->query_flags; + /* Start with the (current) qname. */ + iq->qchase = qstate->qinfo; + outbound_list_init(&iq->outlist); + return 1; +} + +/** + * Transition to the next state. This can be used to advance a currently + * processing event. It cannot be used to reactivate a forEvent. + * + * @param iq: iterator query state + * @param nextstate The state to transition to. + * @return true. This is so this can be called as the return value for the + * actual process*State() methods. (Transitioning to the next state + * implies further processing). + */ +static int +next_state(struct iter_qstate* iq, enum iter_state nextstate) +{ + /* If transitioning to a "response" state, make sure that there is a + * response */ + if(iter_state_is_responsestate(nextstate)) { + if(iq->response == NULL) { + log_err("transitioning to response state sans " + "response."); + } + } + iq->state = nextstate; + return 1; +} + +/** + * Transition an event to its final state. Final states always either return + * a result up the module chain, or reactivate a dependent event. Which + * final state to transtion to is set in the module state for the event when + * it was created, and depends on the original purpose of the event. + * + * The response is stored in the qstate->buf buffer. + * + * @param iq: iterator query state + * @return false. This is so this method can be used as the return value for + * the processState methods. (Transitioning to the final state + */ +static int +final_state(struct iter_qstate* iq) +{ + return next_state(iq, iq->final_state); +} + +/** + * Callback routine to handle errors in parent query states + * @param qstate: query state that failed. + * @param id: module id. + * @param super: super state. + */ +static void +error_supers(struct module_qstate* qstate, int id, struct module_qstate* super) +{ + struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id]; + + if(qstate->qinfo.qtype == LDNS_RR_TYPE_A || + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) { + /* mark address as failed. */ + struct delegpt_ns* dpns = NULL; + if(super_iq->dp) + dpns = delegpt_find_ns(super_iq->dp, + qstate->qinfo.qname, qstate->qinfo.qname_len); + if(!dpns) { + /* not interested */ + verbose(VERB_ALGO, "subq error, but not interested"); + log_query_info(VERB_ALGO, "superq", &super->qinfo); + if(super_iq->dp) + delegpt_log(VERB_ALGO, super_iq->dp); + log_assert(0); + return; + } else { + /* see if the failure did get (parent-lame) info */ + if(!cache_fill_missing(super->env, + super_iq->qchase.qclass, super->region, + super_iq->dp)) + log_err("out of memory adding missing"); + } + dpns->resolved = 1; /* mark as failed */ + super_iq->num_target_queries--; + } + if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) { + /* prime failed to get delegation */ + super_iq->dp = NULL; + } + /* evaluate targets again */ + super_iq->state = QUERYTARGETS_STATE; + /* super becomes runnable, and will process this change */ +} + +/** + * Return an error to the client + * @param qstate: our query state + * @param id: module id + * @param rcode: error code (DNS errcode). + * @return: 0 for use by caller, to make notation easy, like: + * return error_response(..). + */ +static int +error_response(struct module_qstate* qstate, int id, int rcode) +{ + verbose(VERB_QUERY, "return error response %s", + sldns_lookup_by_id(sldns_rcodes, rcode)? + sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??"); + qstate->return_rcode = rcode; + qstate->return_msg = NULL; + qstate->ext_state[id] = module_finished; + return 0; +} + +/** + * Return an error to the client and cache the error code in the + * message cache (so per qname, qtype, qclass). + * @param qstate: our query state + * @param id: module id + * @param rcode: error code (DNS errcode). + * @return: 0 for use by caller, to make notation easy, like: + * return error_response(..). + */ +static int +error_response_cache(struct module_qstate* qstate, int id, int rcode) +{ + /* store in cache */ + struct reply_info err; + if(qstate->prefetch_leeway > NORR_TTL) { + verbose(VERB_ALGO, "error response for prefetch in cache"); + /* attempt to adjust the cache entry prefetch */ + if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo, + NORR_TTL)) + return error_response(qstate, id, rcode); + /* if that fails (not in cache), fall through to store err */ + } + memset(&err, 0, sizeof(err)); + err.flags = (uint16_t)(BIT_QR | BIT_RA); + FLAGS_SET_RCODE(err.flags, rcode); + err.qdcount = 1; + err.ttl = NORR_TTL; + err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl); + /* do not waste time trying to validate this servfail */ + err.security = sec_status_indeterminate; + verbose(VERB_ALGO, "store error response in message cache"); + iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL); + return error_response(qstate, id, rcode); +} + +/** check if prepend item is duplicate item */ +static int +prepend_is_duplicate(struct ub_packed_rrset_key** sets, size_t to, + struct ub_packed_rrset_key* dup) +{ + size_t i; + for(i=0; irk.type == dup->rk.type && + sets[i]->rk.rrset_class == dup->rk.rrset_class && + sets[i]->rk.dname_len == dup->rk.dname_len && + query_dname_compare(sets[i]->rk.dname, dup->rk.dname) + == 0) + return 1; + } + return 0; +} + +/** prepend the prepend list in the answer and authority section of dns_msg */ +static int +iter_prepend(struct iter_qstate* iq, struct dns_msg* msg, + struct regional* region) +{ + struct iter_prep_list* p; + struct ub_packed_rrset_key** sets; + size_t num_an = 0, num_ns = 0;; + for(p = iq->an_prepend_list; p; p = p->next) + num_an++; + for(p = iq->ns_prepend_list; p; p = p->next) + num_ns++; + if(num_an + num_ns == 0) + return 1; + verbose(VERB_ALGO, "prepending %d rrsets", (int)num_an + (int)num_ns); + sets = regional_alloc(region, (num_an+num_ns+msg->rep->rrset_count) * + sizeof(struct ub_packed_rrset_key*)); + if(!sets) + return 0; + /* ANSWER section */ + num_an = 0; + for(p = iq->an_prepend_list; p; p = p->next) { + sets[num_an++] = p->rrset; + } + memcpy(sets+num_an, msg->rep->rrsets, msg->rep->an_numrrsets * + sizeof(struct ub_packed_rrset_key*)); + /* AUTH section */ + num_ns = 0; + for(p = iq->ns_prepend_list; p; p = p->next) { + if(prepend_is_duplicate(sets+msg->rep->an_numrrsets+num_an, + num_ns, p->rrset) || prepend_is_duplicate( + msg->rep->rrsets+msg->rep->an_numrrsets, + msg->rep->ns_numrrsets, p->rrset)) + continue; + sets[msg->rep->an_numrrsets + num_an + num_ns++] = p->rrset; + } + memcpy(sets + num_an + msg->rep->an_numrrsets + num_ns, + msg->rep->rrsets + msg->rep->an_numrrsets, + (msg->rep->ns_numrrsets + msg->rep->ar_numrrsets) * + sizeof(struct ub_packed_rrset_key*)); + + /* NXDOMAIN rcode can stay if we prepended DNAME/CNAMEs, because + * this is what recursors should give. */ + msg->rep->rrset_count += num_an + num_ns; + msg->rep->an_numrrsets += num_an; + msg->rep->ns_numrrsets += num_ns; + msg->rep->rrsets = sets; + return 1; +} + +/** + * Add rrset to ANSWER prepend list + * @param qstate: query state. + * @param iq: iterator query state. + * @param rrset: rrset to add. + * @return false on failure (malloc). + */ +static int +iter_add_prepend_answer(struct module_qstate* qstate, struct iter_qstate* iq, + struct ub_packed_rrset_key* rrset) +{ + struct iter_prep_list* p = (struct iter_prep_list*)regional_alloc( + qstate->region, sizeof(struct iter_prep_list)); + if(!p) + return 0; + p->rrset = rrset; + p->next = NULL; + /* add at end */ + if(iq->an_prepend_last) + iq->an_prepend_last->next = p; + else iq->an_prepend_list = p; + iq->an_prepend_last = p; + return 1; +} + +/** + * Add rrset to AUTHORITY prepend list + * @param qstate: query state. + * @param iq: iterator query state. + * @param rrset: rrset to add. + * @return false on failure (malloc). + */ +static int +iter_add_prepend_auth(struct module_qstate* qstate, struct iter_qstate* iq, + struct ub_packed_rrset_key* rrset) +{ + struct iter_prep_list* p = (struct iter_prep_list*)regional_alloc( + qstate->region, sizeof(struct iter_prep_list)); + if(!p) + return 0; + p->rrset = rrset; + p->next = NULL; + /* add at end */ + if(iq->ns_prepend_last) + iq->ns_prepend_last->next = p; + else iq->ns_prepend_list = p; + iq->ns_prepend_last = p; + return 1; +} + +/** + * Given a CNAME response (defined as a response containing a CNAME or DNAME + * that does not answer the request), process the response, modifying the + * state as necessary. This follows the CNAME/DNAME chain and returns the + * final query name. + * + * sets the new query name, after following the CNAME/DNAME chain. + * @param qstate: query state. + * @param iq: iterator query state. + * @param msg: the response. + * @param mname: returned target new query name. + * @param mname_len: length of mname. + * @return false on (malloc) error. + */ +static int +handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq, + struct dns_msg* msg, uint8_t** mname, size_t* mname_len) +{ + size_t i; + /* Start with the (current) qname. */ + *mname = iq->qchase.qname; + *mname_len = iq->qchase.qname_len; + + /* Iterate over the ANSWER rrsets in order, looking for CNAMEs and + * DNAMES. */ + for(i=0; irep->an_numrrsets; i++) { + struct ub_packed_rrset_key* r = msg->rep->rrsets[i]; + /* If there is a (relevant) DNAME, add it to the list. + * We always expect there to be CNAME that was generated + * by this DNAME following, so we don't process the DNAME + * directly. */ + if(ntohs(r->rk.type) == LDNS_RR_TYPE_DNAME && + dname_strict_subdomain_c(*mname, r->rk.dname)) { + if(!iter_add_prepend_answer(qstate, iq, r)) + return 0; + continue; + } + + if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME && + query_dname_compare(*mname, r->rk.dname) == 0) { + /* Add this relevant CNAME rrset to the prepend list.*/ + if(!iter_add_prepend_answer(qstate, iq, r)) + return 0; + get_cname_target(r, mname, mname_len); + } + + /* Other rrsets in the section are ignored. */ + } + /* add authority rrsets to authority prepend, for wildcarded CNAMEs */ + for(i=msg->rep->an_numrrsets; irep->an_numrrsets + + msg->rep->ns_numrrsets; i++) { + struct ub_packed_rrset_key* r = msg->rep->rrsets[i]; + /* only add NSEC/NSEC3, as they may be needed for validation */ + if(ntohs(r->rk.type) == LDNS_RR_TYPE_NSEC || + ntohs(r->rk.type) == LDNS_RR_TYPE_NSEC3) { + if(!iter_add_prepend_auth(qstate, iq, r)) + return 0; + } + } + return 1; +} + +/** + * Generate a subrequest. + * Generate a local request event. Local events are tied to this module, and + * have a correponding (first tier) event that is waiting for this event to + * resolve to continue. + * + * @param qname The query name for this request. + * @param qnamelen length of qname + * @param qtype The query type for this request. + * @param qclass The query class for this request. + * @param qstate The event that is generating this event. + * @param id: module id. + * @param iq: The iterator state that is generating this event. + * @param initial_state The initial response state (normally this + * is QUERY_RESP_STATE, unless it is known that the request won't + * need iterative processing + * @param finalstate The final state for the response to this request. + * @param subq_ret: if newly allocated, the subquerystate, or NULL if it does + * not need initialisation. + * @param v: if true, validation is done on the subquery. + * @return false on error (malloc). + */ +static int +generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, + uint16_t qclass, struct module_qstate* qstate, int id, + struct iter_qstate* iq, enum iter_state initial_state, + enum iter_state finalstate, struct module_qstate** subq_ret, int v) +{ + struct module_qstate* subq = NULL; + struct iter_qstate* subiq = NULL; + uint16_t qflags = 0; /* OPCODE QUERY, no flags */ + struct query_info qinf; + int prime = (finalstate == PRIME_RESP_STATE)?1:0; + qinf.qname = qname; + qinf.qname_len = qnamelen; + qinf.qtype = qtype; + qinf.qclass = qclass; + + /* RD should be set only when sending the query back through the INIT + * state. */ + if(initial_state == INIT_REQUEST_STATE) + qflags |= BIT_RD; + /* We set the CD flag so we can send this through the "head" of + * the resolution chain, which might have a validator. We are + * uninterested in validating things not on the direct resolution + * path. */ + if(!v) + qflags |= BIT_CD; + + /* attach subquery, lookup existing or make a new one */ + fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); + if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, &subq)) { + return 0; + } + *subq_ret = subq; + if(subq) { + /* initialise the new subquery */ + subq->curmod = id; + subq->ext_state[id] = module_state_initial; + subq->minfo[id] = regional_alloc(subq->region, + sizeof(struct iter_qstate)); + if(!subq->minfo[id]) { + log_err("init subq: out of memory"); + fptr_ok(fptr_whitelist_modenv_kill_sub( + qstate->env->kill_sub)); + (*qstate->env->kill_sub)(subq); + return 0; + } + subiq = (struct iter_qstate*)subq->minfo[id]; + memset(subiq, 0, sizeof(*subiq)); + subiq->num_target_queries = 0; + subiq->num_current_queries = 0; + subiq->depth = iq->depth+1; + outbound_list_init(&subiq->outlist); + subiq->state = initial_state; + subiq->final_state = finalstate; + subiq->qchase = subq->qinfo; + subiq->chase_flags = subq->query_flags; + subiq->refetch_glue = 0; + } + return 1; +} + +/** + * Generate and send a root priming request. + * @param qstate: the qtstate that triggered the need to prime. + * @param iq: iterator query state. + * @param id: module id. + * @param qclass: the class to prime. + * @return 0 on failure + */ +static int +prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id, + uint16_t qclass) +{ + struct delegpt* dp; + struct module_qstate* subq; + verbose(VERB_DETAIL, "priming . %s NS", + sldns_lookup_by_id(sldns_rr_classes, (int)qclass)? + sldns_lookup_by_id(sldns_rr_classes, (int)qclass)->name:"??"); + dp = hints_lookup_root(qstate->env->hints, qclass); + if(!dp) { + verbose(VERB_ALGO, "Cannot prime due to lack of hints"); + return 0; + } + /* Priming requests start at the QUERYTARGETS state, skipping + * the normal INIT state logic (which would cause an infloop). */ + if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, + qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE, + &subq, 0)) { + verbose(VERB_ALGO, "could not prime root"); + return 0; + } + if(subq) { + struct iter_qstate* subiq = + (struct iter_qstate*)subq->minfo[id]; + /* Set the initial delegation point to the hint. + * copy dp, it is now part of the root prime query. + * dp was part of in the fixed hints structure. */ + subiq->dp = delegpt_copy(dp, subq->region); + if(!subiq->dp) { + log_err("out of memory priming root, copydp"); + fptr_ok(fptr_whitelist_modenv_kill_sub( + qstate->env->kill_sub)); + (*qstate->env->kill_sub)(subq); + return 0; + } + /* there should not be any target queries. */ + subiq->num_target_queries = 0; + subiq->dnssec_expected = iter_indicates_dnssec( + qstate->env, subiq->dp, NULL, subq->qinfo.qclass); + } + + /* this module stops, our submodule starts, and does the query. */ + qstate->ext_state[id] = module_wait_subquery; + return 1; +} + +/** + * Generate and process a stub priming request. This method tests for the + * need to prime a stub zone, so it is safe to call for every request. + * + * @param qstate: the qtstate that triggered the need to prime. + * @param iq: iterator query state. + * @param id: module id. + * @param qname: request name. + * @param qclass: request class. + * @return true if a priming subrequest was made, false if not. The will only + * issue a priming request if it detects an unprimed stub. + * Uses value of 2 to signal during stub-prime in root-prime situation + * that a noprime-stub is available and resolution can continue. + */ +static int +prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, + uint8_t* qname, uint16_t qclass) +{ + /* Lookup the stub hint. This will return null if the stub doesn't + * need to be re-primed. */ + struct iter_hints_stub* stub; + struct delegpt* stub_dp; + struct module_qstate* subq; + + if(!qname) return 0; + stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp); + /* The stub (if there is one) does not need priming. */ + if(!stub) + return 0; + stub_dp = stub->dp; + + /* is it a noprime stub (always use) */ + if(stub->noprime) { + int r = 0; + if(iq->dp == NULL) r = 2; + /* copy the dp out of the fixed hints structure, so that + * it can be changed when servicing this query */ + iq->dp = delegpt_copy(stub_dp, qstate->region); + if(!iq->dp) { + log_err("out of memory priming stub"); + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); + return 1; /* return 1 to make module stop, with error */ + } + log_nametypeclass(VERB_DETAIL, "use stub", stub_dp->name, + LDNS_RR_TYPE_NS, qclass); + return r; + } + + /* Otherwise, we need to (re)prime the stub. */ + log_nametypeclass(VERB_DETAIL, "priming stub", stub_dp->name, + LDNS_RR_TYPE_NS, qclass); + + /* Stub priming events start at the QUERYTARGETS state to avoid the + * redundant INIT state processing. */ + if(!generate_sub_request(stub_dp->name, stub_dp->namelen, + LDNS_RR_TYPE_NS, qclass, qstate, id, iq, + QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0)) { + verbose(VERB_ALGO, "could not prime stub"); + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); + return 1; /* return 1 to make module stop, with error */ + } + if(subq) { + struct iter_qstate* subiq = + (struct iter_qstate*)subq->minfo[id]; + + /* Set the initial delegation point to the hint. */ + /* make copy to avoid use of stub dp by different qs/threads */ + subiq->dp = delegpt_copy(stub_dp, subq->region); + if(!subiq->dp) { + log_err("out of memory priming stub, copydp"); + fptr_ok(fptr_whitelist_modenv_kill_sub( + qstate->env->kill_sub)); + (*qstate->env->kill_sub)(subq); + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); + return 1; /* return 1 to make module stop, with error */ + } + /* there should not be any target queries -- although there + * wouldn't be anyway, since stub hints never have + * missing targets. */ + subiq->num_target_queries = 0; + subiq->wait_priming_stub = 1; + subiq->dnssec_expected = iter_indicates_dnssec( + qstate->env, subiq->dp, NULL, subq->qinfo.qclass); + } + + /* this module stops, our submodule starts, and does the query. */ + qstate->ext_state[id] = module_wait_subquery; + return 1; +} + +/** + * Generate A and AAAA checks for glue that is in-zone for the referral + * we just got to obtain authoritative information on the adresses. + * + * @param qstate: the qtstate that triggered the need to prime. + * @param iq: iterator query state. + * @param id: module id. + */ +static void +generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq, + int id) +{ + struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id]; + struct module_qstate* subq; + size_t i; + struct reply_info* rep = iq->response->rep; + struct ub_packed_rrset_key* s; + log_assert(iq->dp); + + if(iq->depth == ie->max_dependency_depth) + return; + /* walk through additional, and check if in-zone, + * only relevant A, AAAA are left after scrub anyway */ + for(i=rep->an_numrrsets+rep->ns_numrrsets; irrset_count; i++) { + s = rep->rrsets[i]; + /* check *ALL* addresses that are transmitted in additional*/ + /* is it an address ? */ + if( !(ntohs(s->rk.type)==LDNS_RR_TYPE_A || + ntohs(s->rk.type)==LDNS_RR_TYPE_AAAA)) { + continue; + } + /* is this query the same as the A/AAAA check for it */ + if(qstate->qinfo.qtype == ntohs(s->rk.type) && + qstate->qinfo.qclass == ntohs(s->rk.rrset_class) && + query_dname_compare(qstate->qinfo.qname, + s->rk.dname)==0 && + (qstate->query_flags&BIT_RD) && + !(qstate->query_flags&BIT_CD)) + continue; + + /* generate subrequest for it */ + log_nametypeclass(VERB_ALGO, "schedule addr fetch", + s->rk.dname, ntohs(s->rk.type), + ntohs(s->rk.rrset_class)); + if(!generate_sub_request(s->rk.dname, s->rk.dname_len, + ntohs(s->rk.type), ntohs(s->rk.rrset_class), + qstate, id, iq, + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) { + verbose(VERB_ALGO, "could not generate addr check"); + return; + } + /* ignore subq - not need for more init */ + } +} + +/** + * Generate a NS check request to obtain authoritative information + * on an NS rrset. + * + * @param qstate: the qtstate that triggered the need to prime. + * @param iq: iterator query state. + * @param id: module id. + */ +static void +generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id) +{ + struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id]; + struct module_qstate* subq; + log_assert(iq->dp); + + if(iq->depth == ie->max_dependency_depth) + return; + /* is this query the same as the nscheck? */ + if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS && + query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 && + (qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){ + /* spawn off A, AAAA queries for in-zone glue to check */ + generate_a_aaaa_check(qstate, iq, id); + return; + } + + log_nametypeclass(VERB_ALGO, "schedule ns fetch", + iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass); + if(!generate_sub_request(iq->dp->name, iq->dp->namelen, + LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq, + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) { + verbose(VERB_ALGO, "could not generate ns check"); + return; + } + if(subq) { + struct iter_qstate* subiq = + (struct iter_qstate*)subq->minfo[id]; + + /* make copy to avoid use of stub dp by different qs/threads */ + /* refetch glue to start higher up the tree */ + subiq->refetch_glue = 1; + subiq->dp = delegpt_copy(iq->dp, subq->region); + if(!subiq->dp) { + log_err("out of memory generating ns check, copydp"); + fptr_ok(fptr_whitelist_modenv_kill_sub( + qstate->env->kill_sub)); + (*qstate->env->kill_sub)(subq); + return; + } + } +} + +/** + * Generate a DNSKEY prefetch query to get the DNSKEY for the DS record we + * just got in a referral (where we have dnssec_expected, thus have trust + * anchors above it). Note that right after calling this routine the + * iterator detached subqueries (because of following the referral), and thus + * the DNSKEY query becomes detached, its return stored in the cache for + * later lookup by the validator. This cache lookup by the validator avoids + * the roundtrip incurred by the DNSKEY query. The DNSKEY query is now + * performed at about the same time the original query is sent to the domain, + * thus the two answers are likely to be returned at about the same time, + * saving a roundtrip from the validated lookup. + * + * @param qstate: the qtstate that triggered the need to prime. + * @param iq: iterator query state. + * @param id: module id. + */ +static void +generate_dnskey_prefetch(struct module_qstate* qstate, + struct iter_qstate* iq, int id) +{ + struct module_qstate* subq; + log_assert(iq->dp); + + /* is this query the same as the prefetch? */ + if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY && + query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 && + (qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){ + return; + } + + /* if the DNSKEY is in the cache this lookup will stop quickly */ + log_nametypeclass(VERB_ALGO, "schedule dnskey prefetch", + iq->dp->name, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass); + if(!generate_sub_request(iq->dp->name, iq->dp->namelen, + LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass, qstate, id, iq, + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) { + /* we'll be slower, but it'll work */ + verbose(VERB_ALGO, "could not generate dnskey prefetch"); + return; + } + if(subq) { + struct iter_qstate* subiq = + (struct iter_qstate*)subq->minfo[id]; + /* this qstate has the right delegation for the dnskey lookup*/ + /* make copy to avoid use of stub dp by different qs/threads */ + subiq->dp = delegpt_copy(iq->dp, subq->region); + /* if !subiq->dp, it'll start from the cache, no problem */ + } +} + +/** + * See if the query needs forwarding. + * + * @param qstate: query state. + * @param iq: iterator query state. + * @return true if the request is forwarded, false if not. + * If returns true but, iq->dp is NULL then a malloc failure occurred. + */ +static int +forward_request(struct module_qstate* qstate, struct iter_qstate* iq) +{ + struct delegpt* dp; + uint8_t* delname = iq->qchase.qname; + size_t delnamelen = iq->qchase.qname_len; + if(iq->refetch_glue) { + delname = iq->dp->name; + delnamelen = iq->dp->namelen; + } + /* strip one label off of DS query to lookup higher for it */ + if( (iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) + && !dname_is_root(iq->qchase.qname)) + dname_remove_label(&delname, &delnamelen); + dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass); + if(!dp) + return 0; + /* send recursion desired to forward addr */ + iq->chase_flags |= BIT_RD; + iq->dp = delegpt_copy(dp, qstate->region); + /* iq->dp checked by caller */ + verbose(VERB_ALGO, "forwarding request"); + return 1; +} + +/** + * Process the initial part of the request handling. This state roughly + * corresponds to resolver algorithms steps 1 (find answer in cache) and 2 + * (find the best servers to ask). + * + * Note that all requests start here, and query restarts revisit this state. + * + * This state either generates: 1) a response, from cache or error, 2) a + * priming event, or 3) forwards the request to the next state (init2, + * generally). + * + * @param qstate: query state. + * @param iq: iterator query state. + * @param ie: iterator shared global environment. + * @param id: module id. + * @return true if the event needs more request processing immediately, + * false if not. + */ +static int +processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, + struct iter_env* ie, int id) +{ + uint8_t* delname; + size_t delnamelen; + struct dns_msg* msg; + + log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo); + /* check effort */ + + /* We enforce a maximum number of query restarts. This is primarily a + * cheap way to prevent CNAME loops. */ + if(iq->query_restart_count > MAX_RESTART_COUNT) { + verbose(VERB_QUERY, "request has exceeded the maximum number" + " of query restarts with %d", iq->query_restart_count); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + + /* We enforce a maximum recursion/dependency depth -- in general, + * this is unnecessary for dependency loops (although it will + * catch those), but it provides a sensible limit to the amount + * of work required to answer a given query. */ + verbose(VERB_ALGO, "request has dependency depth of %d", iq->depth); + if(iq->depth > ie->max_dependency_depth) { + verbose(VERB_QUERY, "request has exceeded the maximum " + "dependency depth with depth of %d", iq->depth); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + + /* If the request is qclass=ANY, setup to generate each class */ + if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) { + iq->qchase.qclass = 0; + return next_state(iq, COLLECT_CLASS_STATE); + } + + /* Resolver Algorithm Step 1 -- Look for the answer in local data. */ + + /* This either results in a query restart (CNAME cache response), a + * terminating response (ANSWER), or a cache miss (null). */ + + if(qstate->blacklist) { + /* if cache, or anything else, was blacklisted then + * getting older results from cache is a bad idea, no cache */ + verbose(VERB_ALGO, "cache blacklisted, going to the network"); + msg = NULL; + } else { + msg = dns_cache_lookup(qstate->env, iq->qchase.qname, + iq->qchase.qname_len, iq->qchase.qtype, + iq->qchase.qclass, qstate->region, qstate->env->scratch); + if(!msg && qstate->env->neg_cache) { + /* lookup in negative cache; may result in + * NOERROR/NODATA or NXDOMAIN answers that need validation */ + msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase, + qstate->region, qstate->env->rrset_cache, + qstate->env->scratch_buffer, + *qstate->env->now, 1/*add SOA*/, NULL); + } + /* item taken from cache does not match our query name, thus + * security needs to be re-examined later */ + if(msg && query_dname_compare(qstate->qinfo.qname, + iq->qchase.qname) != 0) + msg->rep->security = sec_status_unchecked; + } + if(msg) { + /* handle positive cache response */ + enum response_type type = response_type_from_cache(msg, + &iq->qchase); + if(verbosity >= VERB_ALGO) { + log_dns_msg("msg from cache lookup", &msg->qinfo, + msg->rep); + verbose(VERB_ALGO, "msg ttl is %d, prefetch ttl %d", + (int)msg->rep->ttl, + (int)msg->rep->prefetch_ttl); + } + + if(type == RESPONSE_TYPE_CNAME) { + uint8_t* sname = 0; + size_t slen = 0; + verbose(VERB_ALGO, "returning CNAME response from " + "cache"); + if(!handle_cname_response(qstate, iq, msg, + &sname, &slen)) + return error_response(qstate, id, + LDNS_RCODE_SERVFAIL); + iq->qchase.qname = sname; + iq->qchase.qname_len = slen; + /* This *is* a query restart, even if it is a cheap + * one. */ + iq->dp = NULL; + iq->refetch_glue = 0; + iq->query_restart_count++; + iq->sent_count = 0; + sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region); + return next_state(iq, INIT_REQUEST_STATE); + } + + /* if from cache, NULL, else insert 'cache IP' len=0 */ + if(qstate->reply_origin) + sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region); + /* it is an answer, response, to final state */ + verbose(VERB_ALGO, "returning answer from cache."); + iq->response = msg; + return final_state(iq); + } + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***