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)
{
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;
}
* @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.
*/
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);
/**
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);
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;
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;
}
* @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.
*/
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 */
/** 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;
}
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;
/* 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);
(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) {
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) {
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 &&
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;
}
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;
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;
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 {
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. */
/** 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;
* @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.
*/
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.
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);
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,
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;
*/
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.
* @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.
*/
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.