]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
process_response, classify response, delegpt_from_message.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 1 Jun 2007 20:24:33 +0000 (20:24 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 1 Jun 2007 20:24:33 +0000 (20:24 +0000)
git-svn-id: file:///svn/unbound/trunk@359 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
iterator/iter_delegpt.c
iterator/iter_delegpt.h
iterator/iter_resptype.c
iterator/iter_resptype.h
iterator/iter_utils.c
iterator/iter_utils.h
iterator/iterator.c
services/cache/dns.c
util/data/msgreply.c
util/data/msgreply.h

index b8974c8255d872c88a83fa5f5eab4310d79d23c2..697b233d7f0dd2caae5764bb0527db4a9b8d39cc 100644 (file)
@@ -4,6 +4,7 @@
        - sanitize incoming messages.
        - split msgreply encode functions into own file msgencode.c.
        - msg_parse to queryinfo/replyinfo conversion more versatile.
+       - process_response, classify response, delegpt_from_message. 
 
 31 May 2007: Wouter
        - querytargets state.
index 0483bd4c44fa0594afcf8ea7fa2966c97f032260..f7742702b66a9d203f2f4d84cb1f4b4322608b91 100644 (file)
  */
 #include "config.h"
 #include "iterator/iter_delegpt.h"
+#include "services/cache/dns.h"
 #include "util/region-allocator.h"
 #include "util/data/dname.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/msgreply.h"
 #include "util/net_help.h"
 
 struct delegpt* 
@@ -188,3 +191,132 @@ delegpt_count_missing_targets(struct delegpt* dp)
                        n++;
        return n;
 }
+
+/** find NS rrset in given list */
+static struct ub_packed_rrset_key*
+find_NS(struct reply_info* rep, size_t from, size_t to)
+{
+       size_t i;
+       for(i=from; i<to; i++) {
+               if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
+                       return rep->rrsets[i];
+       }
+       return NULL;
+}
+
+struct delegpt* 
+delegpt_from_message(struct dns_msg* msg, struct region* region)
+{
+       struct ub_packed_rrset_key* ns_rrset = NULL;
+       struct delegpt* dp;
+       size_t i;
+       /* look for NS records in the authority section... */
+       ns_rrset = find_NS(msg->rep, msg->rep->an_numrrsets, 
+               msg->rep->an_numrrsets+msg->rep->ns_numrrsets);
+
+       /* In some cases (even legitimate, perfectly legal cases), the 
+        * NS set for the "referral" might be in the answer section. */
+       if(!ns_rrset)
+               ns_rrset = find_NS(msg->rep, 0, msg->rep->an_numrrsets);
+       
+       /* If there was no NS rrset in the authority section, then this 
+        * wasn't a referral message. (It might not actually be a 
+        * referral message anyway) */
+       if(!ns_rrset)
+               return NULL;
+       
+       /* If we found any, then Yay! we have a delegation point. */
+       dp = delegpt_create(region);
+       if(!dp)
+               return NULL;
+       if(!delegpt_set_name(dp, region, ns_rrset->rk.dname))
+               return NULL;
+       if(!delegpt_rrset_add_ns(dp, region, ns_rrset))
+               return NULL;
+
+       /* add glue, A and AAAA in answer and additional section */
+       for(i=0; i<msg->rep->rrset_count; i++) {
+               struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
+               /* skip auth section. FIXME really needed?*/
+               if(msg->rep->an_numrrsets <= i && 
+                       i < (msg->rep->an_numrrsets+msg->rep->ns_numrrsets))
+                       continue;
+
+               if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
+                       if(!delegpt_add_rrset_A(dp, region, s))
+                               return NULL;
+               } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
+                       if(!delegpt_add_rrset_AAAA(dp, region, s))
+                               return NULL;
+               }
+       }
+       return dp;
+}
+
+int 
+delegpt_rrset_add_ns(struct delegpt* dp, struct region* region,
+        struct ub_packed_rrset_key* ns_rrset)
+{
+       struct packed_rrset_data* nsdata = (struct packed_rrset_data*)
+               ns_rrset->entry.data;
+       size_t i;
+       for(i=0; i<nsdata->count; 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))
+                       return 0;
+       }
+       return 1;
+}
+
+int 
+delegpt_add_rrset_A(struct delegpt* dp, struct region* region,
+       struct ub_packed_rrset_key* ak)
+{
+        struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
+        size_t i;
+        struct sockaddr_in sa;
+        socklen_t len = (socklen_t)sizeof(sa);
+        memset(&sa, 0, len);
+        sa.sin_family = AF_INET;
+        sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+        for(i=0; i<d->count; i++) {
+                if(d->rr_len[i] != 2 + INET_SIZE)
+                        continue;
+                memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE);
+                log_addr("adding A to deleg",  (struct sockaddr_storage*)&sa,
+                        len);
+                if(!delegpt_add_target(dp, region, ak->rk.dname,
+                        ak->rk.dname_len, (struct sockaddr_storage*)&sa,
+                        len))
+                        return 0;
+        }
+        return 1;
+}
+
+int 
+delegpt_add_rrset_AAAA(struct delegpt* dp, struct region* region,
+       struct ub_packed_rrset_key* ak)
+{
+        struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
+        size_t i;
+        struct sockaddr_in6 sa;
+        socklen_t len = (socklen_t)sizeof(sa);
+        memset(&sa, 0, len);
+        sa.sin6_family = AF_INET6;
+        sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+        for(i=0; i<d->count; i++) {
+                if(d->rr_len[i] != 2 + INET6_SIZE) /* rdatalen + len of IP6 */
+                        continue;
+                memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE);
+                log_addr("adding AAAA to deleg",  (struct sockaddr_storage*)&sa,                        len);
+                if(!delegpt_add_target(dp, region, ak->rk.dname,
+                        ak->rk.dname_len, (struct sockaddr_storage*)&sa,
+                        len))
+                        return 0;
+        }
+        return 1;
+}
index b8ec9ab54e4fd0f94a0bd240e682e1a30c7f4583..2c3b60058ec06e7f19ea81190372c8248f7db42f 100644 (file)
@@ -45,6 +45,8 @@
 struct region;
 struct delegpt_ns;
 struct delegpt_addr;
