From: Wouter Wijngaards Date: Wed, 30 May 2007 11:18:17 +0000 (+0000) Subject: iterator work. X-Git-Tag: release-0.4~118 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=635461389c181ed12c989f7149f6e499c0320e39;p=thirdparty%2Funbound.git iterator work. git-svn-id: file:///svn/unbound/trunk@348 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index f0e4f3c62..0989c2f8e 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -97,6 +97,8 @@ qstate_free(struct worker* worker, struct module_qstate* qstate) if(qstate->parent) { module_subreq_remove(qstate); free(qstate); + } else { + region_destroy(qstate->region); } } @@ -159,40 +161,66 @@ replyerror(int r, struct work_query* w) query_info_clear(&w->state.qinfo); } +/** init qstate module states */ +static void +set_extstates_initial(struct worker* worker, struct module_qstate* qstate) +{ + int i; + for(i=0; idaemon->num_modules; i++) + qstate->ext_state[i] = module_state_initial; +} + /** process incoming request */ static void worker_process_query(struct worker* worker, struct work_query* w, struct module_qstate* qstate, enum module_ev event, struct outbound_entry* entry) { - int i; enum module_ext_state s; if(event == module_event_new) { qstate->curmod = 0; - for(i=0; idaemon->num_modules; i++) - qstate->ext_state[i] = module_state_initial; + set_extstates_initial(worker, qstate); } /* allow current module to run */ - (*worker->daemon->modfunc[qstate->curmod]->operate)(qstate, event, - qstate->curmod, entry); - s = qstate->ext_state[qstate->curmod]; - /* TODO examine results, start further modules, etc. - * assume it went to sleep - */ - region_free_all(worker->scratchpad); - /* subrequest done */ - /* TODO properly delete subquery */ - if(s == module_error && qstate->parent) { - qstate_free(worker, qstate); - worker_process_query(worker, w, qstate->parent, - module_event_error, NULL); - return; - } - if(s == module_finished && qstate->parent) { - qstate_free(worker, qstate); - worker_process_query(worker, w, qstate->parent, - module_event_subq_done, NULL); - return; + /* loops for subqueries or parent queries. */ + while(1) { + (*worker->daemon->modfunc[qstate->curmod]->operate)(qstate, + event, qstate->curmod, entry); + region_free_all(worker->scratchpad); + s = qstate->ext_state[qstate->curmod]; + /* examine results, start further modules, etc. */ + if(s == module_wait_subquery) { + if(!qstate->subquery_first) { + log_err("module exit wait subq, but no subq"); + s = module_error; + } else { + /* start submodule */ + qstate = qstate->subquery_first; + set_extstates_initial(worker, qstate); + event = module_event_pass; + entry = NULL; + continue; + } + } + + /* subrequest done */ + if(s == module_error && qstate->parent) { + struct module_qstate* up = qstate->parent; + qstate_free(worker, qstate); + qstate = up; + entry = NULL; + event = module_event_error; + continue; + } + if(s == module_finished && qstate->parent) { + struct module_qstate* up = qstate->parent; + qstate_free(worker, qstate); + qstate = up; + entry = NULL; + event = module_event_subq_done; + continue; + } + break; } /* request done */ if(s == module_error) { diff --git a/doc/Changelog b/doc/Changelog index 1192c876f..764064f8f 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,10 @@ - removed FLAG_CD from message and rrset caches. This was useful for an agnostic forwarder, but not for a sophisticated (trust value per rrset enabled) cache. + - iterator reponse typing. + - iterator cname handle. + - iterator prime start. + - subquery work. 29 May 2007: Wouter - routines to lock and unlock array of rrsets moved to cache/rrset. diff --git a/iterator/iter_resptype.c b/iterator/iter_resptype.c new file mode 100644 index 000000000..5101ce52e --- /dev/null +++ b/iterator/iter_resptype.c @@ -0,0 +1,99 @@ +/* + * iterator/iter_resptype.c - response type information and classification. + * + * 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 REGENTS 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 defines the response type. DNS Responses can be classified as + * one of the response types. + */ +#include "config.h" +#include "iterator/iter_resptype.h" +#include "services/cache/dns.h" +#include "util/net_help.h" +#include "util/data/dname.h" + +enum response_type +response_type_from_cache(struct dns_msg* msg, + struct query_info* request) +{ + /* If the message is NXDOMAIN, then it is an ANSWER. */ + if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) + return RESPONSE_TYPE_ANSWER; + + /* First we look at the answer section. This can tell us if this is + * CNAME or positive ANSWER. */ + if(msg->rep->an_numrrsets > 0) { + /* Now look at the answer section first. 3 states: + * o our answer is there directly, + * o our answer is there after a cname, + * o or there is just a cname. */ + uint8_t* mname = request->qname; + size_t mname_len = request->qname_len; + size_t i; + for(i=0; irep->an_numrrsets; i++) { + struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; + + /* If we have encountered an answer (before or + * after a CNAME), then we are done! Note that + * if qtype == CNAME then this will be noted as + * an ANSWER before it gets treated as a CNAME, + * as it should */ + if(ntohs(s->rk.type) == request->qtype && + ntohs(s->rk.rrset_class) == request->qclass && + query_dname_compare(mname, s->rk.dname) == 0) { + return RESPONSE_TYPE_ANSWER; + } + + /* If we have encountered a CNAME, make sure that + * it is relevant. */ + if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && + query_dname_compare(mname, s->rk.dname) == 0) { + get_cname_target(s, &mname, &mname_len); + } + } + + /* if we encountered a CNAME (or a bunch of CNAMEs), and + * still got to here, then it is a CNAME response. (i.e., + * the CNAME chain didn't terminate in an answer rrset.) */ + if(mname != request->qname) { + return RESPONSE_TYPE_CNAME; + } + } + + /* At this point, since we don't need to detect REFERRAL or LAME + * messages, it can only be an ANSWER. */ + return RESPONSE_TYPE_ANSWER; +} diff --git a/iterator/iter_resptype.h b/iterator/iter_resptype.h new file mode 100644 index 000000000..d6e1890f9 --- /dev/null +++ b/iterator/iter_resptype.h @@ -0,0 +1,100 @@ +/* + * iterator/iter_resptype.h - response type information and classification. + * + * 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 REGENTS 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 defines the response type. DNS Responses can be classified as + * one of the response types. + */ + +#ifndef ITERATOR_ITER_RESPTYPE_H +#define ITERATOR_ITER_RESPTYPE_H +struct dns_msg; +struct query_info; + +/** + * The response type is used to interpret the response. + */ +enum response_type { + /** + * 'untyped' means that the type of this response hasn't been + * assigned. + */ + RESPONSE_TYPE_UNTYPED = 0, + + /** + * 'answer' means that the response terminates the resolution + * process. + */ + RESPONSE_TYPE_ANSWER, + + /** 'delegation' means that the response is a delegation. */ + RESPONSE_TYPE_REFERRAL, + + /** + * 'cname' means that the response is a cname without the final + * answer, and thus must be restarted. + */ + RESPONSE_TYPE_CNAME, + + /** + * 'throwaway' means that this particular response should be + * discarded and the next nameserver should be contacted + */ + RESPONSE_TYPE_THROWAWAY, + + /** + * 'lame' means that this particular response indicates that + * the nameserver knew nothing about the question. + */ + RESPONSE_TYPE_LAME +}; + +/** + * Classifies a response message from cache based on the current request. + * Note that this routine assumes that THROWAWAY or LAME responses will not + * occur. Also, it will not detect REFERRAL type messages, since those are + * (currently) automatically classified based on how they came from the + * cache (findDelegation() instead of lookup()). + * + * @param msg: the message from the cache. + * @param request: the request that generated the response. + * @return the response type (CNAME or ANSWER). + */ +enum response_type response_type_from_cache(struct dns_msg* msg, + struct query_info* request); + +#endif /* ITERATOR_ITER_RESPTYPE_H */ diff --git a/iterator/iterator.c b/iterator/iterator.c index 2fca3f9d3..51bcd3630 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -44,11 +44,13 @@ #include "iterator/iterator.h" #include "iterator/iter_utils.h" #include "iterator/iter_hints.h" +#include "iterator/iter_resptype.h" #include "services/cache/dns.h" #include "util/module.h" #include "util/netevent.h" #include "util/net_help.h" #include "util/region-allocator.h" +#include "util/data/dname.h" /** iterator init */ static int @@ -254,6 +256,7 @@ error_response(struct module_qstate* qstate, struct iter_qstate* iq, int rcode) return final_state(qstate, iq); } +#if 0 /** prepend the prepend list in the answer section of dns_msg */ static int iter_prepend(struct iter_qstate* iq, struct dns_msg* msg, @@ -324,6 +327,32 @@ iter_encode_respmsg(struct module_qstate* qstate, struct iter_qstate* iq, return; } } +#endif + +/** + * Add rrset to 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(struct module_qstate* qstate, struct iter_qstate* iq, + struct ub_packed_rrset_key* rrset) +{ + struct iter_prep_list* p = (struct iter_prep_list*)region_alloc( + qstate->region, sizeof(struct iter_prep_list)); + if(!p) + return 0; + p->rrset = rrset; + p->next = NULL; + /* add at end */ + if(iq->prepend_last) + iq->prepend_last->next = p; + else iq->prepend_list = p; + iq->prepend_last = p; + return 1; +} /** * Given a CNAME response (defined as a response containing a CNAME or DNAME @@ -334,15 +363,174 @@ iter_encode_respmsg(struct module_qstate* qstate, struct iter_qstate* iq, * sets the new query name, after following the CNAME/DNAME chain. * @param qstate: query state. * @param iq: iterator query state. - * @param ie: iterator shared global environment. + * @param msg: the response. + * @param mname: returned target new query name. + * @param mname_len: length of mname. + * @return false on (malloc) error. */ -static void +static int handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct dns_msg* msg, uint8_t** mname, size_t* mname_len) { + size_t i; + /* Start with the (current) qname. */ + *mname = qstate->qinfo.qname; + *mname_len = qstate->qinfo.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(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(qstate, iq, r)) + return 0; + get_cname_target(r, mname, mname_len); + } + /* Other rrsets in the section are ignored. */ + } + 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 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 final_state The final state for the response to this + * request. + * @return generated subquerystate, or NULL on error (malloc). + */ +static struct module_qstate* +generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, + uint16_t qclass, struct module_qstate* qstate, int id, + enum iter_state initial_state, enum iter_state final_state) +{ + struct module_qstate* subq = (struct module_qstate*)malloc( + sizeof(struct module_qstate)); + struct iter_qstate* subiq; + if(!subq) + return NULL; + memset(subq, 0, sizeof(*subq)); + subq->qinfo.qname = memdup(qname, qnamelen); + if(!subq->qinfo.qname) { + free(subq); + return NULL; + } + subq->qinfo.qname_len = qnamelen; + subq->qinfo.qtype = qtype; + subq->qinfo.qclass = qclass; + subq->query_hash = query_info_hash(&subq->qinfo); + subq->query_flags = 0; /* OPCODE QUERY, no flags */ + subq->edns.udp_size = 65535; + subq->buf = qstate->buf; + subq->scratch = qstate->scratch; + subq->region = region_create(malloc, free); + if(!subq->region) { + free(subq->qinfo.qname); + free(subq); + return NULL; + } + subq->curmod = id; + subq->ext_state[id] = module_state_initial; + subq->minfo[id] = region_alloc(subq->region, + sizeof(struct iter_qstate)); + if(!subq->minfo[id]) { + region_destroy(subq->region); + free(subq->qinfo.qname); + free(subq); + return NULL; + } + subq->env = qstate->env; + subq->work_info = qstate->work_info; + subq->parent = qstate; + subq->subquery_next = qstate->subquery_first; + qstate->subquery_first = subq; + + subiq = (struct iter_qstate*)subq->minfo[id]; + memset(subiq, 0, sizeof(*subiq)); + subiq->num_target_queries = -1; /* default our targetQueries counter. */ + outbound_list_init(&subiq->outlist); + subiq->state = initial_state; + subiq->final_state = final_state; + + /* RD should be set only when sending the query back through the INIT + * state. */ + if(initial_state == INIT_REQUEST_STATE) + subq->query_flags |= 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. */ + subq->query_flags |= BIT_CD; + + return subq; +} + +/** + * Generate and send a root priming request. + * @param qstate: the qtstate that triggered the need to prime. + * @param ie: iterator global state. + * @param id: module id. + * @param qclass: the class to prime. + */ +static int +prime_root(struct module_qstate* qstate, struct iter_env* ie, int id, + uint16_t qclass) +{ + struct delegpt* dp; + struct module_qstate* subq; + struct iter_qstate* subiq; + verbose(VERB_ALGO, "priming <./%s>", + ldns_lookup_by_id(ldns_rr_classes, (int)qclass)? + ldns_lookup_by_id(ldns_rr_classes, (int)qclass)->name:"??"); + dp = hints_lookup_root(ie->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). */ + subq = generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, + qclass, qstate, id, QUERYTARGETS_STATE, PRIME_RESP_STATE); + if(!subq) { + log_err("out of memory priming root"); + return 0; + } + subiq = (struct iter_qstate*)subq->minfo[id]; + + /* Set the initial delegation point to the hint. */ + subiq->dp = dp; + /* suppress any target queries. */ + subiq->num_target_queries = 0; + + /* this module stops, our submodule starts, and does the query. */ + qstate->ext_state[id] = module_wait_subquery; + return 1; +} /** * Process the initial part of the request handling. This state roughly @@ -358,12 +546,13 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq, * @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) + struct iter_env* ie, int id) { int d; uint8_t* delname; @@ -404,14 +593,25 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, qstate->qinfo.qclass, qstate->region, qstate->scratch); if(msg) { /* handle positive cache response */ - /* - enum response_type type = type_cache_response(msg); + enum response_type type = response_type_from_cache(msg, + &qstate->qinfo); - if(type == RESPONSE_TYPE_CNAME) */ { + if(type == RESPONSE_TYPE_CNAME) { + uint8_t* sname = 0; + size_t slen = 0; verbose(VERB_ALGO, "returning CNAME response from " "cache"); - /* handleCnameresponse &iq->orig_qname, &iq->orig_qname_len */ - /* his *is* a query restart, even if it is a cheap + if(!iq->orig_qname) { + iq->orig_qname = qstate->qinfo.qname; + iq->orig_qnamelen = qstate->qinfo.qname_len; + } + if(!handle_cname_response(qstate, iq, msg, + &sname, &slen)) + return error_response(qstate, iq, + LDNS_RCODE_SERVFAIL); + qstate->qinfo.qname = sname; + qstate->qinfo.qname_len = slen; + /* This *is* a query restart, even if it is a cheap * one. */ iq->query_restart_count++; return next_state(qstate, iq, INIT_REQUEST_STATE); @@ -419,13 +619,39 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, /* it is an answer, response, to final state */ verbose(VERB_ALGO, "returning answer from cache."); - iter_encode_respmsg(qstate, iq, msg); + iq->response = msg; return final_state(qstate, iq); } /* TODO attempt to forward the request */ + /* if (forwardRequest(event, state, req)) + { + // the request has been forwarded. + // forwarded requests need to be immediately sent to the + // next state, QUERYTARGETS. + return nextState(event, req, state, + IterEventState.QUERYTARGETS_STATE); + } + */ /* TODO attempt to find a covering DNAME in the cache */ + /* resp = mDNSCache.findDNAME(req.getQName(), req.getQType(), req + .getQClass()); + if (resp != null) + { +log.trace("returning synthesized CNAME response from cache: " + resp); +Name cname = handleCNAMEResponse(state, req, resp); +// At this point, we just initiate the query restart. +// This might not be a query restart situation (e.g., qtype == CNAME), +// but +// the answer returned from findDNAME() is likely to be one that we +// don't want to return. +// Thus we allow the cache and other resolution mojo kick in regardless. +req.setQName(cname); +state.queryRestartCount++; +return nextState(event, req, state, IterEventState.INIT_REQUEST_STATE); +} + */ /* Resolver Algorithm Step 2 -- find the "best" servers. */ @@ -435,7 +661,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, delname = qstate->qinfo.qname; delnamelen = qstate->qinfo.qname_len; if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS && delname[0] != 0) { - /* do not adjust root label */ + /* do not adjust root label, remove first label from delname */ size_t lablen = delname[0] + 1; delname += lablen; delnamelen -= lablen; @@ -451,11 +677,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, if(iq->dp == NULL) { /* Note that the result of this will set a new * DelegationPoint based on the result of priming. */ - - /* TODO - if(!prime_root(qstate, iq, ie, qstate->qinfo.qclass)) - return error_response(qstate, iq, LDNS_RCODE_SERVFAIL); - */ + if(!prime_root(qstate, ie, id, qstate->qinfo.qclass)) + return error_response(qstate, iq, LDNS_RCODE_REFUSED); /* priming creates an sends a subordinate query, with * this query as the parent. So further processing for @@ -480,7 +703,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, /** TODO */ static int processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { return 0; } @@ -488,7 +711,7 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq, /** TODO */ static int processInitRequest3(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { return 0; } @@ -496,7 +719,7 @@ processInitRequest3(struct module_qstate* qstate, struct iter_qstate* iq, /** TODO */ static int processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { return 0; } @@ -504,7 +727,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /** TODO */ static int processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { return 0; } @@ -512,7 +735,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, /** TODO */ static int processPrimeResponse(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { return 0; } @@ -520,7 +743,7 @@ processPrimeResponse(struct module_qstate* qstate, struct iter_qstate* iq, /** TODO */ static int processTargetResponse(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { return 0; } @@ -528,7 +751,7 @@ processTargetResponse(struct module_qstate* qstate, struct iter_qstate* iq, /** TODO */ static int processFinished(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { return 0; } @@ -544,10 +767,11 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, * @param qstate: query state. * @param ie: iterator shared global environment. * @param iq: iterator query state. + * @param id: module id. */ static void iter_handle(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { int cont = 1; while(cont) { @@ -555,29 +779,29 @@ iter_handle(struct module_qstate* qstate, struct iter_qstate* iq, iter_state_to_string(iq->state)); switch(iq->state) { case INIT_REQUEST_STATE: - cont = processInitRequest(qstate, iq, ie); + cont = processInitRequest(qstate, iq, ie, id); break; #if 0 case INIT_REQUEST_2_STATE: - cont = processInitRequest2(qstate, iq, ie); + cont = processInitRequest2(qstate, iq, ie, id); break; case INIT_REQUEST_3_STATE: - cont = processInitRequest3(qstate, iq, ie); + cont = processInitRequest3(qstate, iq, ie, id); break; case QUERYTARGETS_STATE: - cont = processQueryTargets(qstate, iq, ie); + cont = processQueryTargets(qstate, iq, ie, id); break; case QUERY_RESP_STATE: - cont = processQueryResponse(qstate, iq, ie); + cont = processQueryResponse(qstate, iq, ie, id); break; case PRIME_RESP_STATE: - cont = processPrimeResponse(qstate, iq, ie); + cont = processPrimeResponse(qstate, iq, ie, id); break; case TARGET_RESP_STATE: - cont = processTargetResponse(qstate, iq, ie); + cont = processTargetResponse(qstate, iq, ie, id); break; case FINISHED_STATE: - cont = processFinished(qstate, iq, ie); + cont = processFinished(qstate, iq, ie, id); break; #endif default: @@ -595,28 +819,29 @@ iter_handle(struct module_qstate* qstate, struct iter_qstate* iq, * @param qstate: query state. * @param ie: iterator shared global environment. * @param iq: iterator query state. + * @param id: module id. */ static void process_request(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) + struct iter_env* ie, int id) { /* external requests start in the INIT state, and finish using the * FINISHED state. */ iq->state = INIT_REQUEST_STATE; iq->final_state = FINISHED_STATE; verbose(VERB_ALGO, "process_request: new external request event"); - iter_handle(qstate, iq, ie); + iter_handle(qstate, iq, ie, id); } /** process authoritative server reply */ static void process_response(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie, struct outbound_entry* outbound) + struct iter_env* ie, int id, struct outbound_entry* outbound) { verbose(VERB_ALGO, "process_response: new external response event"); /* TODO outbound: use it for scrubbing and so on */ iq->state = QUERY_RESP_STATE; - iter_handle(qstate, iq, ie); + iter_handle(qstate, iq, ie, id); } /** iterator operate on a query */ @@ -625,7 +850,7 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound) { struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id]; - struct iter_qstate* iq; + struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id]; verbose(VERB_ALGO, "iterator[module %d] operate: extstate:%s event:%s", id, strextstate(qstate->ext_state[id]), strmodulevent(event)); if(ie->fwd_addrlen != 0) { @@ -633,19 +858,22 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id, return; } /* perform iterator state machine */ - if(event == module_event_new) { + if(event == module_event_new && iq == NULL) { log_info("iter state machine"); if(!iter_new(qstate, id)) { qstate->ext_state[id] = module_error; return; } iq = (struct iter_qstate*)qstate->minfo[id]; - process_request(qstate, iq, ie); + process_request(qstate, iq, ie, id); + return; + } + if(event == module_event_pass) { + iter_handle(qstate, iq, ie, id); return; } - iq = (struct iter_qstate*)qstate->minfo[id]; if(event == module_event_reply) { - process_response(qstate, iq, ie, outbound); + process_response(qstate, iq, ie, id, outbound); return; } /* TODO: uhh */ @@ -662,6 +890,11 @@ iter_clear(struct module_qstate* qstate, int id) if(!qstate) return; iq = (struct iter_qstate*)qstate->minfo[id]; + if(iq->orig_qname) { + /* so the correct qname gets free'd */ + qstate->qinfo.qname = iq->orig_qname; + qstate->qinfo.qname_len = iq->orig_qnamelen; + } outbound_list_clear(&iq->outlist); qstate->minfo[id] = NULL; } diff --git a/iterator/iterator.h b/iterator/iterator.h index 6f83eb98c..fa2ea2f05 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -45,8 +45,8 @@ #include "services/outbound_list.h" struct module_func_block; struct delegpt; -struct packed_rrset_list; struct iter_hints; +struct iter_prep_list; /** max number of query restarts. Determines max number of CNAME chain. */ #define MAX_RESTART_COUNT 8 @@ -159,13 +159,18 @@ struct iter_qstate { */ enum iter_state final_state; + /** + * The response + */ + struct dns_msg* response; + /** * This is a list of RRsets that must be prepended to the * ANSWER section of a response before being sent upstream. */ - struct packed_rrset_list* prepend_list; + struct iter_prep_list* prepend_list; /** Last element of the prepend list */ - struct packed_rrset_list* prepend_last; + struct iter_prep_list* prepend_last; /** original query name - if not NULL, malloced and before CNAME */ uint8_t* orig_qname; @@ -209,6 +214,16 @@ struct iter_qstate { struct outbound_list outlist; }; +/** + * List of prepend items + */ +struct iter_prep_list { + /** next in list */ + struct iter_prep_list* next; + /** rrset */ + struct ub_packed_rrset_key* rrset; +}; + /** * Get the iterator function block. * @return: function block with function pointers to iterator methods. diff --git a/services/cache/dns.c b/services/cache/dns.c index fc0a3fef9..526a28131 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -44,6 +44,7 @@ #include "services/cache/rrset.h" #include "util/data/msgreply.h" #include "util/data/packed_rrset.h" +#include "util/data/dname.h" #include "util/module.h" #include "util/net_help.h" #include "util/region-allocator.h" @@ -224,6 +225,9 @@ dns_cache_find_delegation(struct module_env* env, uint8_t* qname, /* add NS entries */ for(i=0; icount; i++) { if(nsdata->rr_len[i] < 2+1) continue; /* len + root label */ + if(dname_valid(nsdata->rr_data[i]+2, nsdata->rr_len[i]-2) != + (size_t)ldns_read_uint16(nsdata->rr_data[i])-2) + continue; /* bad format */ /* add rdata of NS (= wirefmt dname), skip rdatalen bytes */ if(!delegpt_add_ns(dp, region, nsdata->rr_data[i]+2)) log_err("find_delegation: addns out of memory"); diff --git a/util/data/dname.c b/util/data/dname.c index 60c3e7bce..89d1247dd 100644 --- a/util/data/dname.c +++ b/util/data/dname.c @@ -68,6 +68,29 @@ query_dname_len(ldns_buffer* query) } } +size_t +dname_valid(uint8_t* dname, size_t maxlen) +{ + size_t len = 0; + size_t labellen; + labellen = *dname++; + while(labellen) { + if(labellen&0xc0) + return 0; /* no compression ptrs allowed */ + len += labellen + 1; + if(len > LDNS_MAX_DOMAINLEN) + return 0; /* too long */ + if(len > maxlen) + return 0; /* does not fit in memory allocation */ + dname += labellen; + labellen = *dname++; + } + len += 1; + if(len > maxlen) + return 0; /* does not fit in memory allocation */ + return len; +} + int query_dname_compare(uint8_t* d1, uint8_t* d2) { @@ -524,3 +547,9 @@ dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2) return 0; } +int +dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2) +{ + return dname_strict_subdomain(d1, dname_count_labels(d1), d2, + dname_count_labels(d2)); +} diff --git a/util/data/dname.h b/util/data/dname.h index 278ebe16e..7d35cb0bd 100644 --- a/util/data/dname.h +++ b/util/data/dname.h @@ -54,6 +54,14 @@ */ size_t query_dname_len(ldns_buffer* query); +/** + * Determine if dname in memory is correct. no compression ptrs allowed. + * @param dname: where dname starts in memory. + * @param len: dname is not allowed to exceed this length (i.e. of allocation). + * @return length of dname if dname is ok, 0 on a parse error. + */ +size_t dname_valid(uint8_t* dname, size_t len); + /** lowercase query dname */ void query_dname_tolower(uint8_t* dname, size_t len); @@ -165,6 +173,14 @@ int dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs); */ int dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2); +/** + * Like dname_strict_subdomain but counts labels + * @param d1: domain name, uncompressed wireformat + * @param d2: domain name, uncompressed wireformat + * @return true if d1 is a subdomain of d2, but not equal to d2. + */ +int dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2); + /** * Debug helper. Print wireformat dname to output. * @param out: like stdout or a file. diff --git a/util/data/packed_rrset.c b/util/data/packed_rrset.c index 64210c64d..e7d172152 100644 --- a/util/data/packed_rrset.c +++ b/util/data/packed_rrset.c @@ -190,3 +190,26 @@ packed_rrset_ptr_fixup(struct packed_rrset_data* data) nextrdata += data->rr_len[i]; } } + +void +get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname, + size_t* dname_len) +{ + struct packed_rrset_data* d; + size_t len; + if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_CNAME) + return; + d = (struct packed_rrset_data*)rrset->entry.data; + if(d->count < 1) + return; + if(d->rr_len[0] < 3) /* at least rdatalen + 0byte root label */ + return; + len = ldns_read_uint16(d->rr_data[0]); + if(len != d->rr_len[0] - sizeof(uint16_t)) + return; + len -= sizeof(uint16_t); + if(dname_valid(d->rr_data[0]+sizeof(uint16_t), len) != len) + return; + *dname = d->rr_data[0]+sizeof(uint16_t); + *dname_len = len; +} diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h index 64c12ebeb..6af63d916 100644 --- a/util/data/packed_rrset.h +++ b/util/data/packed_rrset.h @@ -297,4 +297,17 @@ hashvalue_t rrset_key_hash(struct packed_rrset_key* key); */ void packed_rrset_ptr_fixup(struct packed_rrset_data* data); +/** + * Utility procedure to extract CNAME target name from its rdata. + * Failsafes; it will change passed dname to a valid dname or do nothing. + * @param rrset: the rrset structure. Must be a CNAME. + * Only first RR is used (multiple RRs are technically illegal anyway). + * @param dname: this pointer is updated to point into the cname rdata. + * If a failsafe fails, nothing happens to the pointer (such as the + * rdata was not a valid dname, not a CNAME, ...). + * @param dname_len: length of dname is returned. + */ +void get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname, + size_t* dname_len); + #endif /* UTIL_DATA_PACKED_RRSET_H */ diff --git a/util/net_help.h b/util/net_help.h index 4a20dd2f2..ced5180ed 100644 --- a/util/net_help.h +++ b/util/net_help.h @@ -53,6 +53,8 @@ #define BIT_CD 0x0010 /** QR flag */ #define BIT_QR 0x8000 +/** get RCODE bits from uint16 flags */ +#define FLAGS_GET_RCODE(f) ((f) & 0xf) /** timeout in seconds for UDP queries to auth servers. TODO: proper rtt */ #define UDP_QUERY_TIMEOUT 4