diff --git a/src/context.c b/src/context.c index 0038b02e..acca4c0f 100644 --- a/src/context.c +++ b/src/context.c @@ -1389,9 +1389,9 @@ getdns_context_set_memory_functions(struct getdns_context *context, static void cancel_dns_req(getdns_dns_req *req) { - getdns_network_req *netreq; + getdns_network_req *netreq, **netreq_p; - for (netreq = req->first_req; netreq; netreq = netreq->next) + for (netreq_p = req->netreqs; (netreq = *netreq_p); netreq_p++) if (netreq->unbound_id != -1) { ub_cancel(req->context->unbound_ctx, netreq->unbound_id); @@ -1921,12 +1921,12 @@ getdns_context_local_namespace_resolve( ldns_rr_list *result_list = NULL; host_name_addrs *hnas; ldns_rdf *query_name; - int ipv4 = dnsreq->first_req->request_type == GETDNS_RRTYPE_A || - (dnsreq->first_req->next && - dnsreq->first_req->next->request_type == GETDNS_RRTYPE_A); - int ipv6 = dnsreq->first_req->request_type == GETDNS_RRTYPE_AAAA || - (dnsreq->first_req->next && - dnsreq->first_req->next->request_type == GETDNS_RRTYPE_AAAA); + int ipv4 = dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A || + (dnsreq->netreqs[1] && + dnsreq->netreqs[1]->request_type == GETDNS_RRTYPE_A); + int ipv6 = dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_AAAA || + (dnsreq->netreqs[1] && + dnsreq->netreqs[1]->request_type == GETDNS_RRTYPE_AAAA); if (!ipv4 && !ipv6) return GETDNS_RETURN_GENERIC_ERROR; diff --git a/src/dnssec.c b/src/dnssec.c index 8053b964..22664e4e 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -272,7 +272,7 @@ static void destroy_chain(struct validation_chain *chain) static void getdns_get_validation_chain(getdns_dns_req *dns_req, uint64_t *timeout) { - getdns_network_req *netreq = dns_req->first_req; + getdns_network_req **netreq_p, *netreq; struct validation_chain *chain = create_chain(dns_req, timeout); if (! chain) { @@ -280,7 +280,7 @@ getdns_get_validation_chain(getdns_dns_req *dns_req, uint64_t *timeout) dns_req, create_getdns_response(dns_req)); return; } - while (netreq) { + for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) { size_t i; ldns_rr_list *answer = ldns_pkt_answer(netreq->result); ldns_rr_list *authority = ldns_pkt_authority(netreq->result); @@ -296,7 +296,6 @@ getdns_get_validation_chain(getdns_dns_req *dns_req, uint64_t *timeout) launch_chain_link_lookup(chain, ldns_rdf2str(ldns_rr_rdf(rr, 7))); } - netreq = netreq->next; } callback_on_complete_chain(chain); } diff --git a/src/general.c b/src/general.c index 582138fa..8b04b08c 100644 --- a/src/general.c +++ b/src/general.c @@ -81,10 +81,10 @@ handle_network_request_error(getdns_network_req * netreq, int err) void priv_getdns_check_dns_req_complete(getdns_dns_req *dns_req) { - getdns_network_req *netreq; + getdns_network_req **netreq_p, *netreq; int results_found = 0; - for (netreq = dns_req->first_req; netreq; netreq = netreq->next) + for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) if (netreq->state != NET_REQ_FINISHED && netreq->state != NET_REQ_CANCELED) return; @@ -168,7 +168,7 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, getdns_callback_t callbackfn, int usenamespaces) { getdns_return_t r = GETDNS_RETURN_GOOD; - getdns_network_req *netreq; + getdns_network_req *netreq, **netreq_p; getdns_dns_req *req; getdns_dict *localnames_response; size_t i; @@ -201,7 +201,9 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, if (!usenamespaces) /* issue all network requests */ - for (netreq = req->first_req; !r && netreq; netreq = netreq->next) + for ( netreq_p = req->netreqs + ; !r && (netreq = *netreq_p) + ; netreq_p++) r = submit_network_request(netreq); else for (i = 0; i < context->namespace_count; i++) { @@ -221,11 +223,10 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, if this means we go onto the next namespace instead of returning */ r = GETDNS_RETURN_GOOD; - netreq = req->first_req; - while (!r && netreq) { + for ( netreq_p = req->netreqs + ; !r && (netreq = *netreq_p) + ; netreq_p++) r = submit_network_request(netreq); - netreq = netreq->next; - } break; } else r = GETDNS_RETURN_BAD_CONTEXT; diff --git a/src/request-internal.c b/src/request-internal.c index 60fbc9a4..8866edf1 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -38,31 +38,24 @@ #include "util-internal.h" #include "gldns/rrdef.h" -void -network_req_free(getdns_network_req * net_req) +static void +network_req_cleanup(getdns_network_req *net_req) { - if (!net_req) { - return; - } - if (net_req->result) { + assert(net_req); + + if (net_req->result) ldns_pkt_free(net_req->result); - } - GETDNS_FREE(net_req->owner->my_mf, net_req); + + if (net_req->response && net_req->response != net_req->wire_data) + GETDNS_FREE(net_req->owner->my_mf, net_req->response); } -getdns_network_req * -network_req_new(getdns_dns_req * owner, - uint16_t request_type, - uint16_t request_class, struct getdns_dict *extensions) +static void +network_req_init(getdns_network_req *net_req, + getdns_dns_req *owner, uint16_t request_type, + uint16_t request_class, size_t wire_data_sz) { - - getdns_network_req *net_req = GETDNS_MALLOC( owner->my_mf - , getdns_network_req); - if (!net_req) { - return NULL; - } net_req->result = NULL; - net_req->next = NULL; net_req->request_type = request_type; net_req->request_class = request_class; @@ -70,8 +63,6 @@ network_req_new(getdns_dns_req * owner, net_req->state = NET_REQ_NOT_SENT; net_req->owner = owner; - /* TODO: records and other extensions */ - net_req->upstream = NULL; net_req->fd = -1; memset(&net_req->event, 0, sizeof(net_req->event)); @@ -79,16 +70,18 @@ network_req_new(getdns_dns_req * owner, net_req->query_id = 0; net_req->max_udp_payload_size = 0; net_req->write_queue_tail = NULL; - return net_req; + + net_req->wire_data_sz = wire_data_sz; + net_req->response = NULL; } void dns_req_free(getdns_dns_req * req) { + getdns_network_req **net_req; if (!req) { return; } - getdns_network_req *net_req = NULL; /* free extensions */ getdns_dict_destroy(req->extensions); @@ -96,14 +89,11 @@ dns_req_free(getdns_dns_req * req) if (req->upstreams && --req->upstreams->referenced == 0) GETDNS_FREE(req->upstreams->mf, req->upstreams); - /* free network requests */ - net_req = req->first_req; - while (net_req) { - getdns_network_req *next = net_req->next; - network_req_free(net_req); - net_req = next; - } + /* cleanup network requests */ + for (net_req = req->netreqs; *net_req; net_req++) + network_req_cleanup(*net_req); + /* clear timeout event */ if (req->timeout.timeout_cb) { req->loop->vmt->clear(req->loop, &req->timeout); req->timeout.timeout_cb = NULL; @@ -116,25 +106,47 @@ dns_req_free(getdns_dns_req * req) /* create a new dns req to be submitted */ getdns_dns_req * -dns_req_new(struct getdns_context *context, getdns_eventloop *loop, - const char *name, uint16_t request_type, struct getdns_dict *extensions) +dns_req_new(getdns_context *context, getdns_eventloop *loop, + const char *name, uint16_t request_type, getdns_dict *extensions) { getdns_dns_req *result = NULL; - getdns_network_req *req = NULL; uint32_t klass = GLDNS_RR_CLASS_IN; - - result = GETDNS_MALLOC(context->mf, getdns_dns_req); - if (result == NULL) { + size_t edns_maximum_udp_payload_size = + getdns_get_maximum_udp_payload_size(context, extensions, NULL); + int a_aaaa_query = + is_extension_set(extensions, "return_both_v4_and_v6") && + ( request_type == GETDNS_RRTYPE_A || + request_type == GETDNS_RRTYPE_AAAA ); + /* Reserve for the buffer at least one more byte + * (to test for udp overflow) (hence the + 1), + * And align on the 8 byte boundry (hence the (x + 7) / 8 * 8) + */ + size_t netreq_sz = ( sizeof(getdns_network_req) + + edns_maximum_udp_payload_size + 1 + 7) / 8 * 8; + size_t dnsreq_base_sz = ( sizeof(getdns_dns_req) + + (a_aaaa_query ? 3 : 2) + * sizeof(getdns_network_req *)); + uint8_t *region; + + if (! (region = GETDNS_XMALLOC(context->mf, uint8_t, + dnsreq_base_sz + (a_aaaa_query ? 2 : 1) * netreq_sz))) return NULL; - } + + result = (getdns_dns_req *)region; + result->netreqs[0] = (getdns_network_req *)(region + dnsreq_base_sz); + if (a_aaaa_query) { + result->netreqs[1] = (getdns_network_req *) + (region + dnsreq_base_sz + netreq_sz); + result->netreqs[2] = NULL; + } else + result->netreqs[1] = NULL; + result->my_mf = context->mf; result->name = getdns_strdup(&(result->my_mf), name); result->context = context; result->loop = loop; result->canceled = 0; - result->current_req = NULL; - result->first_req = NULL; result->trans_id = (uint64_t)(((intptr_t) result) ^ ldns_get_random()); getdns_dict_copy(extensions, &result->extensions); @@ -152,32 +164,14 @@ dns_req_new(struct getdns_context *context, getdns_eventloop *loop, if (result->upstreams) result->upstreams->referenced++; - /* create the requests */ - req = network_req_new(result, request_type, klass, extensions); - if (!req) { - dns_req_free(result); - return NULL; - } + network_req_init(result->netreqs[0], result, request_type, + klass, netreq_sz - sizeof(getdns_network_req)); - result->current_req = req; - result->first_req = req; - - /* tack on A or AAAA if needed */ - if (is_extension_set(extensions, "return_both_v4_and_v6") && - (request_type == GETDNS_RRTYPE_A || - request_type == GETDNS_RRTYPE_AAAA)) { - - uint16_t next_req_type = (request_type == GETDNS_RRTYPE_A) ? - GETDNS_RRTYPE_AAAA : GETDNS_RRTYPE_A; - getdns_network_req *next_req = network_req_new(result, - next_req_type, LDNS_RR_CLASS_IN, extensions); - - if (!next_req) { - dns_req_free(result); - return NULL; - } - req->next = next_req; - } + if (a_aaaa_query) + network_req_init(result->netreqs[1], result, + ( request_type == GETDNS_RRTYPE_A + ? GETDNS_RRTYPE_AAAA : GETDNS_RRTYPE_A ), + klass, netreq_sz - sizeof(getdns_network_req)); return result; } diff --git a/src/stub.c b/src/stub.c index cfa19e70..7512a01e 100644 --- a/src/stub.c +++ b/src/stub.c @@ -170,7 +170,6 @@ getdns_make_query_pkt_buf(const getdns_network_req *netreq, uint8_t *buf, if (len < 11) return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; - *omax_udp_payload_size = edns_maximum_udp_payload_size; buf[0] = 0; /* dname for . */ gldns_write_uint16(buf + 1, GLDNS_RR_TYPE_OPT); gldns_write_uint16(buf + 3, diff --git a/src/types-internal.h b/src/types-internal.h index 707ecd4e..8ff2aa32 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -191,9 +191,6 @@ typedef struct getdns_network_req int secure; int bogus; - /* next request to issue after this one */ - struct getdns_network_req *next; - /* For stub resolving */ struct getdns_upstream *upstream; int fd; @@ -206,6 +203,14 @@ typedef struct getdns_network_req /* Network requests scheduled to write after me */ struct getdns_network_req *write_queue_tail; + /* When more space is needed for the wire_data response than is + * available in wire_data[], it will be allocated seperately. + * response will then not point to wire_data anymore. + */ + uint8_t *response; + size_t wire_data_sz; + uint8_t wire_data[]; + } getdns_network_req; /** @@ -222,12 +227,6 @@ typedef struct getdns_dns_req { /* canceled flag */ int canceled; - /* current network request */ - struct getdns_network_req *current_req; - - /* first request in list */ - struct getdns_network_req *first_req; - /* context that owns the request */ struct getdns_context *context; @@ -256,6 +255,16 @@ typedef struct getdns_dns_req { /* Stuff for stub resolving */ struct getdns_upstreams *upstreams; + /* network requests for this dns request. + * The array is terminated with NULL. + * + * Memory for these netreqs has been allocated by the same malloc + * operation that reserved space for this getdns_dns_req. + * They will thus be freed as part of the desctruction of this struct, + * and do not need to be freed seperately. + */ + getdns_network_req *netreqs[]; + } getdns_dns_req; #define GETDNS_XMALLOC(obj, type, count) \ @@ -294,13 +303,6 @@ typedef struct getdns_dns_req { /* utility methods */ -/* network request utilities */ -void network_req_free(getdns_network_req * net_req); - -getdns_network_req *network_req_new(getdns_dns_req * owner, - uint16_t request_type, - uint16_t request_class, struct getdns_dict *extensions); - /* dns request utils */ getdns_dns_req *dns_req_new(getdns_context *context, getdns_eventloop *loop, const char *name, uint16_t request_type, getdns_dict *extensions); diff --git a/src/util-internal.c b/src/util-internal.c index ed36ef02..6e94691f 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -544,7 +544,7 @@ create_getdns_response(struct getdns_dns_req * completed_request) struct getdns_list *just_addrs = NULL; struct getdns_list *replies_tree = getdns_list_create_with_context( completed_request->context); - getdns_network_req *netreq; + getdns_network_req *netreq, **netreq_p; char *canonical_name = NULL; getdns_return_t r = 0; int nreplies = 0, nanswers = 0, nsecure = 0, ninsecure = 0, nbogus = 0; @@ -562,13 +562,11 @@ create_getdns_response(struct getdns_dns_req * completed_request) completed_request->extensions, "dnssec_return_status") || completed_request->return_dnssec_status == GETDNS_EXTENSION_TRUE; - if (completed_request->first_req && - (completed_request->first_req->request_class == GETDNS_RRTYPE_A || - completed_request->first_req->request_class == - GETDNS_RRTYPE_AAAA)) { + if (completed_request->netreqs[0]->request_type == GETDNS_RRTYPE_A || + completed_request->netreqs[0]->request_type == GETDNS_RRTYPE_AAAA) just_addrs = getdns_list_create_with_context( completed_request->context); - } + do { canonical_name = get_canonical_name(completed_request->name); r = getdns_dict_util_set_string(result, GETDNS_STR_KEY_CANONICAL_NM, @@ -584,9 +582,9 @@ create_getdns_response(struct getdns_dns_req * completed_request) break; } - for ( netreq = completed_request->first_req - ; netreq && r == GETDNS_RETURN_GOOD - ; netreq = netreq->next ) { + for ( netreq_p = completed_request->netreqs + ; ! r && (netreq = *netreq_p) + ; netreq_p++) { if (! netreq->result) continue; @@ -959,7 +957,7 @@ validate_dname(const char* dname) { } /* validate_dname */ int -is_extension_set(struct getdns_dict *extensions, const char *extension) +is_extension_set(getdns_dict *extensions, const char *extension) { getdns_return_t r; uint32_t value; @@ -971,4 +969,76 @@ is_extension_set(struct getdns_dict *extensions, const char *extension) return r == GETDNS_RETURN_GOOD && value == GETDNS_EXTENSION_TRUE; } +size_t +getdns_get_maximum_udp_payload_size(getdns_context *context, + getdns_dict *extensions, getdns_upstream *upstream) +{ + int dnssec_return_status + = is_extension_set(extensions, "dnssec_return_status"); + int dnssec_return_only_secure + = is_extension_set(extensions, "dnssec_return_only_secure"); + int dnssec_return_validation_chain + = is_extension_set(extensions, "dnssec_return_validation_chain"); + int dnssec_extension_set = dnssec_return_status + || dnssec_return_only_secure || dnssec_return_validation_chain; + + uint32_t edns_do_bit; + int edns_maximum_udp_payload_size; + uint32_t get_edns_maximum_udp_payload_size; + uint32_t edns_extended_rcode; + uint32_t edns_version; + + getdns_dict *add_opt_parameters; + int have_add_opt_parameters; + + getdns_list *options; + size_t noptions = 0; + + int with_opt; + + have_add_opt_parameters = getdns_dict_get_dict(extensions, + "add_opt_parameters", &add_opt_parameters) == GETDNS_RETURN_GOOD; + + if (dnssec_extension_set) { + edns_maximum_udp_payload_size = + ( upstream && upstream->addr.ss_family == AF_INET6 ) + ? 1232 : 1432; + edns_extended_rcode = 0; + edns_version = 0; + edns_do_bit = 1; + } else { + edns_maximum_udp_payload_size = + context->edns_maximum_udp_payload_size; + edns_extended_rcode = context->edns_extended_rcode; + edns_version = context->edns_version; + edns_do_bit = context->edns_do_bit; + + if (have_add_opt_parameters) { + if (!getdns_dict_get_int(add_opt_parameters, + "maximum_udp_payload_size", + &get_edns_maximum_udp_payload_size)) + edns_maximum_udp_payload_size = + get_edns_maximum_udp_payload_size; + (void) getdns_dict_get_int(add_opt_parameters, + "extended_rcode", &edns_extended_rcode); + (void) getdns_dict_get_int(add_opt_parameters, + "version", &edns_version); + (void) getdns_dict_get_int(add_opt_parameters, + "do_bit", &edns_do_bit); + } + } + if (have_add_opt_parameters && getdns_dict_get_list( + add_opt_parameters, "options", &options) == GETDNS_RETURN_GOOD) + (void) getdns_list_get_length(options, &noptions); + + with_opt = edns_do_bit != 0 || edns_maximum_udp_payload_size != -1 || + edns_extended_rcode != 0 || edns_version != 0 || noptions; + + return ! with_opt ? 512 + : edns_maximum_udp_payload_size == -1 ? + (upstream && upstream->addr.ss_family==AF_INET6 ) ? 1232 : 1432 + : edns_maximum_udp_payload_size > 512 ? + edns_maximum_udp_payload_size : 512; +} + /* util-internal.c */ diff --git a/src/util-internal.h b/src/util-internal.h index b3b762aa..344c2ced 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -147,7 +147,10 @@ create_list_from_rr_list(struct getdns_context *context, ldns_rr_list * rr_list) * @param extension name of extension to check * @return int with value 1 if set to GETDNS_EXTENSION_TRUE and 0 otherwise */ -int is_extension_set(struct getdns_dict *extensions, const char *extension); +int is_extension_set(getdns_dict *extensions, const char *extension); + +size_t getdns_get_maximum_udp_payload_size(getdns_context *context, + getdns_dict *extensions, getdns_upstream *upstream); #define DEBUG_ON(...) do { \ struct timeval tv; \