+struct dns_msg;
+struct ub_packed_rrset_key;
 
 /**
  * Delegation Point.
@@ -136,6 +138,16 @@ int delegpt_set_name(struct delegpt* dp, struct region* region, uint8_t* name);
  */
 int delegpt_add_ns(struct delegpt* dp, struct region* region, uint8_t* name);
 
+/**
+ * Add NS rrset; calls add_ns repeatedly.
+ * @param dp: delegation point.
+ * @param region: where to allocate the info.
+ * @param ns_rrset: NS rrset.
+ * return 0 on alloc error.
+ */
+int delegpt_rrset_add_ns(struct delegpt* dp, struct region* region,
+       struct ub_packed_rrset_key* ns_rrset);
+
 /**
  * Add target address to the delegation point.
  * @param dp: delegation point.
@@ -151,6 +163,26 @@ int delegpt_add_target(struct delegpt* dp, struct region* region,
        uint8_t* name, size_t namelen, struct sockaddr_storage* addr, 
        socklen_t addrlen);
 
+/**
+ * Add A RRset to delegpt.
+ * @param dp: delegation point.
+ * @param region: where to allocate the info.
+ * @param rrset: RRset A to add.
+ * @return 0 on alloc error.
+ */
+int delegpt_add_rrset_A(struct delegpt* dp, struct region* region, 
+       struct ub_packed_rrset_key* rrset);
+
+/**
+ * Add AAAA RRset to delegpt.
+ * @param dp: delegation point.
+ * @param region: where to allocate the info.
+ * @param rrset: RRset AAAA to add.
+ * @return 0 on alloc error.
+ */
+int delegpt_add_rrset_AAAA(struct delegpt* dp, struct region* region, 
+       struct ub_packed_rrset_key* rrset);
+
 /**
  * Add address to the delegation point. No servername is associated or checked.
  * @param dp: delegation point.
@@ -181,4 +213,25 @@ void delegpt_add_unused_targets(struct delegpt* dp);
  */
 size_t delegpt_count_missing_targets(struct delegpt* dp);
 
