From e598f64e80e356ecf761e6d65e331d9a00ecc662 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 16 Sep 2014 15:43:20 +0200 Subject: [PATCH] gldns _buf support + add_opt_parameters extension --- src/stub.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++------ src/stub.h | 10 +++ src/sync.c | 27 +++++- 3 files changed, 253 insertions(+), 28 deletions(-) diff --git a/src/stub.c b/src/stub.c index 727e21ca..b4a36cc5 100644 --- a/src/stub.c +++ b/src/stub.c @@ -31,42 +31,232 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "getdns/getdns.h" #include "config.h" -#include "gldns/gbuffer.h" +#include "stub.h" #include "gldns/rrdef.h" #include "gldns/str2wire.h" +#include "gldns/pkthdr.h" +#include "context.h" #include +#include "util-internal.h" -gldns_buffer * -make_query_pkt(const char *name, uint16_t request_type, struct getdns_dict *extensions) +int +getdns_make_query_pkt_buf(getdns_context *context, const char *name, + uint16_t request_type, getdns_dict *extensions, uint8_t* buf, size_t* olen) { - uint16_t flags = 0; /* QUERY, NOERROR */ - uint32_t klass; - size_t pos; - gldns_buffer *pkt = gldns_buffer_new(512); /* max query */ + uint32_t klass = GLDNS_RR_CLASS_IN; + size_t len; - if (! pkt) - return NULL; + 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; - gldns_buffer_clear(pkt); - gldns_buffer_write_u16(pkt, ldns_get_random()); - gldns_buffer_write_u16(pkt, flags); - gldns_buffer_write_u16(pkt, 1); /* query count */ - gldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */ - pos = gldns_buffer_remaining(pkt); - if (gldns_str2wire_dname_buf(name, gldns_buffer_current(pkt), &pos)) { - gldns_buffer_free(pkt); - return NULL; + uint32_t edns_do_bit; + uint32_t 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; + size_t i; + + getdns_dict *option; + uint32_t option_code; + getdns_bindata *option_data; + size_t opt_options_size = 0; + + int with_opt; + int r; + size_t dname_len; + + have_add_opt_parameters = getdns_dict_get_dict(extensions, + "add_opt_parameters", &add_opt_parameters); + + if (dnssec_extension_set) { + edns_maximum_udp_payload_size = 1232; + edns_extended_rcode = 0; + edns_version = 0; + edns_do_bit = 0; + } 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) { + (void) getdns_dict_get_int(add_opt_parameters, + "maximum_udp_payload_size", + &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); + } } - gldns_buffer_skip(pkt, gldns_buffer_remaining(pkt) - pos); - gldns_buffer_write_u16(pkt, request_type); - if (getdns_dict_get_int(extensions, "specify_class", &klass) - != GETDNS_RETURN_GOOD) - klass = GLDNS_RR_CLASS_IN; - gldns_buffer_write_u16(pkt, (uint16_t) klass); - return pkt; + 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 || edns_maximum_udp_payload_size > 512 + || edns_extended_rcode != 0 || edns_version != 0 + || opt_options_size > 0; + + assert(buf); + assert(olen); + + len = *olen; + *olen = 0; + + (void) getdns_dict_get_int(extensions, "specify_class", &klass); + + if (len < GLDNS_HEADER_SIZE) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + + gldns_write_uint16(buf + 2, 0); /* reset all flags */ + GLDNS_RD_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); + + len -= GLDNS_HEADER_SIZE; + *olen += GLDNS_HEADER_SIZE; + buf += GLDNS_HEADER_SIZE; + + dname_len = len; + if ((r = gldns_str2wire_dname_buf(name, buf, &dname_len))) return r; + len -= dname_len; + *olen += dname_len; + buf += dname_len; + + if (len < 4) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + gldns_write_uint16(buf, request_type); + gldns_write_uint16(buf + 2, klass); + len -= 4; + *olen += 4; + buf += 4; + + if (with_opt) { + if (len < 11) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + + buf[0] = 0; /* dname for . */ + gldns_write_uint16(buf + 1, GLDNS_RR_TYPE_OPT); + gldns_write_uint16(buf + 3, (uint16_t) edns_maximum_udp_payload_size); + buf[5] = (uint8_t) edns_extended_rcode; + buf[6] = (uint8_t) edns_version; + buf[7] = edns_do_bit ? 0x80 : 0; + buf[8] = 0; + gldns_write_uint16(buf + 9, (uint16_t) opt_options_size); + len -= 11; + *olen += 11; + 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; + + if (len < option_data->size + 4) { + gldns_write_uint16(buf - opt_options_size - 2, + (uint16_t) opt_options_size); + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + } + 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); + + opt_options_size += option_data->size + 4; + len -= option_data->size + 4; + *olen += option_data->size + 4; + buf += option_data->size + 4; + } + gldns_write_uint16(buf - opt_options_size - 2, + (uint16_t) opt_options_size); + } + return 0; +} + + +/* Return a rough estimate for mallocs */ +size_t +getdns_get_query_pkt_size(getdns_context *context, + const char *name, uint16_t request_type, getdns_dict *extensions) +{ + getdns_dict *add_opt_parameters; + + getdns_list *options; + size_t noptions = 0; + size_t i; + + getdns_dict *option; + uint32_t option_code; + getdns_bindata *option_data; + size_t opt_options_size = 0; + + do { + if (getdns_dict_get_dict(extensions, + "add_opt_parameters", &add_opt_parameters)) break; + if (getdns_dict_get_list( + add_opt_parameters, "options", &options)) break; + if (getdns_list_get_length(options, &noptions)) break; + + 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; + + opt_options_size += option_data->size + + 2 /* option-code */ + + 2 /* option-length */ + ; + } + } while (0); + + return GLDNS_HEADER_SIZE + + strlen(name) + 1 + 4 /* dname always smaller then strlen(name) + 1 */ + + 12 + opt_options_size /* space needed for OPT (if needed) */ + /* TODO: TSIG */ + ; +} + + +uint8_t * +getdns_make_query_pkt(getdns_context *context, const char *name, + uint16_t request_type, getdns_dict *extensions, size_t *pkt_len) +{ + size_t query_pkt_sz = getdns_get_query_pkt_size( + context, name, request_type, extensions); + uint8_t *query_pkt = GETDNS_XMALLOC(context->mf, uint8_t, query_pkt_sz); + + if (query_pkt) { + if (getdns_make_query_pkt_buf(context, name, request_type, + extensions, query_pkt, &query_pkt_sz)) { + GETDNS_FREE(context->mf, query_pkt); + return NULL; + } + } + *pkt_len = query_pkt_sz; + return query_pkt; } /* stub.c */ - diff --git a/src/stub.h b/src/stub.h index 2dfeb8d1..16213896 100644 --- a/src/stub.h +++ b/src/stub.h @@ -34,6 +34,16 @@ #ifndef STUB_H_ #define STUB_H_ +#include "getdns/getdns.h" +#include "gldns/gbuffer.h" + +int getdns_make_query_pkt_buf(getdns_context *context, const char *name, + uint16_t request_type, getdns_dict *extensions, uint8_t* buf, size_t* len); +size_t getdns_get_query_pkt_size(getdns_context *context, + const char *name, uint16_t request_type, getdns_dict *extensions); +uint8_t *getdns_make_query_pkt(getdns_context *context, const char *name, + uint16_t request_type, getdns_dict *extensions, size_t *pkt_len); + #endif /* stub.h */ diff --git a/src/sync.c b/src/sync.c index d607d835..63fdb17e 100644 --- a/src/sync.c +++ b/src/sync.c @@ -44,6 +44,9 @@ #include "dnssec.h" #include "ub_timed_resolve.h" +#include "stub.h" +#include "gldns/wire2str.h" + /* stuff to make it compile pedantically */ #define UNUSED_PARAM(x) ((void)(x)) #define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code; @@ -76,6 +79,26 @@ static getdns_return_t submit_request_sync( return gr; } +static getdns_return_t submit_request_sync_stub( + getdns_dns_req* req, uint64_t *timeout) +{ + getdns_network_req *netreq; + uint8_t *pkt; + size_t pkt_len; + char *str; + + for (netreq = req->first_req; netreq; netreq = netreq->next) { + pkt = getdns_make_query_pkt(req->context, req->name, + netreq->request_type, req->extensions, &pkt_len); + str = gldns_wire2str_pkt(pkt, pkt_len); + fprintf(stderr, "%s\n", str); + free(str); + GETDNS_FREE(req->context->mf, pkt); + } + return submit_request_sync(req, timeout); +} + + getdns_return_t getdns_general_sync(struct getdns_context *context, const char *name, @@ -111,7 +134,9 @@ getdns_general_sync(struct getdns_context *context, if (!req) return GETDNS_RETURN_MEMORY_ERROR; - response_status = submit_request_sync(req, &timeout); + response_status = ( context->resolution_type == GETDNS_RESOLUTION_STUB + ? submit_request_sync_stub : submit_request_sync )(req, &timeout); + if (response_status == GETDNS_RETURN_GOOD) { if (is_extension_set(req->extensions, "dnssec_return_validation_chain"))