From 875ef3f9d450637bc7d4a6b3e24df6d33095e2f6 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 29 Dec 2015 23:06:02 +0100 Subject: [PATCH] Successive suffix append retries --- src/context.c | 2 +- src/general.c | 146 +++++++++++++++++++++++++++++++++++++++++ src/request-internal.c | 112 ++++++++++++++++++++++--------- src/types-internal.h | 2 + 4 files changed, 230 insertions(+), 32 deletions(-) diff --git a/src/context.c b/src/context.c index 03fba0d1..d796c909 100644 --- a/src/context.c +++ b/src/context.c @@ -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) diff --git a/src/general.c b/src/general.c index 4d485093..705d904f 100644 --- a/src/general.c +++ b/src/general.c @@ -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) diff --git a/src/request-internal.c b/src/request-internal.c index d8e607af..c75b947e 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -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 diff --git a/src/types-internal.h b/src/types-internal.h index b00ae9f4..49b631a9 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -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 */