+/**
+ * Create new delegation point from a dns message
+ *
+ * Note that this method does not actually test to see if the message is an
+ * actual referral. It really is just checking to see if it can construct a
+ * delegation point, so the message could be of some other type (some ANSWER
+ * messages, some CNAME messages, generally.) Note that the resulting
+ * DelegationPoint will contain targets for all "relevant" glue (i.e.,
+ * address records whose ownernames match the target of one of the NS
+ * records), so if policy dictates that some glue should be discarded beyond
+ * that, discard it before calling this method. Note that this method will
+ * find "glue" in either the ADDITIONAL section or the ANSWER section.
+ *
+ * @param msg: the dns message, referral.
+ * @param region: where to allocate delegation point.
+ * @return new delegation point or NULL on alloc error, or if the
+ *         message was not appropriate.
+ */
+struct delegpt* delegpt_from_message(struct dns_msg* msg, 
+       struct region* region);
+
 #endif /* ITERATOR_ITER_DELEGPT_H */
index 5101ce52e39e18647017fa3046277c2396045d80..d12a3b77ce8489a8facf7a33a5150d47341da9a2 100644 (file)
@@ -41,6 +41,7 @@
  */
 #include "config.h"
 #include "iterator/iter_resptype.h"
+#include "iterator/iter_delegpt.h"
 #include "services/cache/dns.h"
 #include "util/net_help.h"
 #include "util/data/dname.h"
@@ -97,3 +98,126 @@ response_type_from_cache(struct dns_msg* msg,
         * messages, it can only be an ANSWER. */
        return RESPONSE_TYPE_ANSWER;
 }
