mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-01 20:39:38 -05:00
process_response, classify response, delegpt_from_message.
git-svn-id: file:///svn/unbound/trunk@359 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
a84ed747dc
commit
a5e722d85f
11 changed files with 558 additions and 82 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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; 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
84
services/cache/dns.c
vendored
84
services/cache/dns.c
vendored
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in a new issue