Successive suffix append retries

This commit is contained in:
Willem Toorop 2015-12-29 23:06:02 +01:00
parent 89b6c04d4f
commit 875ef3f9d4
4 changed files with 230 additions and 32 deletions

View File

@ -1866,7 +1866,7 @@ getdns_context_set_suffix(getdns_context *context, getdns_list *value)
else
break;
gldns_buffer_write_u8(&gbuf, 0);
gldns_buffer_write_u8(&gbuf, 1);
gldns_buffer_write_u8(&gbuf, 0);
if (gldns_buffer_begin(&gbuf) != buf_spc)

View File

@ -44,6 +44,7 @@
#include "util-internal.h"
#include "dnssec.h"
#include "stub.h"
#include "general.h"
/* cancel, cleanup and send timeout to callback */
static void
@ -72,6 +73,83 @@ void _getdns_call_user_callback(getdns_dns_req *dns_req,
context->processing = 0;
}
static int
no_answer(getdns_dns_req *dns_req)
{
getdns_network_req **netreq_p, *netreq;
int new_canonical = 0;
uint8_t canon_spc[256];
const uint8_t *canon;
size_t canon_len;
uint8_t owner_spc[256];
const uint8_t *owner;
size_t owner_len;
_getdns_rr_iter rr_spc, *rr;
_getdns_rdf_iter rdf_spc, *rdf;
for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) {
if (netreq->response_len == 0 ||
GLDNS_ANCOUNT(netreq->response) == 0)
continue;
canon = netreq->owner->name;
canon_len = netreq->owner->name_len;
if (netreq->request_type != GETDNS_RRTYPE_CNAME
&& GLDNS_ANCOUNT(netreq->response) > 1) do {
new_canonical = 0;
for ( rr = _getdns_rr_iter_init(&rr_spc
, netreq->response
, netreq->response_len)
; rr && _getdns_rr_iter_section(rr)
<= GLDNS_SECTION_ANSWER
; rr = _getdns_rr_iter_next(rr)) {
if (_getdns_rr_iter_section(rr) !=
GLDNS_SECTION_ANSWER)
continue;
if (gldns_read_uint16(rr->rr_type) !=
GETDNS_RRTYPE_CNAME)
continue;
owner = _getdns_owner_if_or_as_decompressed(
rr, owner_spc, &owner_len);
if (!_getdns_dname_equal(canon, owner))
continue;
if (!(rdf = _getdns_rdf_iter_init(
&rdf_spc, rr)))
continue;
canon = _getdns_rdf_if_or_as_decompressed(
rdf, canon_spc, &canon_len);
new_canonical = 1;
}
} while (new_canonical);
for ( rr = _getdns_rr_iter_init(&rr_spc
, netreq->response
, netreq->response_len)
; rr && _getdns_rr_iter_section(rr)
<= GLDNS_SECTION_ANSWER
; rr = _getdns_rr_iter_next(rr)) {
if (_getdns_rr_iter_section(rr) !=
GLDNS_SECTION_ANSWER)
continue;
if (gldns_read_uint16(rr->rr_type) !=
netreq->request_type)
continue;
owner = _getdns_owner_if_or_as_decompressed(
rr, owner_spc, &owner_len);
if (_getdns_dname_equal(canon, owner))
return 0;
}
}
return 1;
}
void
_getdns_check_dns_req_complete(getdns_dns_req *dns_req)
{
@ -85,6 +163,74 @@ _getdns_check_dns_req_complete(getdns_dns_req *dns_req)
else if (netreq->response_len > 0)
results_found = 1;
/* Do we have to check more suffixes on nxdomain/nodata?
*/
if (dns_req->suffix_appended && /* Something was appended */
dns_req->suffix_len > 1 && /* Next suffix available */
no_answer(dns_req)) {
/* Remove suffix from name */
dns_req->name_len -= dns_req->suffix_len - 1;
dns_req->name[dns_req->name_len - 1] = 0;
do {
dns_req->suffix += dns_req->suffix_len;
dns_req->suffix_len = *dns_req->suffix++;
if (dns_req->suffix_len + dns_req->name_len - 1 <
sizeof(dns_req->name)) {
memcpy(dns_req->name + dns_req->name_len - 1,
dns_req->suffix, dns_req->suffix_len);
dns_req->name_len += dns_req->suffix_len - 1;
dns_req->suffix_appended = 1;
break;
}
} while (dns_req->suffix_len > 1 && *dns_req->suffix);
if (dns_req->append_name == GETDNS_APPEND_NAME_ALWAYS ||
(dns_req->suffix_len > 1 && *dns_req->suffix)) {
for ( netreq_p = dns_req->netreqs
; (netreq = *netreq_p)
; netreq_p++ ) {
_getdns_netreq_reinit(netreq);
if (_getdns_submit_netreq(netreq))
netreq->state = NET_REQ_FINISHED;
}
_getdns_check_dns_req_complete(dns_req);
return;
}
} else if (
( dns_req->append_name ==
GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE ||
dns_req->append_name ==
GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE
) &&
!dns_req->suffix_appended &&
dns_req->suffix_len > 1 &&
no_answer(dns_req)) {
/* Initial suffix append */
for (
; dns_req->suffix_len > 1 && *dns_req->suffix
; dns_req->suffix += dns_req->suffix_len
, dns_req->suffix_len = *dns_req->suffix++) {
if (dns_req->suffix_len + dns_req->name_len - 1 <
sizeof(dns_req->name)) {
memcpy(dns_req->name + dns_req->name_len - 1,
dns_req->suffix, dns_req->suffix_len);
dns_req->name_len += dns_req->suffix_len - 1;
dns_req->suffix_appended = 1;
break;
}
}
if (dns_req->suffix_appended) {
for ( netreq_p = dns_req->netreqs
; (netreq = *netreq_p)
; netreq_p++ ) {
_getdns_netreq_reinit(netreq);
if (_getdns_submit_netreq(netreq))
netreq->state = NET_REQ_FINISHED;
}
_getdns_check_dns_req_complete(dns_req);
return;
}
}
if (dns_req->internal_cb)
dns_req->internal_cb(dns_req);
else if (! results_found)

View File

@ -110,6 +110,33 @@ network_req_cleanup(getdns_network_req *net_req)
GETDNS_FREE(net_req->owner->my_mf, net_req->response);
}
static uint8_t *
netreq_reset(getdns_network_req *net_req)
{
uint8_t *buf;
/* variables that need to be reset on reinit
*/
net_req->unbound_id = -1;
net_req->state = NET_REQ_NOT_SENT;
net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->query_id = 0;
net_req->response_len = 0;
/* Some fields to record info for return_call_reporting */
net_req->debug_start_time = 0;
net_req->debug_end_time = 0;
if (!net_req->query)
return NULL;
buf = net_req->query + GLDNS_HEADER_SIZE;
(void) memcpy(buf, net_req->owner->name, net_req->owner->name_len);
buf += net_req->owner->name_len;
gldns_write_uint16(buf, net_req->request_type);
gldns_write_uint16(buf + 2, net_req->owner->request_class);
return buf + 4;
}
static int
network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
uint16_t request_type, int dnssec_extension_set, int with_opt,
@ -125,48 +152,45 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
size_t i;
int r = 0;
/* variables that stay the same on reinit, don't touch
*/
net_req->request_type = request_type;
net_req->unbound_id = -1;
net_req->state = NET_REQ_NOT_SENT;
net_req->owner = owner;
net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->upstream = NULL;
net_req->fd = -1;
net_req->transport_count = owner->context->dns_transport_count;
net_req->transport_current = 0;
memcpy(net_req->transports, owner->context->dns_transports,
net_req->transport_count * sizeof(getdns_transport_list_t));
net_req->tls_auth_min = owner->context->tls_auth_min;
memset(&net_req->event, 0, sizeof(net_req->event));
memset(&net_req->tcp, 0, sizeof(net_req->tcp));
net_req->query_id = 0;
net_req->edns_maximum_udp_payload_size = edns_maximum_udp_payload_size;
net_req->max_udp_payload_size = edns_maximum_udp_payload_size != -1
? edns_maximum_udp_payload_size : 1432;
net_req->base_query_option_sz = opt_options_size;
net_req->wire_data_sz = wire_data_sz;
net_req->transport_count = owner->context->dns_transport_count;
memcpy(net_req->transports, owner->context->dns_transports,
net_req->transport_count * sizeof(getdns_transport_list_t));
net_req->tls_auth_min = owner->context->tls_auth_min;
/* state variables from the resolver, don't touch
*/
net_req->upstream = NULL;
net_req->fd = -1;
net_req->transport_current = 0;
memset(&net_req->event, 0, sizeof(net_req->event));
memset(&net_req->tcp, 0, sizeof(net_req->tcp));
net_req->keepalive_sent = 0;
net_req->write_queue_tail = NULL;
net_req->response_len = 0;
net_req->base_query_option_sz = opt_options_size;
/* Some fields to record info for return_call_reporting */
net_req->debug_start_time = 0;
net_req->debug_end_time = 0;
net_req->debug_tls_auth_status = 0;
net_req->debug_udp = 0;
net_req->wire_data_sz = wire_data_sz;
if (max_query_sz == 0) {
net_req->query = NULL;
net_req->opt = NULL;
net_req->response = net_req->wire_data;
netreq_reset(net_req);
return r;
}
/* first two bytes will contain query length (for tcp) */
buf = net_req->query = net_req->wire_data + 2;
net_req->query = net_req->wire_data + 2;
buf = net_req->query;
gldns_write_uint16(buf + 2, 0); /* reset all flags */
GLDNS_RD_SET(buf);
if (dnssec_extension_set) /* We will do validation ourselves */
@ -177,14 +201,7 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
gldns_write_uint16(buf + GLDNS_NSCOUNT_OFF, 0); /* 0 authorities */
gldns_write_uint16(buf + GLDNS_ARCOUNT_OFF, with_opt ? 1 : 0);
buf += GLDNS_HEADER_SIZE;
memcpy(buf, owner->name, owner->name_len);
buf += owner->name_len;
gldns_write_uint16(buf, request_type);
gldns_write_uint16(buf + 2, owner->request_class);
buf += 4;
buf = netreq_reset(net_req);
if (with_opt) {
net_req->opt = buf;
buf[0] = 0; /* dname for . */
@ -242,6 +259,39 @@ _getdns_network_req_clear_upstream_options(getdns_network_req * req)
}
}
void
_getdns_netreq_reinit(getdns_network_req *netreq)
{
uint8_t *base_opt_backup;
size_t base_opt_rr_sz;
if (!netreq->query) {
(void) netreq_reset(netreq);
return;
} else if (!netreq->opt) {
/* Remove TSIG (if any) */
gldns_write_uint16(netreq->query + GLDNS_ARCOUNT_OFF, 0);
netreq->response = netreq_reset(netreq);
gldns_write_uint16(netreq->wire_data,
netreq->response - netreq->query);
return;
}
_getdns_network_req_clear_upstream_options(netreq);
base_opt_rr_sz = netreq->base_query_option_sz + 11;
base_opt_backup = netreq->wire_data + netreq->wire_data_sz
- base_opt_rr_sz;
(void) memcpy(base_opt_backup, netreq->opt, base_opt_rr_sz);
netreq->opt = netreq_reset(netreq);
(void) memcpy(netreq->opt, base_opt_backup, base_opt_rr_sz);
netreq->response = netreq->opt + base_opt_rr_sz;
/* Remove TSIG (if any), but leave the opt RR */
gldns_write_uint16(netreq->query + GLDNS_ARCOUNT_OFF, 1);
gldns_write_uint16(netreq->wire_data,
netreq->response - netreq->query);
}
/* add_upstream_option appends an option that is derived at send time.
(you can send data as NULL and it will fill with all zeros) */
getdns_return_t

View File

@ -398,5 +398,7 @@ size_t _getdns_network_req_add_tsig(getdns_network_req *req);
void _getdns_network_validate_tsig(getdns_network_req *req);
void _getdns_netreq_reinit(getdns_network_req *netreq);
#endif
/* types-internal.h */