+
+enum response_type 
+response_type_from_server(struct dns_msg* msg, struct query_info* request,
+       struct delegpt* dp)
+{
+       uint8_t* origzone = (uint8_t*)"\000"; /* the default */
+       size_t origzonelen = 1;
+       size_t i;
+
+       if(!msg || !request)
+               return RESPONSE_TYPE_THROWAWAY;
+       
+       /* If the message is NXDOMAIN, then it answers the question. */
+       if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN)
+               return RESPONSE_TYPE_ANSWER;
+       
+       /* Other response codes mean (so far) to throw the response away as
+        * meaningless and move on to the next nameserver. */
+       if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NOERROR)
+               return RESPONSE_TYPE_THROWAWAY;
+
+       /* Note: TC bit has already been handled */
+
+       if(dp) {
+               origzone = dp->name;
+               origzonelen = dp->namelen;
+       }
+
+       /* First we look at the answer section. This can tell us if this is a
+        * CNAME or ANSWER or (provisional) ANSWER. */
+       if(msg->rep->an_numrrsets > 0) {
+               uint8_t* mname = request->qname;
+               size_t mname_len = request->qname_len;
+
+               /* Now look at the answer section first. 3 states: our 
+                * answer is there directly, our answer is there after 
+                * a cname, or there is just a cname. */
+               for(i=0; i<msg->rep->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) {
+                               if((msg->rep->flags&BIT_AA))
+                                       return RESPONSE_TYPE_ANSWER;
+                               /* If the AA bit isn't on, and we've seen 
+                                * the answer, we only provisionally say 
+                                * 'ANSWER' -- it very well could be a 
+                                * REFERRAL. */
+                               break;
+                       }
+
+                       /* 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. 
+                * (This is regardless of the AA bit at this point) */
+               if(mname != request->qname) {
+                       return RESPONSE_TYPE_CNAME;
+               }
+       }
+
+       /* Looking at the authority section, we just look and see if 
+        * there is a delegation NS set, turning it into a delegation. 
+        * Otherwise, we will have to conclude ANSWER (either it is 
+        * NOERROR/NODATA, or an non-authoritative answer). */
+       for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
+               msg->rep->ns_numrrsets); i++) {
+               struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
+
+               /* The normal way of detecting NOERROR/NODATA. */
+               if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
+                       dname_subdomain_c(request->qname, s->rk.dname)) {
+                       return RESPONSE_TYPE_ANSWER;
+               }
+
+               /* Detect REFERRAL/LAME/ANSWER based on the relationship 
+                * of the NS set to the originating zone name. */
+               if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) {
+                       /* If we are getting an NS set for the zone we 
+                        * thought we were contacting, then it is an answer.*/
+                       /* FIXME: is this correct? */
+                       if(query_dname_compare(s->rk.dname, origzone)) {
+                               return RESPONSE_TYPE_ANSWER;
+                       }
+                       /* If we are getting a referral upwards (or to 
+                        * the same zone), then the server is 'lame'. */
+                       if(dname_subdomain_c(origzone, s->rk.dname)) {
+                               return RESPONSE_TYPE_LAME;
+                       }
+                       /* If the NS set is below the delegation point we 
+                        * are on, and it is non-authoritative, then it is 
+                        * a referral, otherwise it is an answer. */
+                       if(dname_subdomain_c(s->rk.dname, origzone)) {
+                               /* NOTE: I no longer remember in what case 
+                                * we would like this to be an answer. 
+                                * NODATA should have a SOA or nothing, 
+                                * not an NS rrset. 
+                                * True, referrals should not have the AA 
+                                * bit set, but... */
+                                
+                               /* if((msg->rep->flags&BIT_AA))
+                                       return RESPONSE_TYPE_ANSWER; */
+                               return RESPONSE_TYPE_REFERRAL;
+                       }
+                       /* Otherwise, the NS set is irrelevant. */
+               }
+       }
+
+       /* If we've gotten this far, this is NOERROR/NODATA (which could 
+        * be an entirely empty message) */
+       return RESPONSE_TYPE_ANSWER;
+}
index d6e1890f945ecdb29ac79650974404616465d4c0..c50f07e1c35bf9dcc509b484b7d35636cc8e9fa3 100644 (file)
@@ -44,6 +44,7 @@
 #define ITERATOR_ITER_RESPTYPE_H
 struct dns_msg;
 struct query_info;
+struct delegpt;
 
 /**
  * The response type is used to interpret the response.
@@ -97,4 +98,22 @@ enum response_type {
 enum response_type response_type_from_cache(struct dns_msg* msg, 
        struct query_info* request);
 
+/**
+ * Classifies a response message (from the wire) based on the current
+ * request.
+ *
+ * NOTE: currently this routine uses the AA bit in the response to help
+ * distinguish between some non-standard referrals and answers. It also
+ * relies somewhat on the originating zone to be accurate (for lameness
+ * detection, mostly).
+ *
+ * @param msg: the message from the cache.
+ * @param request: the request that generated the response.
+ * @param dp: The delegation point that was being queried
+ *          when the response was returned.
+ * @return the response type (CNAME or ANSWER).
+ */
+enum response_type response_type_from_server(struct dns_msg* msg, 
+       struct query_info* request, struct delegpt* dp);
+
 #endif /* ITERATOR_ITER_RESPTYPE_H */
index 4dc70e6e55f393771a1f03c37fb5742bf2e68938..13cbe9fef76fe9809012f85812a6724996cd1059 100644 (file)
@@ -46,6 +46,7 @@
 #include "iterator/iter_delegpt.h"
 #include "services/cache/infra.h"
 #include "services/cache/dns.h"
+#include "services/cache/rrset.h"
 #include "util/net_help.h"
 #include "util/module.h"
 #include "util/log.h"
@@ -168,3 +169,42 @@ dns_alloc_msg(ldns_buffer* pkt, struct msg_parse* msg, struct region* region)
        }
        return m;
 }
