From: Wouter Wijngaards Date: Fri, 1 Jun 2007 20:24:33 +0000 (+0000) Subject: process_response, classify response, delegpt_from_message. X-Git-Tag: release-0.4~107 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a5e722d85ff560ce745166a5f3a8142bc8c75c3e;p=thirdparty%2Funbound.git process_response, classify response, delegpt_from_message. git-svn-id: file:///svn/unbound/trunk@359 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index b8974c825..697b233d7 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/iterator/iter_delegpt.c b/iterator/iter_delegpt.c index 0483bd4c4..f7742702b 100644 --- a/iterator/iter_delegpt.c +++ b/iterator/iter_delegpt.c @@ -41,8 +41,11 @@ */ #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; irrsets[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; irep->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; 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)) + 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; icount; 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; icount; 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; +} diff --git a/iterator/iter_delegpt.h b/iterator/iter_delegpt.h index b8ec9ab54..2c3b60058 100644 --- a/iterator/iter_delegpt.h +++ b/iterator/iter_delegpt.h @@ -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 */ diff --git a/iterator/iter_resptype.c b/iterator/iter_resptype.c index 5101ce52e..d12a3b77c 100644 --- a/iterator/iter_resptype.c +++ b/iterator/iter_resptype.c @@ -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; 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) { + 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; +} diff --git a/iterator/iter_resptype.h b/iterator/iter_resptype.h index d6e1890f9..c50f07e1c 100644 --- a/iterator/iter_resptype.h +++ b/iterator/iter_resptype.h @@ -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 */ diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 4dc70e6e5..13cbe9fef 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -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; irrset_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; +} diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index 59205a854..5a7921bda 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -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 */ diff --git a/iterator/iterator.c b/iterator/iterator.c index d169f7371..90d6d3fff 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -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; diff --git a/services/cache/dns.c b/services/cache/dns.c index 59c9b9143..5cba4e6f1 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -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; icount; 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; icount; 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; 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"); - } -} - /** 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) diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 1a65c3414..58e91ff4a 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -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; +} diff --git a/util/data/msgreply.h b/util/data/msgreply.h index b3a06cc96..697a768be 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -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 */