mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
0x20 fallback code.
git-svn-id: file:///svn/unbound/trunk@1285 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
3ddabf26f3
commit
939fabd809
15 changed files with 181 additions and 28 deletions
|
|
@ -211,7 +211,7 @@ worker_handle_reply(struct comm_point* c, void* arg, int error,
|
|||
e.qsent = NULL;
|
||||
|
||||
if(error != 0) {
|
||||
mesh_report_reply(worker->env.mesh, &e, 0, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, &e, reply_info, error);
|
||||
worker_mem_report(worker, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -222,11 +222,12 @@ worker_handle_reply(struct comm_point* c, void* arg, int error,
|
|||
|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
mesh_report_reply(worker->env.mesh, &e, 0, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, &e, reply_info,
|
||||
NETEVENT_TIMEOUT);
|
||||
worker_mem_report(worker, NULL);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(worker->env.mesh, &e, 1, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, &e, reply_info, NETEVENT_NOERROR);
|
||||
worker_mem_report(worker, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -241,7 +242,7 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
|||
|
||||
verbose(VERB_ALGO, "worker svcd callback for qstate %p", e->qstate);
|
||||
if(error != 0) {
|
||||
mesh_report_reply(worker->env.mesh, e, 0, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, e, reply_info, error);
|
||||
worker_mem_report(worker, sq);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -253,11 +254,12 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
|||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
verbose(VERB_ALGO, "worker: bad reply handled as timeout");
|
||||
mesh_report_reply(worker->env.mesh, e, 0, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, e, reply_info,
|
||||
NETEVENT_TIMEOUT);
|
||||
worker_mem_report(worker, sq);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(worker->env.mesh, e, 1, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, e, reply_info, NETEVENT_NOERROR);
|
||||
worker_mem_report(worker, sq);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
6 October 2008: Wouter
|
||||
- jostle-timeout option, so you can config for slow links.
|
||||
- 0x20 fallback code. Tries 3xnumber of nameserver addresses
|
||||
queries that must all be the same. Sent to random nameservers.
|
||||
|
||||
2 October 2008: Wouter
|
||||
- fixup unlink of pidfile.
|
||||
|
|
|
|||
|
|
@ -484,3 +484,60 @@ int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp,
|
|||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check equality of two rrsets
|
||||
* @param k1: rrset
|
||||
* @param k2: rrset
|
||||
* @return true if equal
|
||||
*/
|
||||
static int
|
||||
rrset_equal(struct ub_packed_rrset_key* k1, struct ub_packed_rrset_key* k2)
|
||||
{
|
||||
struct packed_rrset_data* d1 = (struct packed_rrset_data*)
|
||||
k1->entry.data;
|
||||
struct packed_rrset_data* d2 = (struct packed_rrset_data*)
|
||||
k2->entry.data;
|
||||
size_t i, t;
|
||||
if(k1->rk.dname_len != k2->rk.dname_len ||
|
||||
k1->rk.flags != k2->rk.flags ||
|
||||
k1->rk.type != k2->rk.type ||
|
||||
k1->rk.rrset_class != k2->rk.rrset_class ||
|
||||
query_dname_compare(k1->rk.dname, k2->rk.dname) != 0)
|
||||
return 0;
|
||||
if(d1->ttl != d2->ttl ||
|
||||
d1->count != d2->count ||
|
||||
d1->rrsig_count != d2->rrsig_count ||
|
||||
d1->trust != d2->trust ||
|
||||
d1->security != d2->security)
|
||||
return 0;
|
||||
t = d1->count + d1->rrsig_count;
|
||||
for(i=0; i<t; i++) {
|
||||
if(d1->rr_len[i] != d2->rr_len[i] ||
|
||||
d1->rr_ttl[i] != d2->rr_ttl[i] ||
|
||||
memcmp(d1->rr_data[i], d2->rr_data[i],
|
||||
d1->rr_len[i]) != 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
reply_equal(struct reply_info* p, struct reply_info* q)
|
||||
{
|
||||
size_t i;
|
||||
if(p->flags != q->flags ||
|
||||
p->qdcount != q->qdcount ||
|
||||
p->ttl != q->ttl ||
|
||||
p->security != q->security ||
|
||||
p->an_numrrsets != q->an_numrrsets ||
|
||||
p->ns_numrrsets != q->ns_numrrsets ||
|
||||
p->ar_numrrsets != q->ar_numrrsets ||
|
||||
p->rrset_count != q->rrset_count)
|
||||
return 0;
|
||||
for(i=0; i<p->rrset_count; i++) {
|
||||
if(!rrset_equal(p->rrsets[i], q->rrsets[i]))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,4 +180,14 @@ int iter_msg_has_dnssec(struct dns_msg* msg);
|
|||
int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp,
|
||||
enum response_type type, uint16_t dclass);
|
||||
|
||||
/**
|
||||
* Check if two replies are equal
|
||||
* For fallback procedures
|
||||
* @param p: reply one. The reply has rrset data pointers in region.
|
||||
* Does not check rrset-IDs
|
||||
* @param q: reply two
|
||||
* @return if one and two are equal.
|
||||
*/
|
||||
int reply_equal(struct reply_info* p, struct reply_info* q);
|
||||
|
||||
#endif /* ITERATOR_ITER_UTILS_H */
|
||||
|
|
|
|||
|
|
@ -1153,11 +1153,41 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
tf_policy = ie->target_fetch_policy[iq->depth];
|
||||
}
|
||||
|
||||
/* if in 0x20 fallback get as many targets as possible */
|
||||
if(iq->caps_fallback) {
|
||||
int extra = 0;
|
||||
size_t naddr, nres, navail;
|
||||
if(!query_for_targets(qstate, iq, ie, id, -1, &extra)) {
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
iq->num_target_queries += extra;
|
||||
if(iq->num_target_queries > 0) {
|
||||
/* wait to get all targets, we want to try em */
|
||||
verbose(VERB_ALGO, "wait for all targets for fallback");
|
||||
return 0;
|
||||
}
|
||||
/* did we do enough fallback queries already? */
|
||||
delegpt_count_addr(iq->dp, &naddr, &nres, &navail);
|
||||
/* the current caps_server is the number of fallbacks sent.
|
||||
* the original query is one that matched too, so we have
|
||||
* caps_server+1 number of matching queries now */
|
||||
if(iq->caps_server+1 >= naddr*3) {
|
||||
/* we're done, process the response */
|
||||
verbose(VERB_ALGO, "0x20 fallback had %d responses "
|
||||
"match for %d wanted, done.",
|
||||
(int)iq->caps_server+1, (int)naddr*3);
|
||||
iq->caps_fallback = 0;
|
||||
iq->state = QUERY_RESP_STATE;
|
||||
return 1;
|
||||
}
|
||||
verbose(VERB_ALGO, "0x20 fallback number %d",
|
||||
(int)iq->caps_server);
|
||||
|
||||
/* if there is a policy to fetch missing targets
|
||||
* opportunistically, do it. we rely on the fact that once a
|
||||
* query (or queries) for a missing name have been issued,
|
||||
* they will not be show up again. */
|
||||
if(tf_policy != 0) {
|
||||
} else if(tf_policy != 0) {
|
||||
int extra = 0;
|
||||
verbose(VERB_ALGO, "attempt to get extra %d targets",
|
||||
tf_policy);
|
||||
|
|
@ -1760,7 +1790,8 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
if(event == module_event_noreply || event == module_event_error) {
|
||||
goto handle_it;
|
||||
}
|
||||
if(event != module_event_reply || !qstate->reply) {
|
||||
if( (event != module_event_reply && event != module_event_capsfail)
|
||||
|| !qstate->reply) {
|
||||
log_err("Bad event combined with response");
|
||||
outbound_list_remove(&iq->outlist, outbound);
|
||||
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
|
|
@ -1805,6 +1836,37 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
log_dns_msg("incoming scrubbed packet:", &iq->response->qinfo,
|
||||
iq->response->rep);
|
||||
|
||||
if(event == module_event_capsfail) {
|
||||
if(!iq->caps_fallback) {
|
||||
/* start fallback */
|
||||
iq->caps_fallback = 1;
|
||||
iq->caps_server = 0;
|
||||
iq->caps_reply = iq->response->rep;
|
||||
iq->state = QUERYTARGETS_STATE;
|
||||
iq->num_current_queries--;
|
||||
verbose(VERB_DETAIL, "Capsforid: starting fallback");
|
||||
goto handle_it;
|
||||
} else {
|
||||
/* check if reply is the same, otherwise, fail */
|
||||
if(!reply_equal(iq->response->rep, iq->caps_reply)) {
|
||||
verbose(VERB_DETAIL, "Capsforid fallback: "
|
||||
"getting different replies, failed");
|
||||
outbound_list_remove(&iq->outlist, outbound);
|
||||
(void)error_response(qstate, id,
|
||||
LDNS_RCODE_SERVFAIL);
|
||||
return;
|
||||
}
|
||||
/* continue the fallback procedure at next server */
|
||||
iq->caps_server++;
|
||||
iq->state = QUERYTARGETS_STATE;
|
||||
iq->num_current_queries--;
|
||||
verbose(VERB_DETAIL, "Capsforid: reply is equal. "
|
||||
"go to next fallback");
|
||||
goto handle_it;
|
||||
}
|
||||
}
|
||||
iq->caps_fallback = 0; /* if we were in fallback, 0x20 is OK now */
|
||||
|
||||
handle_it:
|
||||
outbound_list_remove(&iq->outlist, outbound);
|
||||
iter_handle(qstate, iq, ie, id);
|
||||
|
|
|
|||
|
|
@ -218,6 +218,13 @@ struct iter_qstate {
|
|||
*/
|
||||
struct delegpt* dp;
|
||||
|
||||
/** state for 0x20 fallback when capsfail happens, 0 not a fallback */
|
||||
int caps_fallback;
|
||||
/** state for capsfail: current server number to try */
|
||||
size_t caps_server;
|
||||
/** state for capsfail: stored query for comparisons */
|
||||
struct reply_info* caps_reply;
|
||||
|
||||
/** Current delegation message - returned for non-RD queries */
|
||||
struct dns_msg* deleg_msg;
|
||||
|
||||
|
|
|
|||
|
|
@ -695,7 +695,7 @@ libworker_handle_reply(struct comm_point* c, void* arg, int error,
|
|||
e.qsent = NULL;
|
||||
|
||||
if(error != 0) {
|
||||
mesh_report_reply(lw->env->mesh, &e, 0, reply_info);
|
||||
mesh_report_reply(lw->env->mesh, &e, reply_info, error);
|
||||
return 0;
|
||||
}
|
||||
/* sanity check. */
|
||||
|
|
@ -705,10 +705,11 @@ libworker_handle_reply(struct comm_point* c, void* arg, int error,
|
|||
|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
mesh_report_reply(lw->env->mesh, &e, 0, reply_info);
|
||||
mesh_report_reply(lw->env->mesh, &e, reply_info,
|
||||
NETEVENT_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(lw->env->mesh, &e, 1, reply_info);
|
||||
mesh_report_reply(lw->env->mesh, &e, reply_info, NETEVENT_NOERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -720,7 +721,7 @@ libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
|||
struct libworker* lw = (struct libworker*)e->qstate->env->worker;
|
||||
|
||||
if(error != 0) {
|
||||
mesh_report_reply(lw->env->mesh, e, 0, reply_info);
|
||||
mesh_report_reply(lw->env->mesh, e, reply_info, error);
|
||||
return 0;
|
||||
}
|
||||
/* sanity check. */
|
||||
|
|
@ -730,10 +731,11 @@ libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
|||
|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
mesh_report_reply(lw->env->mesh, e, 0, reply_info);
|
||||
mesh_report_reply(lw->env->mesh, e, reply_info,
|
||||
NETEVENT_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(lw->env->mesh, e, 1, reply_info);
|
||||
mesh_report_reply(lw->env->mesh, e, reply_info, NETEVENT_NOERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -363,11 +363,16 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
|||
}
|
||||
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
int is_ok, struct comm_reply* reply)
|
||||
struct comm_reply* reply, int what)
|
||||
{
|
||||
enum module_ev event = module_event_reply;
|
||||
e->qstate->reply = reply;
|
||||
mesh_run(mesh, e->qstate->mesh_info,
|
||||
is_ok?module_event_reply:module_event_noreply, e);
|
||||
if(what != NETEVENT_NOERROR) {
|
||||
event = module_event_noreply;
|
||||
if(what == NETEVENT_CAPSFAIL)
|
||||
event = module_event_capsfail;
|
||||
}
|
||||
mesh_run(mesh, e->qstate->mesh_info, event, e);
|
||||
}
|
||||
|
||||
struct mesh_state*
|
||||
|
|
|
|||
|
|
@ -289,11 +289,11 @@ int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
|||
*
|
||||
* @param mesh: the query mesh.
|
||||
* @param e: outbound entry, with query state to run and reply pointer.
|
||||
* @param is_ok: if true, reply is OK, otherwise a timeout happened.
|
||||
* @param reply: the comm point reply info.
|
||||
* @param what: NETEVENT_* error code (if not 0, what is wrong, TIMEOUT).
|
||||
*/
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
int is_ok, struct comm_reply* reply);
|
||||
struct comm_reply* reply, int what);
|
||||
|
||||
/* ------------------- Functions for module environment --------------- */
|
||||
|
||||
|
|
|
|||
|
|
@ -1284,22 +1284,23 @@ serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
|
|||
== LDNS_RCODE_NOERROR ||
|
||||
LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
|
||||
== LDNS_RCODE_NXDOMAIN)) {
|
||||
verbose(VERB_OPS, "no qname in reply to check 0x20ID");
|
||||
log_addr(VERB_OPS, "from server",
|
||||
verbose(VERB_DETAIL, "no qname in reply to check 0x20ID");
|
||||
log_addr(VERB_DETAIL, "from server",
|
||||
&sq->addr, sq->addrlen);
|
||||
log_buf(VERB_OPS, "for packet", c->buffer);
|
||||
log_buf(VERB_DETAIL, "for packet", c->buffer);
|
||||
error = NETEVENT_CLOSED;
|
||||
c = NULL;
|
||||
} else if(ldns_buffer_read_u16_at(c->buffer, 4) > 0 &&
|
||||
!serviced_check_qname(c->buffer, sq->qbuf,
|
||||
sq->qbuflen)) {
|
||||
verbose(VERB_OPS, "wrong 0x20-ID in reply qname, "
|
||||
"answer dropped");
|
||||
log_addr(VERB_OPS, "from server",
|
||||
verbose(VERB_DETAIL, "wrong 0x20-ID in reply qname");
|
||||
log_addr(VERB_DETAIL, "from server",
|
||||
&sq->addr, sq->addrlen);
|
||||
log_buf(VERB_OPS, "for packet", c->buffer);
|
||||
error = NETEVENT_CLOSED;
|
||||
c = NULL;
|
||||
log_buf(VERB_DETAIL, "for packet", c->buffer);
|
||||
error = NETEVENT_CAPSFAIL;
|
||||
/* and cleanup too */
|
||||
pkt_dname_tolower(c->buffer,
|
||||
ldns_buffer_at(c->buffer, 12));
|
||||
} else {
|
||||
verbose(VERB_ALGO, "good 0x20-ID in reply qname");
|
||||
/* cleanup caps, prettier cache contents. */
|
||||
|
|
|
|||
BIN
testdata/fwd_capsid.tpkg
vendored
Normal file
BIN
testdata/fwd_capsid.tpkg
vendored
Normal file
Binary file not shown.
BIN
testdata/fwd_capsid_fallback.tpkg
vendored
Normal file
BIN
testdata/fwd_capsid_fallback.tpkg
vendored
Normal file
Binary file not shown.
|
|
@ -62,6 +62,7 @@ strmodulevent(enum module_ev e)
|
|||
case module_event_pass: return "module_event_pass";
|
||||
case module_event_reply: return "module_event_reply";
|
||||
case module_event_noreply: return "module_event_noreply";
|
||||
case module_event_capsfail: return "module_event_capsfail";
|
||||
case module_event_moddone: return "module_event_moddone";
|
||||
case module_event_error: return "module_event_error";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,8 @@ enum module_ev {
|
|||
module_event_reply,
|
||||
/** no reply, timeout or other error */
|
||||
module_event_noreply,
|
||||
/** reply is there, but capitalisation check failed */
|
||||
module_event_capsfail,
|
||||
/** next module is done, and its reply is awaiting you */
|
||||
module_event_moddone,
|
||||
/** error */
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ typedef int comm_point_callback_t(struct comm_point*, void*, int,
|
|||
#define NETEVENT_CLOSED -1
|
||||
/** to pass timeout happened to callback function */
|
||||
#define NETEVENT_TIMEOUT -2
|
||||
/** to pass fallback from capsforID to callback function; 0x20 failed */
|
||||
#define NETEVENT_CAPSFAIL -3
|
||||
|
||||
/**
|
||||
* A communication point dispatcher. Thread specific.
|
||||
|
|
|
|||
Loading…
Reference in a new issue