+
+int 
+iter_dns_store(struct module_env* env, struct dns_msg* msg, int is_referral)
+{
+       struct reply_info* rep = NULL;
+       /* alloc, malloc properly (not in region, like msg is) */
+       rep = reply_info_copy(msg->rep, env->alloc);
+       if(!rep)
+               return 0;
+
+       if(is_referral) {
+               /* store rrsets */
+               struct rrset_ref ref;
+               uint32_t now = time(NULL);
+               size_t i;
+               reply_info_set_ttls(rep, now);
+               for(i=0; i<rep->rrset_count; i++) {
+                       ref.key = rep->rrsets[i];
+                       ref.id = rep->rrsets[i]->id;
+                       /*ignore ret: it was in the cache, ref updated */
+                       (void)rrset_cache_update(env->rrset_cache, &ref, 
+                               env->alloc, now);
+               }
+               return 1;
+       } else {
+               /* store msg, and rrsets */
+               struct query_info qinf;
+               hashvalue_t h;
+
+               qinf = msg->qinfo;
+               qinf.qname = memdup(msg->qinfo.qname, msg->qinfo.qname_len);
+               if(!qinf.qname)
+                       return 0;
+               h = query_info_hash(&qinf);
+               dns_cache_store_msg(env, &qinf, h, rep);
+               free(qinf.qname);
+       }
+       return 1;
+}
index 59205a8540029d4ab9c23d70978c369af1d9fbac..5a7921bdada6acbcf313f3141ed8dc49f45ba60b 100644 (file)
@@ -86,4 +86,15 @@ struct delegpt_addr* iter_server_selection(struct iter_env* iter_env,
 struct dns_msg* dns_alloc_msg(ldns_buffer* pkt, struct msg_parse* msg, 
        struct region* region);
 
+/**
+ * Allocate a dns_msg with malloc/alloc structure and store in dns cache.
+ * @param env: environment, with alloc structure and dns cache.
+ * @param msg: dns_msg from dns_alloc_msg for example.
+ * @param is_referral: If true, then the given message to be stored is a
+ *     referral. The cache implementation may use this as a hint.
+ * @return 0 on alloc error (out of memory).
+ */
+int iter_dns_store(struct module_env* env, struct dns_msg* msg, 
+       int is_referral);
+
 #endif /* ITERATOR_ITER_UTILS_H */
index d169f737153dac2ec77fcd45a7e1587030de3db2..90d6d3fff5e81ca78dba9ed489c76127e29c4c58 100644 (file)
@@ -1067,15 +1067,132 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
        return 0;
 }
 
-#if 0
-/** TODO */
+/** 
+ * Process the query response. All queries end up at this state first. This
+ * process generally consists of analyzing the response and routing the
+ * event to the next state (either bouncing it back to a request state, or
+ * terminating the processing for this event).
+ * 
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ * @return true if the event requires more immediate processing, false if
+ *         not. This is generally only true when forwarding the request to
+ *         the final state (i.e., on answer).
+ */
 static int
 processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
