diff --git a/src/context.c b/src/context.c index 491b544f..d796c909 100644 --- a/src/context.c +++ b/src/context.c @@ -98,12 +98,14 @@ getdns_port_array[GETDNS_UPSTREAM_TRANSPORTS] = { GETDNS_PORT_DNS_OVER_TLS }; -char* +static char* getdns_port_str_array[] = { GETDNS_STR_PORT_DNS, GETDNS_STR_PORT_DNS_OVER_TLS }; +static const uint8_t no_suffixes[] = { 1, 0 }; + /* Private functions */ static getdns_return_t create_default_namespaces(struct getdns_context *context); static getdns_return_t create_default_dns_transports(struct getdns_context *context); @@ -717,7 +719,7 @@ upstream_init(getdns_upstream *upstream, static getdns_return_t set_os_defaults_windows(struct getdns_context *context) { - char domain[1024]; + char domain[1024] = ""; size_t upstreams_limit = 10, length; struct getdns_bindata bindata; struct addrinfo hints; @@ -737,7 +739,6 @@ set_os_defaults_windows(struct getdns_context *context) } _getdns_filechg_check(context, context->fchg_resolvconf); - context->suffix = getdns_list_create_with_context(context); context->upstreams = upstreams_create(context, upstreams_limit); memset(&hints, 0, sizeof(struct addrinfo)); @@ -786,13 +787,6 @@ set_os_defaults_windows(struct getdns_context *context) } free(info); } - - (void)getdns_list_get_length(context->suffix, &length); - if (length == 0 && *domain != 0) { - bindata.data = (uint8_t *)domain; - bindata.size = strlen(domain) + 1; - (void)getdns_list_set_bindata(context->suffix, 0, &bindata); - } return GETDNS_RETURN_GOOD; } /* set_os_defaults_windows */ #else @@ -806,6 +800,7 @@ set_os_defaults(struct getdns_context *context) struct addrinfo hints; struct addrinfo *result; getdns_upstream *upstream; + getdns_list *suffix; int s; if(context->fchg_resolvconf == NULL) { @@ -830,7 +825,7 @@ set_os_defaults(struct getdns_context *context) upstream_count++; fclose(in); - context->suffix = getdns_list_create_with_context(context); + suffix = getdns_list_create_with_context(context); context->upstreams = upstreams_create( context, upstream_count * GETDNS_UPSTREAM_TRANSPORTS); @@ -871,7 +866,7 @@ set_os_defaults(struct getdns_context *context) prev_ch = *token; *token = 0; - _getdns_list_append_string(context->suffix, parse); + _getdns_list_append_string(suffix, parse); *token = prev_ch; parse = token; @@ -902,9 +897,12 @@ set_os_defaults(struct getdns_context *context) } fclose(in); - (void) getdns_list_get_length(context->suffix, &length); + (void) getdns_list_get_length(suffix, &length); if (length == 0 && *domain != 0) - _getdns_list_append_string(context->suffix, domain); + _getdns_list_append_string(suffix, domain); + (void )getdns_context_set_suffix(context, suffix); + getdns_list_destroy(suffix); + return GETDNS_RETURN_GOOD; } /* set_os_defaults */ #endif @@ -1008,7 +1006,8 @@ getdns_context_create_with_extended_memory_functions( result->dns_root_servers = NULL; result->root_servers_fn[0] = 0; result->append_name = GETDNS_APPEND_NAME_ALWAYS; - result->suffix = NULL; + result->suffixes = no_suffixes; + result->suffixes_len = sizeof(no_suffixes); gldns_buffer_init_frm_data(&gbuf, result->trust_anchors_spc , sizeof(result->trust_anchors_spc)); @@ -1181,7 +1180,8 @@ getdns_context_destroy(struct getdns_context *context) if (context->root_servers_fn[0]) unlink(context->root_servers_fn); - getdns_list_destroy(context->suffix); + if (context->suffixes && context->suffixes != no_suffixes) + GETDNS_FREE(context->mf, (void *)context->suffixes); if (context->trust_anchors && context->trust_anchors != context->trust_anchors_spc) @@ -1813,26 +1813,90 @@ getdns_context_set_append_name(struct getdns_context *context, * */ getdns_return_t -getdns_context_set_suffix(struct getdns_context *context, struct getdns_list * value) +getdns_context_set_suffix(getdns_context *context, getdns_list *value) { - struct getdns_list *copy = NULL; - RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - if (context->resolution_type_set != 0) { - /* already setup */ - return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; - } - if (value != NULL) { - if (_getdns_list_copy(value, ©) != GETDNS_RETURN_GOOD) { - return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; - } - value = copy; - } - getdns_list_destroy(context->suffix); - context->suffix = value; + getdns_return_t r; + size_t i; + gldns_buffer gbuf; + uint8_t buf_spc[1024], *suffixes = NULL; + size_t suffixes_len = 0; + uint8_t dname[256]; + size_t dname_len; + char name_spc[1025], *name; + getdns_bindata *bindata; - dispatch_updated(context, GETDNS_CONTEXT_CODE_SUFFIX); + if (!context) + return GETDNS_RETURN_INVALID_PARAMETER; - return GETDNS_RETURN_GOOD; + if (value == NULL) { + if (context->suffixes && context->suffixes != no_suffixes) + GETDNS_FREE(context->mf, (void *)context->suffixes); + + context->suffixes = no_suffixes; + context->suffixes_len = sizeof(no_suffixes); + return GETDNS_RETURN_GOOD; + } + gldns_buffer_init_frm_data(&gbuf, buf_spc, sizeof(buf_spc)); + for (;;) { + for ( i = 0 + ; !(r = getdns_list_get_bindata(value, i, &bindata)) + ; i++) { + + if (bindata->size == 0 || bindata->size >= sizeof(name_spc)) + continue; + + if (bindata->data[bindata->size-1] != 0) { + /* Unterminated string */ + (void) memcpy(name_spc, bindata->data, bindata->size); + name_spc[bindata->size] = 0; + name = name_spc; + } else + /* Terminated string */ + name = (char *)bindata->data; + + dname_len = sizeof(dname); + if (gldns_str2wire_dname_buf(name, dname, &dname_len)) + return GETDNS_RETURN_GENERIC_ERROR; + + gldns_buffer_write_u8(&gbuf, dname_len); + gldns_buffer_write(&gbuf, dname, dname_len); + } + if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM) + r = GETDNS_RETURN_GOOD; + else + break; + + gldns_buffer_write_u8(&gbuf, 1); + gldns_buffer_write_u8(&gbuf, 0); + + if (gldns_buffer_begin(&gbuf) != buf_spc) + break; + + suffixes_len = gldns_buffer_position(&gbuf); + if (!(suffixes = GETDNS_XMALLOC( + context->mf, uint8_t, suffixes_len))) { + r = GETDNS_RETURN_MEMORY_ERROR; + break; + } + if (suffixes_len <= gldns_buffer_limit(&gbuf)) { + (void) memcpy (suffixes, buf_spc, suffixes_len); + break; + } + gldns_buffer_init_frm_data(&gbuf, suffixes, suffixes_len); + } + if (r) { + if (gldns_buffer_begin(&gbuf) != buf_spc) + GETDNS_FREE(context->mf, suffixes); + return r; + } + if (context->suffixes && context->suffixes != no_suffixes) + GETDNS_FREE(context->mf, (void *)context->suffixes); + + context->suffixes = suffixes; + context->suffixes_len = suffixes_len; + + dispatch_updated(context, GETDNS_CONTEXT_CODE_SUFFIX); + return GETDNS_RETURN_GOOD; } /* getdns_context_set_suffix */ /* @@ -2840,7 +2904,7 @@ _get_context_settings(getdns_context* context) { getdns_return_t r = GETDNS_RETURN_GOOD; getdns_dict* result = getdns_dict_create_with_context(context); - getdns_list *upstreams; + getdns_list *list; if (!result) { return NULL; @@ -2858,12 +2922,14 @@ _get_context_settings(getdns_context* context) r |= getdns_dict_set_int(result, "edns_do_bit", context->edns_do_bit); r |= getdns_dict_set_int(result, "append_name", context->append_name); /* list fields */ - if (context->suffix) r |= getdns_dict_set_list(result, "suffix", context->suffix); - - if (!getdns_context_get_upstream_recursive_servers(context, &upstreams)) { + if (!getdns_context_get_suffix(context, &list)) { + r |= getdns_dict_set_list(result, "suffix", list); + getdns_list_destroy(list); + } + if (!getdns_context_get_upstream_recursive_servers(context, &list)) { r |= getdns_dict_set_list(result, "upstream_recursive_servers", - upstreams); - getdns_list_destroy(upstreams); + list); + getdns_list_destroy(list); } if (context->dns_transport_count > 0) { /* create a namespace list */ @@ -3179,14 +3245,41 @@ getdns_context_get_append_name(getdns_context *context, } getdns_return_t -getdns_context_get_suffix(getdns_context *context, getdns_list **value) { - RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER); - *value = NULL; - if (context->suffix) { - return _getdns_list_copy(context->suffix, value); - } - return GETDNS_RETURN_GOOD; +getdns_context_get_suffix(getdns_context *context, getdns_list **value) +{ + size_t dname_len; + const uint8_t *dname; + char name[1024]; + getdns_return_t r = GETDNS_RETURN_GOOD; + getdns_list *list; + + if (!context || !value) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (!(list = getdns_list_create_with_context(context))) + return GETDNS_RETURN_MEMORY_ERROR; + + assert(context->suffixes); + dname_len = context->suffixes[0]; + dname = context->suffixes + 1; + while (dname_len && *dname) { + if (! gldns_wire2str_dname_buf((UNCONST_UINT8_p) + dname, dname_len, name, sizeof(name))) { + r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + if ((r = _getdns_list_append_const_bindata( + list, strlen(name) + 1, name))) + break; + dname += dname_len; + dname_len = *dname++; + } + if (r) + getdns_list_destroy(list); + else + *value = list; + + return GETDNS_RETURN_GOOD; } getdns_return_t diff --git a/src/context.h b/src/context.h index 5b0ac06d..340ac375 100644 --- a/src/context.h +++ b/src/context.h @@ -179,7 +179,13 @@ struct getdns_context { getdns_list *dns_root_servers; char root_servers_fn[FILENAME_MAX]; getdns_append_name_t append_name; - struct getdns_list *suffix; + /* Suffix buffer containing a list of (length byte | dname) where + * length bytes contains the length of the following dname. + * The last dname should be the zero byte. + */ + const uint8_t *suffixes; + /* Length of all suffixes in the suffix buffer */ + size_t suffixes_len; uint8_t *trust_anchors; size_t trust_anchors_len; getdns_upstreams *upstreams; diff --git a/src/dict.c b/src/dict.c index a8e55595..f543e4bc 100644 --- a/src/dict.c +++ b/src/dict.c @@ -280,7 +280,8 @@ getdns_dict_get_names(const getdns_dict *dict, getdns_list **answer) RBTREE_FOR(item, struct getdns_dict_item *, (_getdns_rbtree_t *)&(dict->root)) { - _getdns_list_append_string(*answer, item->node.key); + _getdns_list_append_const_bindata(*answer, + strlen(item->node.key) + 1, item->node.key); } return GETDNS_RETURN_GOOD; } /* getdns_dict_get_names */ @@ -578,7 +579,7 @@ getdns_dict_set_list( /*---------------------------------------- getdns_dict_set_bindata */ getdns_return_t _getdns_dict_set_const_bindata( - getdns_dict *dict, const char *name, size_t size, const uint8_t *data) + getdns_dict *dict, const char *name, size_t size, const void *data) { getdns_item *item; getdns_bindata *newbindata; @@ -612,28 +613,9 @@ getdns_dict_set_bindata( getdns_return_t getdns_dict_util_set_string(getdns_dict *dict, char *name, const char *value) { - getdns_item *item; - getdns_bindata *newbindata; - getdns_return_t r; - - if (!dict || !name || !value) - return GETDNS_RETURN_INVALID_PARAMETER; - - if (!(newbindata = GETDNS_MALLOC(dict->mf, getdns_bindata))) - return GETDNS_RETURN_MEMORY_ERROR; - - newbindata->size = strlen(value); - if (!(newbindata->data = (void *)_getdns_strdup(&dict->mf, value))) { - GETDNS_FREE(dict->mf, newbindata); - return GETDNS_RETURN_MEMORY_ERROR; - } - if ((r = _getdns_dict_find_and_add(dict, name, &item))) { - _getdns_bindata_destroy(&dict->mf, newbindata); - return r; - } - item->dtype = t_bindata; - item->data.bindata = newbindata; - return GETDNS_RETURN_GOOD; + return value + ? _getdns_dict_set_const_bindata(dict, name, strlen(value), value) + : GETDNS_RETURN_INVALID_PARAMETER; } /* getdns_dict_util_set_dict */ /*---------------------------------------- getdns_dict_set_int */ diff --git a/src/dnssec.c b/src/dnssec.c index 28455678..a1b5e8d8 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -3269,7 +3269,7 @@ void _getdns_get_validation_chain(getdns_dns_req *dnsreq) , netreq->response, netreq->response_len , netreq->owner->name , netreq->request_type - , netreq->request_class + , netreq->owner->request_class , netreq ); } diff --git a/src/general.c b/src/general.c index 9ac63f46..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) @@ -173,7 +319,7 @@ _getdns_submit_netreq(getdns_network_req *netreq) #ifdef HAVE_LIBUNBOUND return ub_resolve_async(dns_req->context->unbound_ctx, - name, netreq->request_type, netreq->request_class, + name, netreq->request_type, netreq->owner->request_class, netreq, ub_resolve_callback, &(netreq->unbound_id)) ? GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; #else diff --git a/src/list.c b/src/list.c index 784e6715..487b6bd2 100644 --- a/src/list.c +++ b/src/list.c @@ -554,7 +554,7 @@ getdns_list_set_list( /*---------------------------------------- getdns_list_set_bindata */ static getdns_return_t _getdns_list_set_const_bindata( - getdns_list *list, size_t index, size_t size, const uint8_t *data) + getdns_list *list, size_t index, size_t size, const void *data) { getdns_bindata *newbindata; getdns_return_t r; @@ -587,28 +587,9 @@ getdns_list_set_bindata( static getdns_return_t getdns_list_set_string(getdns_list *list, size_t index, const char *value) { - getdns_bindata *newbindata; - getdns_return_t r; - - if (!list || !value) - return GETDNS_RETURN_INVALID_PARAMETER; - - if (!(newbindata = GETDNS_MALLOC(list->mf, getdns_bindata))) - return GETDNS_RETURN_MEMORY_ERROR; - - newbindata->size = strlen(value); - if (!(newbindata->data = (void *)_getdns_strdup(&list->mf, value))) { - GETDNS_FREE(list->mf, newbindata); - return GETDNS_RETURN_MEMORY_ERROR; - } - if ((r = _getdns_list_request_index(list, index))) { - GETDNS_FREE(list->mf, newbindata->data); - GETDNS_FREE(list->mf, newbindata); - return r; - } - list->items[index].dtype = t_bindata; - list->items[index].data.bindata = newbindata; - return GETDNS_RETURN_GOOD; + return value + ? _getdns_list_set_const_bindata(list, index, strlen(value), value) + : GETDNS_RETURN_INVALID_PARAMETER; } /* getdns_list_set_string */ /*---------------------------------------- getdns_list_set_int */ @@ -648,7 +629,7 @@ _getdns_list_append_bindata(getdns_list *list, const getdns_bindata *child_binda } getdns_return_t _getdns_list_append_const_bindata( - getdns_list *list, size_t size, const uint8_t *data) + getdns_list *list, size_t size, const void *data) { if (!list) return GETDNS_RETURN_INVALID_PARAMETER; return _getdns_list_set_const_bindata(list, list->numinuse, size, data); diff --git a/src/request-internal.c b/src/request-internal.c index 55582afb..c46aa3ea 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -110,123 +110,134 @@ 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, - const char *name, uint16_t request_type, uint16_t request_class, - int dnssec_extension_set, int with_opt, + uint16_t request_type, int dnssec_extension_set, int with_opt, int edns_maximum_udp_payload_size, uint8_t edns_extended_rcode, uint8_t edns_version, int edns_do_bit, uint16_t opt_options_size, size_t noptions, getdns_list *options, size_t wire_data_sz, size_t max_query_sz) { uint8_t *buf; - size_t dname_len; getdns_dict *option; uint32_t option_code; getdns_bindata *option_data; size_t i; int r = 0; + /* variables that stay the same on reinit, don't touch + */ net_req->request_type = request_type; - net_req->request_class = request_class; - 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) { - /* first two bytes will contain query length (for tcp) */ - buf = net_req->query = net_req->wire_data + 2; - - gldns_write_uint16(buf + 2, 0); /* reset all flags */ - GLDNS_RD_SET(buf); - if (dnssec_extension_set) /* We will do validation ourselves */ - GLDNS_CD_SET(buf); - GLDNS_OPCODE_SET(buf, GLDNS_PACKET_QUERY); - gldns_write_uint16(buf + GLDNS_QDCOUNT_OFF, 1); /* 1 query */ - gldns_write_uint16(buf + GLDNS_ANCOUNT_OFF, 0); /* 0 answers */ - 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; - dname_len = max_query_sz - GLDNS_HEADER_SIZE; - if ((r = gldns_str2wire_dname_buf(name, buf, &dname_len))) { - net_req->opt = NULL; - return r; - } - - buf += dname_len; - - gldns_write_uint16(buf, request_type); - gldns_write_uint16(buf + 2, request_class); - buf += 4; - - if (with_opt) { - net_req->opt = buf; - buf[0] = 0; /* dname for . */ - gldns_write_uint16(buf + 1, GLDNS_RR_TYPE_OPT); - gldns_write_uint16(net_req->opt + 3, - net_req->max_udp_payload_size); - buf[5] = edns_extended_rcode; - buf[6] = edns_version; - buf[7] = edns_do_bit ? 0x80 : 0; - buf[8] = 0; - gldns_write_uint16(buf + 9, opt_options_size); - buf += 11; - for (i = 0; i < noptions; i++) { - if (getdns_list_get_dict(options, i, &option)) - continue; - if (getdns_dict_get_int( - option, "option_code", &option_code)) - continue; - if (getdns_dict_get_bindata( - option, "option_data", &option_data)) - continue; - - gldns_write_uint16(buf, (uint16_t) option_code); - gldns_write_uint16(buf + 2, - (uint16_t) option_data->size); - (void) memcpy(buf + 4, option_data->data, - option_data->size); - - buf += option_data->size + 4; - } - } else - net_req->opt = NULL; - net_req->response = buf; - gldns_write_uint16(net_req->wire_data, net_req->response - net_req->query); - } else { + 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) */ + 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 */ + GLDNS_CD_SET(buf); + GLDNS_OPCODE_SET(buf, GLDNS_PACKET_QUERY); + gldns_write_uint16(buf + GLDNS_QDCOUNT_OFF, 1); /* 1 query */ + gldns_write_uint16(buf + GLDNS_ANCOUNT_OFF, 0); /* 0 answers */ + gldns_write_uint16(buf + GLDNS_NSCOUNT_OFF, 0); /* 0 authorities */ + gldns_write_uint16(buf + GLDNS_ARCOUNT_OFF, with_opt ? 1 : 0); + + buf = netreq_reset(net_req); + if (with_opt) { + net_req->opt = buf; + buf[0] = 0; /* dname for . */ + gldns_write_uint16(buf + 1, GLDNS_RR_TYPE_OPT); + gldns_write_uint16(net_req->opt + 3, + net_req->max_udp_payload_size); + buf[5] = edns_extended_rcode; + buf[6] = edns_version; + buf[7] = edns_do_bit ? 0x80 : 0; + buf[8] = 0; + gldns_write_uint16(buf + 9, opt_options_size); + buf += 11; + for (i = 0; i < noptions; i++) { + if (getdns_list_get_dict(options, i, &option)) + continue; + if (getdns_dict_get_int( + option, "option_code", &option_code)) + continue; + if (getdns_dict_get_bindata( + option, "option_data", &option_data)) + continue; + + gldns_write_uint16(buf, (uint16_t) option_code); + gldns_write_uint16(buf + 2, + (uint16_t) option_data->size); + (void) memcpy(buf + 4, option_data->data, + option_data->size); + + buf += option_data->size + 4; + } + } else + net_req->opt = NULL; + + net_req->response = buf; + gldns_write_uint16(net_req->wire_data, net_req->response - net_req->query); + return r; } @@ -248,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 @@ -583,6 +627,8 @@ _getdns_dns_req_free(getdns_dns_req * req) GETDNS_FREE(req->my_mf, req); } +static const uint8_t no_suffixes[] = { 1, 0 }; + /* create a new dns req to be submitted */ getdns_dns_req * _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, @@ -647,7 +693,7 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, * And align on the 8 byte boundry (hence the (x + 7) / 8 * 8) */ size_t max_query_sz, max_response_sz, netreq_sz, dnsreq_base_sz; - uint8_t *region; + uint8_t *region, *suffixes; if (extensions == dnssec_ok_checking_disabled || extensions == dnssec_ok_checking_disabled_roadblock_avoidance || @@ -719,7 +765,7 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, ; } max_query_sz = ( GLDNS_HEADER_SIZE - + strlen(name) + 1 + 4 /* dname always smaller then strlen(name) + 1 */ + + 256 + 4 /* dname maximum 255 bytes (256 with mdns)*/ + 12 + opt_options_size /* space needed for OPT (if needed) */ + MAXIMUM_UPSTREAM_OPTION_SPACE + MAXIMUM_TSIG_SPACE @@ -734,6 +780,7 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, + max_query_sz + max_response_sz + 7 ) / 8 * 8; dnsreq_base_sz = (( sizeof(getdns_dns_req) + (a_aaaa_query ? 3 : 2) * sizeof(getdns_network_req*) + + context->suffixes_len ) + 7) / 8 * 8; if (! (region = GETDNS_XMALLOC(context->mf, uint8_t, @@ -750,12 +797,52 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, result->netreqs[1] = NULL; result->my_mf = context->mf; + + suffixes = region + dnsreq_base_sz - context->suffixes_len; + assert(context->suffixes); + assert(context->suffixes_len); + memcpy(suffixes, context->suffixes, context->suffixes_len); + result->append_name = context->append_name; + if (!strlen(name) || name[strlen(name)-1] == '.' || + result->append_name == GETDNS_APPEND_NAME_NEVER) { + /* Absolute query string, no appending */ + result->suffix_len = no_suffixes[0]; + result->suffix = no_suffixes + 1; + result->suffix_appended = 1; + } else { + result->suffix_len = suffixes[0]; + result->suffix = suffixes + 1; + result->suffix_appended = 0; + } result->name_len = sizeof(result->name); if (gldns_str2wire_dname_buf(name, result->name, &result->name_len)) { GETDNS_FREE(result->my_mf, result); return NULL; } + if (result->append_name == GETDNS_APPEND_NAME_ALWAYS) { + for ( + ; result->suffix_len > 1 && *result->suffix + ; result->suffix += result->suffix_len + , result->suffix_len = *result->suffix++) { + + if (result->suffix_len + result->name_len - 1 < + sizeof(result->name)) { + memcpy(result->name + result->name_len - 1, + result->suffix, result->suffix_len); + result->name_len += result->suffix_len - 1; + result->suffix_appended = 1; + break; + } + } + } else if (result->append_name == + GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE && + result->name[result->name[0]+1] != 0) { + /* We have multiple labels, no appending */ + result->suffix_len = no_suffixes[0]; + result->suffix = no_suffixes + 1; + result->suffix_appended = 1; + } result->context = context; result->loop = loop; result->canceled = 0; @@ -783,23 +870,23 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, /* check the specify_class extension */ (void) getdns_dict_get_int(extensions, "specify_class", &klass); + result->request_class = klass; result->upstreams = context->upstreams; if (result->upstreams) result->upstreams->referenced++; network_req_init(result->netreqs[0], result, - name, request_type, klass, - dnssec_extension_set, with_opt, + request_type, dnssec_extension_set, with_opt, edns_maximum_udp_payload_size, edns_extended_rcode, edns_version, edns_do_bit, opt_options_size, noptions, options, netreq_sz - sizeof(getdns_network_req), max_query_sz); if (a_aaaa_query) - network_req_init(result->netreqs[1], result, name, + network_req_init(result->netreqs[1], result, ( request_type == GETDNS_RRTYPE_A - ? GETDNS_RRTYPE_AAAA : GETDNS_RRTYPE_A ), klass, + ? GETDNS_RRTYPE_AAAA : GETDNS_RRTYPE_A ), dnssec_extension_set, with_opt, edns_maximum_udp_payload_size, edns_extended_rcode, edns_version, edns_do_bit, diff --git a/src/test/getdns_query.c b/src/test/getdns_query.c index 6b46504f..ef041ac7 100644 --- a/src/test/getdns_query.c +++ b/src/test/getdns_query.c @@ -490,6 +490,13 @@ print_usage(FILE *out, const char *progname) fprintf(out, "\t-s\tSet stub resolution type (default = recursing)\n"); fprintf(out, "\t-S\tservice lookup ( is ignored)\n"); fprintf(out, "\t-t \tSet timeout in miliseconds\n"); + + fprintf(out, "\t-W\tAppend suffix always (default)\n"); + fprintf(out, "\t-1\tAppend suffix only to single label after failure\n"); + fprintf(out, "\t-M\tAppend suffix only to multi label name after failure\n"); + fprintf(out, "\t-N\tNever append a suffix\n"); + fprintf(out, "\t-Z \tSet suffixes with the given comma separed list\n"); + fprintf(out, "\t-T\tSet transport to TCP only\n"); fprintf(out, "\t-O\tSet transport to TCP only keep connections open\n"); fprintf(out, "\t-L\tSet transport to TLS only keep connections open\n"); @@ -680,12 +687,15 @@ done: getdns_return_t parse_args(int argc, char **argv) { getdns_return_t r = GETDNS_RETURN_GOOD; - size_t i; + size_t i, j; char *arg, *c, *endptr; int t, print_api_info = 0, print_trust_anchors = 0; getdns_list *upstream_list = NULL; getdns_list *tas = NULL, *hints = NULL; getdns_dict *pubkey_pin = NULL; + getdns_list *suffixes; + char *suffix; + getdns_bindata bindata; size_t upstream_count = 0; FILE *fh; @@ -958,6 +968,43 @@ getdns_return_t parse_args(int argc, char **argv) getdns_context_set_idle_timeout( context, timeout); goto next; + case 'W': + (void) getdns_context_set_append_name(context, + GETDNS_APPEND_NAME_ALWAYS); + break; + case '1': + (void) getdns_context_set_append_name(context, + GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE); + break; + case 'M': + (void) getdns_context_set_append_name(context, + GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE); + break; + case 'N': + (void) getdns_context_set_append_name(context, + GETDNS_APPEND_NAME_NEVER); + break; + case 'Z': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "suffixes expected" + "after -Z\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + if (!(suffixes = getdns_list_create())) + return GETDNS_RETURN_MEMORY_ERROR; + suffix = strtok(argv[i], ","); + j = 0; + while (suffix) { + bindata.size = strlen(suffix); + bindata.data = (void *)suffix; + (void) getdns_list_set_bindata( + suffixes, j++, &bindata); + suffix = strtok(NULL, ","); + } + (void) getdns_context_set_suffix(context, + suffixes); + getdns_list_destroy(suffixes); + goto next; case 'T': getdns_context_set_dns_transport(context, GETDNS_TRANSPORT_TCP_ONLY); @@ -999,7 +1046,7 @@ getdns_return_t parse_args(int argc, char **argv) break; case 'B': batch_mode = 1; - break; + break; default: diff --git a/src/types-internal.h b/src/types-internal.h index 23731bae..cac33621 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -203,9 +203,6 @@ typedef struct getdns_network_req /* request type */ uint16_t request_type; - /* request class */ - uint16_t request_class; - /* dnssec status */ int dnssec_status; @@ -282,6 +279,13 @@ typedef struct getdns_dns_req { uint8_t name[256]; size_t name_len; + getdns_append_name_t append_name; + const uint8_t *suffix; + size_t suffix_len; + int suffix_appended; + + uint16_t request_class; + /* canceled flag */ int canceled; @@ -395,5 +399,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 */ diff --git a/src/util-internal.c b/src/util-internal.c index 7ea0edb9..6b863e68 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -1130,7 +1130,7 @@ getdns_apply_network_result(getdns_network_req* netreq, , netreq->request_type); gldns_write_uint16( netreq->response + GLDNS_HEADER_SIZE + netreq->owner->name_len + 2 - , netreq->request_class); + , netreq->owner->request_class); netreq->response_len = GLDNS_HEADER_SIZE + netreq->owner->name_len + 4; diff --git a/src/util-internal.h b/src/util-internal.h index 6e6ff7cf..7a826b95 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -83,10 +83,10 @@ getdns_return_t _getdns_list_append_int(getdns_list *list, uint32_t child_uint32); getdns_return_t _getdns_list_append_const_bindata(getdns_list *list, - size_t size, const uint8_t *data); + size_t size, const void *data); getdns_return_t _getdns_dict_set_const_bindata(getdns_dict *dict, - const char *name, size_t size, const uint8_t *data); + const char *name, size_t size, const void *data); /** * private function (API users should not be calling this), this uses library