diff --git a/daemon/worker.c b/daemon/worker.c index 3c06c552b..19a1271dd 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -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; } diff --git a/doc/Changelog b/doc/Changelog index 069adddfb..6606ad501 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index d9ea574b1..4c8942199 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -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; irr_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; irrset_count; i++) { + if(!rrset_equal(p->rrsets[i], q->rrsets[i])) + return 0; + } + return 1; +} diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index 7055fbaa6..1d3872294 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -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 */ diff --git a/iterator/iterator.c b/iterator/iterator.c index 0decd27be..4f2a25f5b 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -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); @@ -1804,6 +1835,37 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, if(verbosity >= VERB_ALGO) 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); diff --git a/iterator/iterator.h b/iterator/iterator.h index 775e50d3c..f8b1b2ca0 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -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; diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 846d90b86..47e1b378f 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -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; } diff --git a/services/mesh.c b/services/mesh.c index 3b0f3b8d9..b3ad7f2a2 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -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* diff --git a/services/mesh.h b/services/mesh.h index 259002127..61c5bd111 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -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 --------------- */ diff --git a/services/outside_network.c b/services/outside_network.c index 2e6a185ff..003dfb902 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -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. */ diff --git a/testdata/fwd_capsid.tpkg b/testdata/fwd_capsid.tpkg new file mode 100644 index 000000000..da16f4316 Binary files /dev/null and b/testdata/fwd_capsid.tpkg differ diff --git a/testdata/fwd_capsid_fallback.tpkg b/testdata/fwd_capsid_fallback.tpkg new file mode 100644 index 000000000..a110759d1 Binary files /dev/null and b/testdata/fwd_capsid_fallback.tpkg differ diff --git a/util/module.c b/util/module.c index 60c9489ec..2079eecfe 100644 --- a/util/module.c +++ b/util/module.c @@ -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"; } diff --git a/util/module.h b/util/module.h index 43551ef20..831212b1f 100644 --- a/util/module.h +++ b/util/module.h @@ -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 */ diff --git a/util/netevent.h b/util/netevent.h index bc2b72d38..9cbe38886 100644 --- a/util/netevent.h +++ b/util/netevent.h @@ -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.