-       struct iter_env* ie, int id)
+       int id)
 {
-       return 0;
+       enum response_type type;
+       iq->num_current_queries--;
+       qstate->ext_state[id] = module_error; /* debug, must be overridden */
+       if(iq->response == NULL) {
+               verbose(VERB_ALGO, "query response was timeout");
+               return next_state(qstate, iq, QUERYTARGETS_STATE);
+       }
+       type = response_type_from_server(iq->response, &qstate->qinfo, iq->dp);
+       if(type == RESPONSE_TYPE_ANSWER) {
+               /* ANSWER type responses terminate the query algorithm, 
+                * so they sent on their */
+               verbose(VERB_ALGO, "query response was ANSWER");
+               
+               /* FIXME: there is a question about whether this gets 
+                * stored under the original query or most recent query. 
+                * The original query would reduce cache work, but you 
+                * need to apply the prependList before caching, and 
+                * also cache under the latest query. */
+               if(!iter_dns_store(qstate->env, iq->response, 0))
+                       return error_response(qstate, iq, LDNS_RCODE_SERVFAIL);
+               /* close down outstanding requests to be discarded */
+               outbound_list_clear(&iq->outlist);
+               return final_state(qstate, iq);
+       } else if(type == RESPONSE_TYPE_REFERRAL) {
+               /* REFERRAL type responses get a reset of the 
+                * delegation point, and back to the QUERYTARGETS_STATE. */
+               verbose(VERB_ALGO, "query response was REFERRAL");
+
+               /* Store the referral under the current query */
+               if(!iter_dns_store(qstate->env, iq->response, 1))
+                       return error_response(qstate, iq, LDNS_RCODE_SERVFAIL);
+
+               /* Reset the event state, setting the current delegation 
+                * point to the referral. */
+               iq->deleg_msg = iq->response;
+               iq->dp = delegpt_from_message(iq->response, qstate->region);
+               if(!iq->dp)
+                       return error_response(qstate, iq, LDNS_RCODE_SERVFAIL);
+               iq->num_current_queries = 0;
+               iq->num_target_queries = -1;
+               /* Count this as a referral. */
+               iq->referral_count++;
+
+               /* stop current outstanding queries. 
+                * FIXME: should the outstanding queries be waited for and
+                * handled?
+                */
+               outbound_list_clear(&iq->outlist);
+               verbose(VERB_ALGO, "cleared outbound list for next round");
+               return next_state(qstate, iq, QUERYTARGETS_STATE);
+       } else if(type == RESPONSE_TYPE_CNAME) {
+               uint8_t* sname = NULL;
+               size_t snamelen = 0;
+               /* CNAME type responses get a query restart (i.e., get a 
+                * reset of the query state and go back to INIT_REQUEST_STATE).
+                */
+               verbose(VERB_ALGO, "query response was CNAME");
+               /* Process the CNAME response. */
+               if(!iq->orig_qname) {
+                       iq->orig_qname = qstate->qinfo.qname;
+                       iq->orig_qnamelen = qstate->qinfo.qname_len;
+               }
+               if(!handle_cname_response(qstate, iq, iq->response, 
+                       &sname, &snamelen))
+                       return error_response(qstate, iq, LDNS_RCODE_SERVFAIL);
+               /* cache the CNAME response under the current query */
+               if(!iter_dns_store(qstate->env, iq->response, 0))
+                       return error_response(qstate, iq, LDNS_RCODE_SERVFAIL);
+               /* set the current request's qname to the new value. */
+               qstate->qinfo.qname = sname;
+               qstate->qinfo.qname_len = snamelen;
+               /* Clear the query state, since this is a query restart. */
+               iq->deleg_msg = NULL;
+               iq->dp = NULL;
+               iq->num_current_queries = 0;
+               iq->num_target_queries = -1;
+               /* Note the query restart. */
+               iq->query_restart_count++;
+
+               /* stop current outstanding queries. 
+                * FIXME: should the outstanding queries be waited for and
+                * handled?
+                */
+               outbound_list_clear(&iq->outlist);
+               verbose(VERB_ALGO, "cleared outbound list for query restart");
+               /* go to INIT_REQUEST_STATE for new qname. */
+               return next_state(qstate, iq, INIT_REQUEST_STATE);
+       } else if(type == RESPONSE_TYPE_LAME) {
+               /* Cache the LAMEness. */
+               /* TODO mark addr, dp->name, as lame */
+               verbose(VERB_ALGO, "query response was LAME");
+       } else if(type == RESPONSE_TYPE_THROWAWAY) {
+               /* LAME and THROWAWAY responses are handled the same way. 
+                * In this case, the event is just sent directly back to 
+                * the QUERYTARGETS_STATE without resetting anything, 
+                * because, clearly, the next target must be tried. */
+               verbose(VERB_ALGO, "query response was THROWAWAY");
+       } else {
+               log_warn("A query response came back with an unknown type: %d",
+                       (int)type);
+       }
+
+       /* LAME, THROWAWAY and "unknown" all end up here.
+        * Recycle to the QUERYTARGETS state to hopefully try a 
+        * different target. */
+       return next_state(qstate, iq, QUERYTARGETS_STATE);
 }
 
