From: Wouter Wijngaards Date: Tue, 6 Apr 2010 08:35:37 +0000 (+0000) Subject: - Fix EDNS probe for .de DNSSEC testbed failure, where the infra X-Git-Tag: release-1.4.4rc1~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=18a7df3d5ce88940340de29ba1f4f9482a8bd037;p=thirdparty%2Funbound.git - Fix EDNS probe for .de DNSSEC testbed failure, where the infra cache timeout coincided with a server update, the current EDNS backoff is less sensitive, and does not cache the backoff unless the backoff actually works and the domain is not expecting DNSSEC. git-svn-id: file:///svn/unbound/trunk@2063 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index 76e513533..db4712b2b 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1248,7 +1248,7 @@ outbound_entry_compare(void* a, void* b) struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype, - uint16_t qclass, uint16_t flags, int dnssec, + uint16_t qclass, uint16_t flags, int dnssec, int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen, struct module_qstate* q) { @@ -1259,9 +1259,9 @@ worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype, return NULL; e->qstate = q; e->qsent = outnet_serviced_query(worker->back, qname, - qnamelen, qtype, qclass, flags, dnssec, addr, addrlen, - worker_handle_service_reply, e, worker->back->udp_buff, - &outbound_entry_compare); + qnamelen, qtype, qclass, flags, dnssec, want_dnssec, + addr, addrlen, worker_handle_service_reply, e, + worker->back->udp_buff, &outbound_entry_compare); if(!e->qsent) { return NULL; } diff --git a/daemon/worker.h b/daemon/worker.h index 2116d54d8..b1fa95eba 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -189,6 +189,7 @@ int worker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr, * @param qclass: query class. (host order) * @param flags: host order flags word, with opcode and CD bit. * @param dnssec: if set, EDNS record will have DO bit set. + * @param want_dnssec: signatures needed. * @param addr: where to. * @param addrlen: length of addr. * @param q: wich query state to reactivate upon return. @@ -197,7 +198,7 @@ int worker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr, */ struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, - struct sockaddr_storage* addr, socklen_t addrlen, + int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen, struct module_qstate* q); /** diff --git a/iterator/iterator.c b/iterator/iterator.c index d8b5588a7..e8345c804 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -1521,7 +1521,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->qchase.qname, iq->qchase.qname_len, iq->qchase.qtype, iq->qchase.qclass, iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), EDNS_DO|BIT_CD, - &target->addr, target->addrlen, qstate); + iq->dnssec_expected, &target->addr, target->addrlen, qstate); if(!outq) { log_addr(VERB_DETAIL, "error sending query to auth server", &target->addr, target->addrlen); diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 0c28b7615..ae15c3d2e 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -696,7 +696,7 @@ outbound_entry_compare(void* a, void* b) struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, - struct sockaddr_storage* addr, socklen_t addrlen, + int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen, struct module_qstate* q) { struct libworker* w = (struct libworker*)q->env->worker; @@ -706,9 +706,9 @@ struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen, return NULL; e->qstate = q; e->qsent = outnet_serviced_query(w->back, qname, - qnamelen, qtype, qclass, flags, dnssec, addr, addrlen, - libworker_handle_service_reply, e, w->back->udp_buff, - &outbound_entry_compare); + qnamelen, qtype, qclass, flags, dnssec, want_dnssec, + addr, addrlen, libworker_handle_service_reply, e, + w->back->udp_buff, &outbound_entry_compare); if(!e->qsent) { return NULL; } diff --git a/libunbound/libworker.h b/libunbound/libworker.h index 74ae89443..192094d8d 100644 --- a/libunbound/libworker.h +++ b/libunbound/libworker.h @@ -129,6 +129,7 @@ int libworker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr, * @param qclass: query class. (host order) * @param flags: host order flags word, with opcode and CD bit. * @param dnssec: if set, EDNS record will have DO bit set. + * @param want_dnssec: signatures needed. * @param addr: where to. * @param addrlen: length of addr. * @param q: wich query state to reactivate upon return. @@ -137,7 +138,7 @@ int libworker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr, */ struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, - struct sockaddr_storage* addr, socklen_t addrlen, + int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen, struct module_qstate* q); /** process incoming replies from the network */ diff --git a/services/outside_network.c b/services/outside_network.c index e1c5265d7..3725c636b 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -1077,7 +1077,7 @@ lookup_serviced(struct outside_network* outnet, ldns_buffer* buff, int dnssec, /** Create new serviced entry */ static struct serviced_query* serviced_create(struct outside_network* outnet, ldns_buffer* buff, int dnssec, - struct sockaddr_storage* addr, socklen_t addrlen) + int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen) { struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq)); rbnode_t* ins; @@ -1091,6 +1091,7 @@ serviced_create(struct outside_network* outnet, ldns_buffer* buff, int dnssec, } sq->qbuflen = ldns_buffer_limit(buff); sq->dnssec = dnssec; + sq->want_dnssec = want_dnssec; memcpy(&sq->addr, addr, addrlen); sq->addrlen = addrlen; sq->outnet = outnet; @@ -1132,7 +1133,8 @@ serviced_delete(struct serviced_query* sq) /* clear up the pending query */ if(sq->status == serviced_query_UDP_EDNS || sq->status == serviced_query_UDP || - sq->status == serviced_query_PROBE_EDNS) { + sq->status == serviced_query_PROBE_EDNS || + sq->status == serviced_query_UDP_EDNS_fallback) { struct pending* p = (struct pending*)sq->pending; if(p->pc) portcomm_loweruse(sq->outnet, p->pc); @@ -1409,12 +1411,25 @@ serviced_tcp_callback(struct comm_point* c, void* arg, int error, (LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) == LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(ldns_buffer_begin( c->buffer)) == LDNS_RCODE_NOTIMPL) ) { - if(!infra_edns_update(sq->outnet->infra, &sq->addr, + /* attempt to fallback to nonEDNS */ + sq->status = serviced_query_TCP_EDNS_fallback; + serviced_tcp_initiate(sq->outnet, sq, c->buffer); + return 0; + } else if(error==NETEVENT_NOERROR && + sq->status == serviced_query_TCP_EDNS_fallback && + (LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) == + LDNS_RCODE_NOERROR || LDNS_RCODE_WIRE( + ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NXDOMAIN + || LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) + == LDNS_RCODE_YXDOMAIN)) { + /* the fallback produced a result that looks promising, note + * that this server should be approached without EDNS */ + /* only store noEDNS in cache if domain is noDNSSEC */ + if(!sq->want_dnssec) + if(!infra_edns_update(sq->outnet->infra, &sq->addr, sq->addrlen, -1, *sq->outnet->now_secs)) log_err("Out of memory caching no edns for host"); sq->status = serviced_query_TCP; - serviced_tcp_initiate(sq->outnet, sq, c->buffer); - return 0; } /* insert address into reply info */ if(!rep) { @@ -1504,43 +1519,33 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error, serviced_callbacks(sq, NETEVENT_TIMEOUT, c, rep); return 0; } + } else if(error != NETEVENT_NOERROR) { + /* udp returns error (due to no ID or interface available) */ + serviced_callbacks(sq, error, c, rep); + return 0; } - if(error == NETEVENT_NOERROR && sq->status == serviced_query_UDP_EDNS + if(sq->status == serviced_query_UDP_EDNS && (LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) == LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE( ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL)) { - /* note no EDNS, fallback without EDNS */ - if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, - -1, (uint32_t)now.tv_sec)) { - log_err("Out of memory caching no edns for host"); - } - sq->status = serviced_query_UDP; + /* try to get an answer by falling back without EDNS */ + sq->status = serviced_query_UDP_EDNS_fallback; sq->retry = 0; if(!serviced_udp_send(sq, c->buffer)) { serviced_callbacks(sq, NETEVENT_CLOSED, c, rep); } return 0; - } - if(LDNS_TC_WIRE(ldns_buffer_begin(c->buffer)) || - (error != NETEVENT_NOERROR && fallback_tcp) ) { - /* fallback to TCP */ - /* this discards partial UDP contents */ - if(sq->status == serviced_query_UDP_EDNS) - sq->status = serviced_query_TCP_EDNS; - else sq->status = serviced_query_TCP; - serviced_tcp_initiate(outnet, sq, c->buffer); - return 0; - } - /* yay! an answer */ - if(sq->status == serviced_query_PROBE_EDNS) { + } else if(sq->status == serviced_query_PROBE_EDNS) { /* probe without EDNS succeeds, so we conclude that this * host likely has EDNS packets dropped */ log_addr(VERB_DETAIL, "timeouts, concluded that connection to " "host drops EDNS packets", &sq->addr, sq->addrlen); - if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, + /* only store noEDNS in cache if domain is noDNSSEC */ + if(!sq->want_dnssec) + if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, -1, (uint32_t)now.tv_sec)) { log_err("Out of memory caching no edns for host"); - } + } sq->status = serviced_query_UDP; } else if(sq->status == serviced_query_UDP_EDNS && !sq->edns_lame_known) { @@ -1550,6 +1555,21 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error, log_err("Out of memory caching edns works"); } sq->edns_lame_known = 1; + } else if(sq->status == serviced_query_UDP_EDNS_fallback && + !sq->edns_lame_known && (LDNS_RCODE_WIRE( + ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOERROR || + LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) == + LDNS_RCODE_NXDOMAIN || LDNS_RCODE_WIRE(ldns_buffer_begin( + c->buffer)) == LDNS_RCODE_YXDOMAIN)) { + /* the fallback produced a result that looks promising, note + * that this server should be approached without EDNS */ + /* only store noEDNS in cache if domain is noDNSSEC */ + if(!sq->want_dnssec) + if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, + -1, (uint32_t)now.tv_sec)) { + log_err("Out of memory caching no edns for host"); + } + sq->status = serviced_query_UDP; } if(now.tv_sec > sq->last_sent_time.tv_sec || (now.tv_sec == sq->last_sent_time.tv_sec && @@ -1563,6 +1583,20 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error, roundtime, sq->last_rtt, (uint32_t)now.tv_sec)) log_err("out of memory noting rtt."); } + /* perform TC flag check and TCP fallback after updating our + * cache entries for EDNS status and RTT times */ + if(LDNS_TC_WIRE(ldns_buffer_begin(c->buffer)) || fallback_tcp) { + /* fallback to TCP */ + /* this discards partial UDP contents */ + if(sq->status == serviced_query_UDP_EDNS || + sq->status == serviced_query_UDP_EDNS_fallback) + /* if we have unfinished EDNS_fallback, start again */ + sq->status = serviced_query_TCP_EDNS; + else sq->status = serviced_query_TCP; + serviced_tcp_initiate(outnet, sq, c->buffer); + return 0; + } + /* yay! an answer */ serviced_callbacks(sq, error, c, rep); return 0; } @@ -1583,10 +1617,10 @@ callback_list_find(struct serviced_query* sq, void* cb_arg, struct serviced_query* outnet_serviced_query(struct outside_network* outnet, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, struct sockaddr_storage* addr, - socklen_t addrlen, comm_point_callback_t* callback, - void* callback_arg, ldns_buffer* buff, - int (*arg_compare)(void*,void*)) + uint16_t flags, int dnssec, int want_dnssec, + struct sockaddr_storage* addr, socklen_t addrlen, + comm_point_callback_t* callback, void* callback_arg, + ldns_buffer* buff, int (*arg_compare)(void*,void*)) { struct serviced_query* sq; struct service_callback* cb; @@ -1602,7 +1636,8 @@ outnet_serviced_query(struct outside_network* outnet, return NULL; if(!sq) { /* make new serviced query entry */ - sq = serviced_create(outnet, buff, dnssec, addr, addrlen); + sq = serviced_create(outnet, buff, dnssec, want_dnssec, + addr, addrlen); if(!sq) { free(cb); return NULL; @@ -1754,7 +1789,8 @@ serviced_get_mem(struct serviced_query* sq) s += sizeof(*sb); if(sq->status == serviced_query_UDP_EDNS || sq->status == serviced_query_UDP || - sq->status == serviced_query_PROBE_EDNS) { + sq->status == serviced_query_PROBE_EDNS || + sq->status == serviced_query_UDP_EDNS_fallback) { s += sizeof(struct pending); s += comm_timer_get_mem(NULL); } else { diff --git a/services/outside_network.h b/services/outside_network.h index 342682524..f918ff182 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -291,6 +291,8 @@ struct serviced_query { size_t qbuflen; /** If an EDNS section is included, the DO/CD bit will be turned on. */ int dnssec; + /** We want signatures, or else the answer is likely useless */ + int want_dnssec; /** where to send it */ struct sockaddr_storage addr; /** length of addr field in use. */ @@ -308,7 +310,11 @@ struct serviced_query { /** TCP without EDNS sent */ serviced_query_TCP, /** probe to test EDNS lameness (EDNS is dropped) */ - serviced_query_PROBE_EDNS + serviced_query_PROBE_EDNS, + /** probe to test noEDNS0 (EDNS gives FORMERRorNOTIMP) */ + serviced_query_UDP_EDNS_fallback, + /** probe to test TCP noEDNS0 (EDNS gives FORMERRorNOTIMP) */ + serviced_query_TCP_EDNS_fallback } /** variable with current status */ status; @@ -427,6 +433,8 @@ void pending_delete(struct outside_network* outnet, struct pending* p); * @param dnssec: if set, DO bit is set in EDNS queries. * If the value includes BIT_CD, CD bit is set when in EDNS queries. * If the value includes BIT_DO, DO bit is set when in EDNS queries. + * @param want_dnssec: signatures are needed, without EDNS the answer is + * likely to be useless. * @param callback: callback function. * @param callback_arg: user argument to callback function. * @param addr: to which server to send the query. @@ -439,10 +447,10 @@ void pending_delete(struct outside_network* outnet, struct pending* p); */ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, struct sockaddr_storage* addr, - socklen_t addrlen, comm_point_callback_t* callback, - void* callback_arg, ldns_buffer* buff, - int (*arg_compare)(void*,void*)); + uint16_t flags, int dnssec, int want_dnssec, + struct sockaddr_storage* addr, socklen_t addrlen, + comm_point_callback_t* callback, void* callback_arg, + ldns_buffer* buff, int (*arg_compare)(void*,void*)); /** * Remove service query callback. diff --git a/smallapp/worker_cb.c b/smallapp/worker_cb.c index 78ee01963..b72c56130 100644 --- a/smallapp/worker_cb.c +++ b/smallapp/worker_cb.c @@ -112,7 +112,8 @@ int worker_send_packet(ldns_buffer* ATTR_UNUSED(pkt), struct outbound_entry* worker_send_query(uint8_t* ATTR_UNUSED(qname), size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype), uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags), - int ATTR_UNUSED(dnssec), struct sockaddr_storage* ATTR_UNUSED(addr), + int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec), + struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), struct module_qstate* ATTR_UNUSED(q)) { log_assert(0); diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 29135c410..4fffccb26 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -988,10 +988,10 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet, struct serviced_query* outnet_serviced_query(struct outside_network* outnet, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, struct sockaddr_storage* addr, - socklen_t addrlen, comm_point_callback_t* callback, - void* callback_arg, ldns_buffer* ATTR_UNUSED(buff), - int (*arg_compare)(void*,void*)) + uint16_t flags, int dnssec, int ATTR_UNUSED(want_dnssec), + struct sockaddr_storage* addr, socklen_t addrlen, + comm_point_callback_t* callback, void* callback_arg, + ldns_buffer* ATTR_UNUSED(buff), int (*arg_compare)(void*,void*)) { struct replay_runtime* runtime = (struct replay_runtime*)outnet->base; struct fake_pending* pend = (struct fake_pending*)calloc(1, diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 5d96382d8..d57d96c7a 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -258,8 +258,9 @@ fptr_whitelist_modenv_send_packet(int (*fptr)(ldns_buffer* pkt, int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)( uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, struct sockaddr_storage* addr, - socklen_t addrlen, struct module_qstate* q)) + uint16_t flags, int dnssec, int want_dnssec, + struct sockaddr_storage* addr, socklen_t addrlen, + struct module_qstate* q)) { if(fptr == &worker_send_query) return 1; else if(fptr == &libworker_send_query) return 1; diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index 331626df4..7301cbf3a 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -201,8 +201,9 @@ int fptr_whitelist_modenv_send_packet(int (*fptr)(ldns_buffer* pkt, */ int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)( uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, struct sockaddr_storage* addr, - socklen_t addrlen, struct module_qstate* q)); + uint16_t flags, int dnssec, int want_dnssec, + struct sockaddr_storage* addr, socklen_t addrlen, + struct module_qstate* q)); /** * Check function pointer whitelist for module_env detach_subs callback values. diff --git a/util/module.h b/util/module.h index f2ff7b1be..89dfffd85 100644 --- a/util/module.h +++ b/util/module.h @@ -114,6 +114,8 @@ struct module_env { * @param dnssec: if set, EDNS record will have bits set. * If EDNS_DO bit is set, DO bit is set in EDNS records. * If BIT_CD is set, CD bit is set in queries with EDNS records. + * @param want_dnssec: if set, the validator wants DNSSEC. Without + * EDNS, the answer is likely to be useless for this domain. * @param addr: where to. * @param addrlen: length of addr. * @param q: wich query state to reactivate upon return. @@ -124,8 +126,8 @@ struct module_env { */ struct outbound_entry* (*send_query)(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, - struct sockaddr_storage* addr, socklen_t addrlen, - struct module_qstate* q); + int want_dnssec, struct sockaddr_storage* addr, + socklen_t addrlen, struct module_qstate* q); /** * Detach-subqueries.