+#if 0
 /** TODO */
 static int
 processPrimeResponse(struct module_qstate* qstate, struct iter_qstate* iq,
@@ -1134,10 +1251,10 @@ iter_handle(struct module_qstate* qstate, struct iter_qstate* iq,
                        case QUERYTARGETS_STATE:
                                cont = processQueryTargets(qstate, iq, ie, id);
                                break;
-#if 0
                        case QUERY_RESP_STATE:
-                               cont = processQueryResponse(qstate, iq, ie, id);
+                               cont = processQueryResponse(qstate, iq, id);
                                break;
+#if 0
                        case PRIME_RESP_STATE:
                                cont = processPrimeResponse(qstate, iq, ie, id);
                                break;
index 59c9b91430cf6fee662369101ae1fd10b4893518..5cba4e6f119a22f5cd34a358e49256936542d98d 100644 (file)
@@ -165,62 +165,6 @@ addr_to_additional(struct ub_packed_rrset_key* rrset, struct region* region,
        }
 }
 
-/** add A records to delegation */
-static int
-add_a(struct ub_packed_rrset_key* ak, struct delegpt* dp, 
-       struct region* region, struct dns_msg** msg, uint32_t now)
-{
-       struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
-       size_t i;
-       struct sockaddr_in sa;
-       socklen_t len = (socklen_t)sizeof(sa);
-       memset(&sa, 0, len);
-       sa.sin_family = AF_INET;
-       sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
-       for(i=0; i<d->count; i++) {
-               if(d->rr_len[i] != 2 + INET_SIZE)
-                       continue;
-               memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE);
-               log_addr("adding A to deleg",  (struct sockaddr_storage*)&sa,
-                       len);
-               if(!delegpt_add_target(dp, region, ak->rk.dname, 
-                       ak->rk.dname_len, (struct sockaddr_storage*)&sa,
-                       len))
-                       return 0;
-       }
-       if(msg)
-               addr_to_additional(ak, region, *msg, now);
-       return 1;
-}
-
-/** add AAAA records to delegation */
-static int
-add_aaaa(struct ub_packed_rrset_key* ak, struct delegpt* dp, 
-       struct region* region, struct dns_msg** msg, uint32_t now)
-{
-       struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
-       size_t i;
-       struct sockaddr_in6 sa;
-       socklen_t len = (socklen_t)sizeof(sa);
-       memset(&sa, 0, len);
-       sa.sin6_family = AF_INET6;
-       sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
-       for(i=0; i<d->count; i++) {
-               if(d->rr_len[i] != 2 + INET6_SIZE) /* rdatalen + len of IP6 */
-                       continue;
-               memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE);
-               log_addr("adding AAAA to deleg",  (struct sockaddr_storage*)&sa,
-                       len);
-               if(!delegpt_add_target(dp, region, ak->rk.dname, 
-                       ak->rk.dname_len, (struct sockaddr_storage*)&sa,
-                       len))
-                       return 0;
-       }
-       if(msg)
-               addr_to_additional(ak, region, *msg, now);
-       return 1;
-}
-
 /** find and add A and AAAA records for nameservers in delegpt */
 static int
 find_add_addrs(struct module_env* env, uint16_t qclass, struct region* region,
@@ -232,42 +176,29 @@ find_add_addrs(struct module_env* env, uint16_t qclass, struct region* region,
                akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
                        ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
                if(akey) {
-                       if(!add_a(akey, dp, region, msg, now)) {
+                       if(!delegpt_add_rrset_A(dp, region, akey)) {
                                lock_rw_unlock(&akey->entry.lock);
                                return 0;
                        }
+                       if(msg)
+                               addr_to_additional(akey, region, *msg, now);
                        lock_rw_unlock(&akey->entry.lock);
                }
                akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
                        ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
                if(akey) {
-                       if(!add_aaaa(akey, dp, region, msg, now)) {
+                       if(!delegpt_add_rrset_AAAA(dp, region, akey)) {
                                lock_rw_unlock(&akey->entry.lock);
                                return 0;
                        }
+                       if(msg)
+                               addr_to_additional(akey, region, *msg, now);
                        lock_rw_unlock(&akey->entry.lock);
                }
        }
        return 1;
 }
 
-/** Add NS records to delegation */
-static void
-add_ns(struct packed_rrset_data* nsdata, struct delegpt* dp, 
-       struct region* region)
-{
-       size_t i;
-       for(i=0; i<nsdata->count; 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");
-       }
-}
-
 /** find and add DS or NSEC to delegation msg */
 static void
 find_add_ds(struct module_env* env, struct region* region, 
@@ -372,7 +303,8 @@ dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
                        return NULL;
                }
        }
-       add_ns(nsdata, dp, region);
+       if(!delegpt_rrset_add_ns(dp, region, nskey))
+               log_err("find_delegation: addns out of memory");
        lock_rw_unlock(&nskey->entry.lock); /* first unlock before next lookup*/
        /* find and add DS/NSEC (if any) */
        if(msg)
index 1a65c3414027a950662d0fab8fcdd6c98b0ceb1b..58e91ff4a3ceb0b5d0628158ce421b2d1c28dab4 100644 (file)
@@ -554,3 +554,41 @@ query_info_entrysetup(struct query_info* q, struct reply_info* r,
        q->qname = NULL;
        return e;
 }
+
+static struct reply_info*
+copy_repinfo(struct reply_info* from)
+{
+       struct reply_info* cp;
+       /* rrset_count-1 because the first ref is part of the struct. */
+       size_t s = sizeof(struct reply_info) - sizeof(struct rrset_ref) +
+               sizeof(struct ub_packed_rrset_key*) * from->rrset_count;
+       cp = (struct reply_info*)malloc(s + 
+                       sizeof(struct rrset_ref) * (from->rrset_count));
+       if(!cp) return NULL;
+       cp->flags = from->flags;
+       cp->qdcount = from->qdcount;
+       cp->ttl = from->ttl;
+       cp->an_numrrsets = from->an_numrrsets;
+       cp->ns_numrrsets = from->ns_numrrsets;
+       cp->ar_numrrsets = from->ar_numrrsets;
+       cp->rrset_count = from->rrset_count;
+       /* array starts after the refs */
+       cp->rrsets = (struct ub_packed_rrset_key**)
+               &(cp->ref[from->rrset_count]);
+       /* zero the arrays to assist cleanup in case of malloc failure */
+       memset( cp->rrsets, 0, 
+               sizeof(struct ub_packed_rrset_key*) * from->rrset_count);
+       memset( &cp->ref[0], 0, 
+               sizeof(struct rrset_ref) * from->rrset_count);
+       return cp;
+}
+
+struct reply_info* 
+reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc)
+{
+       struct reply_info* cp;
+       if(!(cp = copy_repinfo(rep)))
+               return NULL;
+       /* TODO copy rrsets */
+       return cp;
+}
index b3a06cc96c9748f372cba2210717c993c1f5319d..697a768be1d55839c52360a67fbab616c5b136cb 100644 (file)
@@ -276,4 +276,13 @@ hashvalue_t query_info_hash(struct query_info *q);
 struct msgreply_entry* query_info_entrysetup(struct query_info* q,
        struct reply_info* r, hashvalue_t h);
 
+/**
+ * Copy reply_info and all rrsets in it and allocate.
+ * @param rep: what to copy, probably inside region, no ref[] array in it.
+ * @param alloc: how to allocate rrset keys.
+ * @return new reply info or NULL on memory error.
+ */
+struct reply_info* reply_info_copy(struct reply_info* rep, 
+       struct alloc_cache* alloc);
+
 #endif /* UTIL_DATA_MSGREPLY_H */