diff --git a/configure.ac b/configure.ac index 439bbc4d..c64dc908 100644 --- a/configure.ac +++ b/configure.ac @@ -415,6 +415,9 @@ esac AC_DEFINE_UNQUOTED([EDNS_COOKIE_OPCODE], [10], [The edns cookie option code.]) AC_DEFINE_UNQUOTED([EDNS_COOKIE_ROLLOVER_TIME], [(24 * 60 * 60)], [How often the edns client cookie is refreshed.]) +AC_DEFINE_UNQUOTED([MAXIMUM_UPSTREAM_OPTION_SPACE], [3000], [limit for dynamically-generated DNS options]) +AC_DEFINE_UNQUOTED([EDNS_PADDING_OPCODE], [65461], [The experimental edns padding option code.]) + my_with_libunbound=1 AC_ARG_ENABLE(stub-only, AC_HELP_STRING([--enable-stub-only], [Restricts resolution modes to STUB (which will be the default mode). Removes the libunbound dependency.])) case "$enable_stub_only" in diff --git a/src/Makefile.in b/src/Makefile.in index 381ab6bd..d6d90054 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -117,7 +117,7 @@ install: libgetdns.la $(INSTALL) -m 755 -d $(DESTDIR)$(includedir) $(INSTALL) -m 755 -d $(DESTDIR)$(includedir)/getdns $(INSTALL) -m 644 getdns/getdns.h $(DESTDIR)$(includedir)/getdns/getdns.h - $(INSTALL) -m 644 $(srcdir)/getdns/getdns_extra.h $(DESTDIR)$(includedir)/getdns/getdns_extra.h + $(INSTALL) -m 644 getdns/getdns_extra.h $(DESTDIR)$(includedir)/getdns/getdns_extra.h $(INSTALL) -m 755 -d $(DESTDIR)$(libdir) $(LIBTOOL) --mode=install cp libgetdns.la $(DESTDIR)$(libdir) if test $(have_libevent) = 1 ; then $(INSTALL) -m 644 $(srcdir)/getdns/getdns_ext_libevent.h $(DESTDIR)$(includedir)/getdns/ ; $(LIBTOOL) --mode=install cp $(EXTENSION_LIBEVENT_LIB) $(DESTDIR)$(libdir) ; fi diff --git a/src/context.c b/src/context.c index 179a8a88..7cf844ea 100644 --- a/src/context.c +++ b/src/context.c @@ -881,6 +881,8 @@ getdns_context_create_with_extended_memory_functions( result->edns_extended_rcode = 0; result->edns_version = 0; result->edns_do_bit = 0; + result->edns_client_subnet_private = 0; + result->tls_query_padding_blocksize = 1; /* default is to not try to pad */ result-> tls_ctx = NULL; result->extension = &result->mini_event.loop; @@ -1897,6 +1899,46 @@ getdns_context_set_edns_do_bit(struct getdns_context *context, uint8_t value) return GETDNS_RETURN_GOOD; } /* getdns_context_set_edns_do_bit */ +/* + * getdns_context_set_edns_client_subnet_private + * + */ +getdns_return_t +getdns_context_set_edns_client_subnet_private(struct getdns_context *context, uint8_t value) +{ + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + /* only allow 1 */ + if (value != 0 && value != 1) { + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } + + context->edns_client_subnet_private = value; + + dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE); + + return GETDNS_RETURN_GOOD; +} /* getdns_context_set_edns_client_subnet_private */ + +/* + * getdns_context_set_tls_query_padding_blocksize + * + */ +getdns_return_t +getdns_context_set_tls_query_padding_blocksize(struct getdns_context *context, uint16_t value) +{ + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + /* only allow values between 0 and MAXIMUM_UPSTREAM_OPTION_SPACE - 4 + (4 is for the overhead of the option itself) */ + if (value > MAXIMUM_UPSTREAM_OPTION_SPACE - 4) { + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } + + context->tls_query_padding_blocksize = value; + + dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE); + + return GETDNS_RETURN_GOOD; +} /* getdns_context_set_tls_query_padding_blocksize */ /* * getdns_context_set_extended_memory_functions * @@ -2211,8 +2253,8 @@ _getdns_context_prepare_for_resolution(struct getdns_context *context, if(context->tls_ctx == NULL) return GETDNS_RETURN_BAD_CONTEXT; /* Be strict and only use the cipher suites recommended in RFC7525 - Unless we later fallback to oppotunistic. */ - const char* const PREFERRED_CIPHERS = "EECDH+aRSA+AESGCM:EDH+aRSA+AESGCM"; + Unless we later fallback to opportunistic. */ + const char* const PREFERRED_CIPHERS = "EECDH+aRSA+AESGCM:EECDH+aECDSA+AESGCM:EDH+aRSA+AESGCM"; if (!SSL_CTX_set_cipher_list(context->tls_ctx, PREFERRED_CIPHERS)) return GETDNS_RETURN_BAD_CONTEXT; if (!SSL_CTX_set_default_verify_paths(context->tls_ctx)) @@ -2977,4 +3019,20 @@ getdns_context_get_edns_do_bit(getdns_context *context, uint8_t* value) { return GETDNS_RETURN_GOOD; } +getdns_return_t +getdns_context_get_edns_client_subnet_private(getdns_context *context, uint8_t* value) { + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER); + *value = context->edns_client_subnet_private; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_get_tls_query_padding_blocksize(getdns_context *context, uint16_t* value) { + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER); + *value = context->tls_query_padding_blocksize; + return GETDNS_RETURN_GOOD; +} + /* context.c */ diff --git a/src/context.h b/src/context.h index 8a205e4e..1e489d2d 100644 --- a/src/context.h +++ b/src/context.h @@ -157,6 +157,8 @@ struct getdns_context { uint8_t edns_version; uint8_t edns_do_bit; int edns_maximum_udp_payload_size; /* -1 is unset */ + uint8_t edns_client_subnet_private; + uint16_t tls_query_padding_blocksize; SSL_CTX* tls_ctx; getdns_update_callback update_callback; diff --git a/src/dict.c b/src/dict.c index 16e0b3fb..dd59d346 100644 --- a/src/dict.c +++ b/src/dict.c @@ -981,6 +981,7 @@ getdns_pp_dict(gldns_buffer * buf, size_t indent, if (!json && (strcmp(item->node.key, "type") == 0 || strcmp(item->node.key, "type_covered") == 0 || + strcmp(item->node.key, "query_type") == 0 || strcmp(item->node.key, "qtype") == 0) && (strval = _getdns_rr_type_name(item->i.data.n))) { if (gldns_buffer_printf( @@ -994,6 +995,7 @@ getdns_pp_dict(gldns_buffer * buf, size_t indent, strcmp(item->node.key, "status") == 0 || strcmp(item->node.key, "append_name") == 0 || strcmp(item->node.key, "follow_redirects") == 0 || + strcmp(item->node.key, "transport") == 0 || strcmp(item->node.key, "resolution_type") == 0) && (strval = _getdns_get_const_info(item->i.data.n)->name)) { diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index bd46a4c2..919d6b05 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -194,6 +194,16 @@ getdns_context_get_edns_version(getdns_context *context, uint8_t* value); getdns_return_t getdns_context_get_edns_do_bit(getdns_context *context, uint8_t* value); +getdns_return_t +getdns_context_set_edns_client_subnet_private(getdns_context *context, uint8_t value); +getdns_return_t +getdns_context_get_edns_client_subnet_private(getdns_context *context, uint8_t* value); + +getdns_return_t +getdns_context_set_tls_query_padding_blocksize(getdns_context *context, uint16_t value); +getdns_return_t +getdns_context_get_tls_query_padding_blocksize(getdns_context *context, uint16_t* value); + /** * Pretty print the getdns_dict in a given buffer snprintf style. @@ -366,6 +376,10 @@ typedef enum getdns_tls_authentication_t { #define GETDNS_CONTEXT_CODE_TLS_AUTHENTICATION 618 #define GETDNS_CONTEXT_CODE_TLS_AUTHENTICATION_TEXT "Change related to getdns_context_set_tls_authentication" +#define GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE 619 +#define GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE_TEXT "Change related to getdns_context_set_edns_client_subnet_private" +#define GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE 620 +#define GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE_TEXT "Change related to getdns_context_set_tls_query_padding_blocksize" getdns_return_t getdns_context_set_tls_authentication( diff --git a/src/libgetdns.symbols b/src/libgetdns.symbols index c0241c31..39226295 100644 --- a/src/libgetdns.symbols +++ b/src/libgetdns.symbols @@ -13,6 +13,7 @@ getdns_context_get_dnssec_allowed_skew getdns_context_get_dnssec_trust_anchors getdns_context_get_dns_transport getdns_context_get_dns_transport_list +getdns_context_get_edns_client_subnet_private getdns_context_get_edns_do_bit getdns_context_get_edns_extended_rcode getdns_context_get_edns_maximum_udp_payload_size @@ -25,6 +26,7 @@ getdns_context_get_num_pending_requests getdns_context_get_resolution_type getdns_context_get_suffix getdns_context_get_timeout +getdns_context_get_tls_query_padding_blocksize getdns_context_get_update_callback getdns_context_get_upstream_recursive_servers getdns_context_process_async @@ -36,6 +38,7 @@ getdns_context_set_dnssec_allowed_skew getdns_context_set_dnssec_trust_anchors getdns_context_set_dns_transport getdns_context_set_dns_transport_list +getdns_context_set_edns_client_subnet_private getdns_context_set_edns_do_bit getdns_context_set_edns_extended_rcode getdns_context_set_edns_maximum_udp_payload_size @@ -52,6 +55,7 @@ getdns_context_set_return_dnssec_status getdns_context_set_suffix getdns_context_set_timeout getdns_context_set_tls_authentication +getdns_context_set_tls_query_padding_blocksize getdns_context_set_update_callback getdns_context_set_upstream_recursive_servers getdns_context_set_use_threads diff --git a/src/request-internal.c b/src/request-internal.c index 90ed195a..e18d384d 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -113,6 +113,12 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, ? edns_maximum_udp_payload_size : 1432; 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_debugging */ + net_req->debug_start_time = 0; + net_req->debug_end_time = 0; + net_req->debug_tls_auth_status = 0; net_req->wire_data_sz = wire_data_sz; if (max_query_sz) { @@ -184,6 +190,75 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, return r; } +/* req->opt + 9 is the length; req->opt + 11 is the start of the + options. + + clear_upstream_options() goes back to the per-query options. + */ + +void +_getdns_network_req_clear_upstream_options(getdns_network_req * req) +{ + size_t pktlen; + if (req->opt) { + gldns_write_uint16(req->opt + 9, req->base_query_option_sz); + req->response = req->opt + 11 + req->base_query_option_sz; + pktlen = req->response - req->query; + gldns_write_uint16(req->query - 2, pktlen); + } +} + +/* 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 +_getdns_network_req_add_upstream_option(getdns_network_req * req, uint16_t code, uint16_t sz, const void* data) +{ + uint16_t oldlen; + uint32_t newlen; + uint32_t pktlen; + size_t cur_upstream_option_sz; + + /* if no options are set, we can't add upstream options */ + if (!req->opt) + return GETDNS_RETURN_GENERIC_ERROR; + + /* if TCP, no overflow allowed for length field + https://tools.ietf.org/html/rfc1035#section-4.2.2 */ + pktlen = req->response - req->query; + pktlen += 4 + sz; + if (pktlen > UINT16_MAX) + return GETDNS_RETURN_GENERIC_ERROR; + + /* no overflow allowed for OPT size either (maybe this is overkill + given the above check?) */ + oldlen = gldns_read_uint16(req->opt + 9); + newlen = oldlen + 4 + sz; + if (newlen > UINT16_MAX) + return GETDNS_RETURN_GENERIC_ERROR; + + /* avoid overflowing MAXIMUM_UPSTREAM_OPTION_SPACE */ + cur_upstream_option_sz = (size_t)oldlen - req->base_query_option_sz; + if (cur_upstream_option_sz + 4 + sz > MAXIMUM_UPSTREAM_OPTION_SPACE) + return GETDNS_RETURN_GENERIC_ERROR; + + /* actually add the option: */ + gldns_write_uint16(req->opt + 11 + oldlen, code); + gldns_write_uint16(req->opt + 11 + oldlen + 2, sz); + if (data != NULL) + memcpy(req->opt + 11 + oldlen + 4, data, sz); + else + memset(req->opt + 11 + oldlen + 4, 0, sz); + gldns_write_uint16(req->opt + 9, newlen); + + /* the response should start right after the options end: */ + req->response = req->opt + 11 + newlen; + + /* for TCP, adjust the size of the wire format itself: */ + gldns_write_uint16(req->query - 2, pktlen); + + return GETDNS_RETURN_GOOD; +} + void _getdns_dns_req_free(getdns_dns_req * req) { @@ -306,7 +381,8 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, with_opt = edns_do_bit != 0 || edns_maximum_udp_payload_size != 512 || edns_extended_rcode != 0 || edns_version != 0 || noptions || - edns_cookies; + edns_cookies || context->edns_client_subnet_private || + context->tls_query_padding_blocksize > 1; edns_maximum_udp_payload_size = with_opt && ( edns_maximum_udp_payload_size == -1 || @@ -337,12 +413,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 */ + 12 + opt_options_size /* space needed for OPT (if needed) */ - + ( !edns_cookies ? 0 - : 2 /* EDNS0 Option Code */ - + 2 /* Option length = 8 + 16 = 24 */ - + 8 /* client cookie */ - + 16 /* server cookie */ - ) + + MAXIMUM_UPSTREAM_OPTION_SPACE /* TODO: TSIG */ + 7) / 8 * 8; } @@ -390,7 +461,11 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, result->dnssec_roadblock_avoidance = dnssec_roadblock_avoidance; result->avoid_dnssec_roadblocks = 0; #endif - + result->edns_client_subnet_private = context->edns_client_subnet_private; + result->tls_query_padding_blocksize = context->tls_query_padding_blocksize; + result->return_call_debugging + = is_extension_set(extensions, "return_call_debugging"); + /* will be set by caller */ result->user_pointer = NULL; result->user_callback = NULL; diff --git a/src/stub.c b/src/stub.c index 0fcaefe4..d211b486 100644 --- a/src/stub.c +++ b/src/stub.c @@ -46,6 +46,7 @@ #include "util-internal.h" #include "general.h" +#define STUB_OUT_OF_OPTIONS -5 /* upstream options exceeded MAXIMUM_UPSTREAM_OPTION_SPACE */ #define STUB_TLS_SETUP_ERROR -4 #define STUB_TCP_AGAIN -3 #define STUB_TCP_ERROR -2 @@ -129,9 +130,27 @@ calc_new_cookie(getdns_upstream *upstream, uint8_t *cookie) cookie[i % 8] ^= md_value[i]; } -static uint8_t * -attach_edns_cookie(getdns_upstream *upstream, uint8_t *opt) +static getdns_return_t +attach_edns_client_subnet_private(getdns_network_req *req) { + /* see + * https://tools.ietf.org/html/draft-ietf-dnsop-edns-client-subnet-04#section-6 */ + /* all-zeros is a request to not leak the data further: */ + /* "\x00\x00" FAMILY: 0 (because no address) */ + /* "\x00" SOURCE PREFIX-LENGTH: 0 */ + /* "\x00"; SCOPE PREFIX-LENGTH: 0 */ + return _getdns_network_req_add_upstream_option(req, + GLDNS_EDNS_CLIENT_SUBNET, + 4, NULL); +} + +static getdns_return_t +attach_edns_cookie(getdns_network_req *req) +{ + getdns_upstream *upstream = req->upstream; + uint16_t sz; + void* val; + uint8_t buf[8 + 32]; /* server cookies can be no larger than 32 bytes */ rollover_secret(); if (!upstream->has_client_cookie) { @@ -139,12 +158,8 @@ attach_edns_cookie(getdns_upstream *upstream, uint8_t *opt) upstream->secret = secret; upstream->has_client_cookie = 1; - gldns_write_uint16(opt + 9, 12); /* rdata len */ - gldns_write_uint16(opt + 11, EDNS_COOKIE_OPCODE); - gldns_write_uint16(opt + 13, 8); /* opt len */ - memcpy(opt + 15, upstream->client_cookie, 8); - return opt + 23; - + sz = 8; + val = upstream->client_cookie; } else if (upstream->secret != secret) { memcpy( upstream->prev_client_cookie , upstream->client_cookie, 8); @@ -152,29 +167,19 @@ attach_edns_cookie(getdns_upstream *upstream, uint8_t *opt) calc_new_cookie(upstream, upstream->client_cookie); upstream->secret = secret; - gldns_write_uint16(opt + 9, 12); /* rdata len */ - gldns_write_uint16(opt + 11, EDNS_COOKIE_OPCODE); - gldns_write_uint16(opt + 13, 8); /* opt len */ - memcpy(opt + 15, upstream->client_cookie, 8); - return opt + 23; - + sz = 8; + val = upstream->client_cookie; } else if (!upstream->has_server_cookie) { - gldns_write_uint16(opt + 9, 12); /* rdata len */ - gldns_write_uint16(opt + 11, EDNS_COOKIE_OPCODE); - gldns_write_uint16(opt + 13, 8); /* opt len */ - memcpy(opt + 15, upstream->client_cookie, 8); - return opt + 23; + sz = 8; + val = upstream->client_cookie; } else { - gldns_write_uint16( opt + 9, 12 /* rdata len */ - + upstream->server_cookie_len); - gldns_write_uint16(opt + 11, EDNS_COOKIE_OPCODE); - gldns_write_uint16(opt + 13, 8 /* opt len */ - + upstream->server_cookie_len); - memcpy(opt + 15, upstream->client_cookie, 8); - memcpy(opt + 23, upstream->server_cookie - , upstream->server_cookie_len); - return opt + 23+ upstream->server_cookie_len; + sz = 8 + upstream->server_cookie_len; + memcpy(buf, upstream->client_cookie, 8); + memcpy(buf+8, upstream->server_cookie, upstream->server_cookie_len); + val = buf; } + return _getdns_network_req_add_upstream_option(req, EDNS_COOKIE_OPCODE, sz, val); + } static int @@ -681,7 +686,7 @@ static int stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) { - size_t pkt_len = netreq->response - netreq->query; + size_t pkt_len; ssize_t written; uint16_t query_id; intptr_t query_id_intptr; @@ -704,16 +709,18 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) GLDNS_ID_SET(netreq->query, query_id); if (netreq->opt) { + _getdns_network_req_clear_upstream_options(netreq); /* no limits on the max udp payload size with tcp */ gldns_write_uint16(netreq->opt + 3, 65535); - if (netreq->owner->edns_cookies) { - netreq->response = attach_edns_cookie( - netreq->upstream, netreq->opt); - pkt_len = netreq->response - netreq->query; - gldns_write_uint16(netreq->query - 2, pkt_len); - } + if (netreq->owner->edns_cookies) + if (attach_edns_cookie(netreq)) + return STUB_OUT_OF_OPTIONS; + if (netreq->owner->edns_client_subnet_private) + if (attach_edns_client_subnet_private(netreq)) + return STUB_OUT_OF_OPTIONS; } + pkt_len = netreq->response - netreq->query; /* We have an initialized packet buffer. * Lets see how much of it we can write */ @@ -908,7 +915,7 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) return NULL; } #endif - /* Allow fallback to oppotunisitc if settings permit it*/ + /* Allow fallback to opportunistic if settings permit it*/ if (dnsreq->netreqs[0]->tls_auth_min == GETDNS_AUTHENTICATION_HOSTNAME) SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_callback); else { @@ -923,6 +930,7 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) upstream->tls_auth_failed = 1; return NULL; } else { + /* no hostname verification, so we will make opportunistic connections */ DEBUG_STUB("--- %s, PROCEEDING WITHOUT HOSTNAME VALIDATION!!\n", __FUNCTION__); upstream->tls_auth_failed = 1; SSL_set_verify(ssl, SSL_VERIFY_NONE, tls_verify_callback_with_fallback); @@ -1125,11 +1133,12 @@ static int stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, getdns_network_req *netreq) { - size_t pkt_len = netreq->response - netreq->query; + size_t pkt_len; ssize_t written; uint16_t query_id; intptr_t query_id_intptr; SSL* tls_obj = upstream->tls_obj; + uint16_t padding_sz; int q = tls_connected(upstream); if (q != 0) @@ -1155,10 +1164,30 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, &netreq->upstream->netreq_by_query_id, &netreq->node)); GLDNS_ID_SET(netreq->query, query_id); - if (netreq->opt) + if (netreq->opt) { + _getdns_network_req_clear_upstream_options(netreq); /* no limits on the max udp payload size with tcp */ gldns_write_uint16(netreq->opt + 3, 65535); + /* we do not edns_cookie over TLS, since TLS + * provides stronger guarantees than cookies + * already */ + if (netreq->owner->edns_client_subnet_private) + if (attach_edns_client_subnet_private(netreq)) + return STUB_OUT_OF_OPTIONS; + if (netreq->owner->tls_query_padding_blocksize > 1) { + pkt_len = netreq->response - netreq->query; + pkt_len += 4; /* this accounts for the OPTION-CODE and OPTION-LENGTH of the padding */ + padding_sz = pkt_len % netreq->owner->tls_query_padding_blocksize; + if (padding_sz) + padding_sz = netreq->owner->tls_query_padding_blocksize - padding_sz; + if (_getdns_network_req_add_upstream_option(netreq, + EDNS_PADDING_OPCODE, + padding_sz, NULL)) + return STUB_OUT_OF_OPTIONS; + } + } + pkt_len = netreq->response - netreq->query; /* We have an initialized packet buffer. * Lets see how much of it we can write */ @@ -1176,6 +1205,19 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, return STUB_TCP_ERROR; } +static uint64_t +_getdns_get_time_as_uintt64() { + + struct timeval tv; + uint64_t now; + + if (gettimeofday(&tv, NULL)) { + return 0; + } + now = tv.tv_sec * 1000000 + tv.tv_usec; + return now; +} + /**************************/ /* UDP callback functions */ /**************************/ @@ -1237,6 +1279,7 @@ stub_udp_read_cb(void *userarg) netreq->response_len = read; dnsreq->upstreams->current = 0; done: + netreq->debug_end_time = _getdns_get_time_as_uintt64(); netreq->state = NET_REQ_FINISHED; _getdns_check_dns_req_complete(dnsreq); } @@ -1247,25 +1290,28 @@ stub_udp_write_cb(void *userarg) DEBUG_STUB("%s\n", __FUNCTION__); getdns_network_req *netreq = (getdns_network_req *)userarg; getdns_dns_req *dnsreq = netreq->owner; - size_t pkt_len = netreq->response - netreq->query; + size_t pkt_len; GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event); + netreq->debug_start_time = _getdns_get_time_as_uintt64(); netreq->query_id = arc4random(); GLDNS_ID_SET(netreq->query, netreq->query_id); if (netreq->opt) { + _getdns_network_req_clear_upstream_options(netreq); if (netreq->edns_maximum_udp_payload_size == -1) gldns_write_uint16(netreq->opt + 3, ( netreq->max_udp_payload_size = netreq->upstream->addr.ss_family == AF_INET6 ? 1232 : 1432)); - if (netreq->owner->edns_cookies) { - netreq->response = attach_edns_cookie( - netreq->upstream, netreq->opt); - pkt_len = netreq->response - netreq->query; - } + if (netreq->owner->edns_cookies) + if (attach_edns_cookie(netreq)) + return; /* too many upstream options */ + if (netreq->owner->edns_client_subnet_private) + if (attach_edns_client_subnet_private(netreq)) + return; /* too many upstream options */ } - + pkt_len = netreq->response - netreq->query; if ((ssize_t)pkt_len != sendto(netreq->fd, netreq->query, pkt_len, 0, (struct sockaddr *)&netreq->upstream->addr, netreq->upstream->addr_len)) { @@ -1314,7 +1360,7 @@ stub_tcp_read_cb(void *userarg) netreq->tcp.read_pos - netreq->tcp.read_buf; netreq->tcp.read_buf = NULL; dnsreq->upstreams->current = 0; - + netreq->debug_end_time = _getdns_get_time_as_uintt64(); stub_cleanup(netreq); close(netreq->fd); _getdns_check_dns_req_complete(dnsreq); @@ -1327,7 +1373,7 @@ stub_tcp_write_cb(void *userarg) getdns_network_req *netreq = (getdns_network_req *)userarg; getdns_dns_req *dnsreq = netreq->owner; int q; - + netreq->debug_start_time = _getdns_get_time_as_uintt64(); switch ((q = stub_tcp_write(netreq->fd, &netreq->tcp, netreq))) { case STUB_TCP_AGAIN: return; @@ -1420,7 +1466,7 @@ upstream_read_cb(void *userarg) getdns_eventloop_event_init(&upstream->event, upstream, NULL, upstream_write_cb, NULL)); } - + netreq->debug_end_time = _getdns_get_time_as_uintt64(); /* This also reschedules events for the upstream*/ stub_cleanup(netreq); @@ -1447,7 +1493,10 @@ upstream_write_cb(void *userarg) getdns_network_req *netreq = upstream->write_queue; getdns_dns_req *dnsreq = netreq->owner; int q; - + + /* TODO: think about TCP AGAIN */ + netreq->debug_start_time = _getdns_get_time_as_uintt64(); + DEBUG_STUB("--- WRITE: %s: %p TYPE: %d\n", __FUNCTION__, netreq, netreq->request_type); if (tls_requested(netreq) && tls_should_write(upstream)) @@ -1477,6 +1526,8 @@ upstream_write_cb(void *userarg) return; default: + /* Need this because auth status is reset on connection clode */ + netreq->debug_tls_auth_status = netreq->upstream->tls_auth_failed; upstream->writes_done++; netreq->query_id = (uint16_t) q; /* Unqueue the netreq from the write_queue */ diff --git a/src/test/check_getdns_context_set_context_update_callback.h b/src/test/check_getdns_context_set_context_update_callback.h index 67bfc928..2b5e6674 100644 --- a/src/test/check_getdns_context_set_context_update_callback.h +++ b/src/test/check_getdns_context_set_context_update_callback.h @@ -365,6 +365,54 @@ } END_TEST + START_TEST (getdns_context_set_context_update_callback_20) + { + /* + * Create a context by calling getdns_context_create() + * Define a callback routine for context changes and call getdns_context_set_context_update_callback() so that it gets called when there are context changes + * Call getdns_context_set_edns_client_subnet_private() setting to 1 + * expect: GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE + */ + struct getdns_context *context = NULL; + CONTEXT_CREATE(TRUE); + + ASSERT_RC(getdns_context_set_context_update_callback(context, update_callbackfn), + GETDNS_RETURN_GOOD, "Return code from getdns_context_set_context_update_callback()"); + + expected_changed_item = GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE; + + ASSERT_RC(getdns_context_set_edns_client_subnet_private(context, 1), + GETDNS_RETURN_GOOD, "Return code from getdns_context_set_edns_client_subnet_private()"); + + CONTEXT_DESTROY; + + } + END_TEST + + START_TEST (getdns_context_set_context_update_callback_21) + { + /* + * Create a context by calling getdns_context_create() + * Define a callback routine for context changes and call getdns_context_set_context_update_callback() so that it gets called when there are context changes + * Call getdns_context_set_edns_client_subnet_private() setting to 1 + * expect: GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE + */ + struct getdns_context *context = NULL; + CONTEXT_CREATE(TRUE); + + ASSERT_RC(getdns_context_set_context_update_callback(context, update_callbackfn), + GETDNS_RETURN_GOOD, "Return code from getdns_context_set_context_update_callback()"); + + expected_changed_item = GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE; + + ASSERT_RC(getdns_context_set_tls_query_padding_blocksize(context, 1400), + GETDNS_RETURN_GOOD, "Return code from getdns_context_set_tls_query_padding_blocksize()"); + + CONTEXT_DESTROY; + + } + END_TEST + Suite * @@ -391,6 +439,8 @@ tcase_add_test(tc_pos, getdns_context_set_context_update_callback_17); tcase_add_test(tc_pos, getdns_context_set_context_update_callback_18); tcase_add_test(tc_pos, getdns_context_set_context_update_callback_19); + tcase_add_test(tc_pos, getdns_context_set_context_update_callback_20); + tcase_add_test(tc_pos, getdns_context_set_context_update_callback_21); suite_add_tcase(s, tc_pos); return s; diff --git a/src/test/getdns_query.c b/src/test/getdns_query.c index 6c1421a6..b2319ad8 100644 --- a/src/test/getdns_query.c +++ b/src/test/getdns_query.c @@ -261,7 +261,7 @@ static char *name; static getdns_context *context; static getdns_dict *extensions; static uint16_t request_type = GETDNS_RRTYPE_NS; -static int timeout, edns0_size; +static int timeout, edns0_size, padding_blocksize; static int async = 0, interactive = 0; static enum { GENERAL, ADDRESS, HOSTNAME, SERVICE } calltype = GENERAL; @@ -368,6 +368,7 @@ print_usage(FILE *out, const char *progname) fprintf(out, "\t-A\taddress lookup ( is ignored)\n"); fprintf(out, "\t-B\tBatch mode. Schedule all messages before processing responses.\n"); fprintf(out, "\t-b \tSet edns0 max_udp_payload size\n"); + fprintf(out, "\t-c\tSend Client Subnet privacy request\n"); fprintf(out, "\t-D\tSet edns0 do bit\n"); fprintf(out, "\t-d\tclear edns0 do bit\n"); fprintf(out, "\t-e \tSet idle timeout in miliseconds\n"); @@ -383,6 +384,7 @@ print_usage(FILE *out, const char *progname) fprintf(out, "\t-n\tSet TLS authentication mode to NONE (default)\n"); fprintf(out, "\t-m\tSet TLS authentication mode to HOSTNAME\n"); fprintf(out, "\t-p\tPretty print response dict\n"); + fprintf(out, "\t-P \tPad TLS queries to a multiple of blocksize\n"); fprintf(out, "\t-r\tSet recursing resolution type\n"); fprintf(out, "\t-q\tQuiet mode - don't print response\n"); fprintf(out, "\t-s\tSet stub resolution type (default = recursing)\n"); @@ -655,6 +657,10 @@ getdns_return_t parse_args(int argc, char **argv) getdns_context_set_edns_maximum_udp_payload_size( context, (uint16_t) edns0_size); goto next; + case 'c': + if (getdns_context_set_edns_client_subnet_private(context, 1)) + return GETDNS_RETURN_GENERIC_ERROR; + break; case 'D': (void) getdns_context_set_edns_do_bit(context, 1); break; @@ -702,6 +708,23 @@ getdns_return_t parse_args(int argc, char **argv) getdns_context_set_tls_authentication(context, GETDNS_AUTHENTICATION_HOSTNAME); break; + case 'P': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "tls_query_padding_blocksize " + "expected after -P\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + padding_blocksize = strtol(argv[i], &endptr, 10); + if (*endptr || padding_blocksize < 0) { + fprintf(stderr, "non-negative " + "numeric padding blocksize expected " + "after -P\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + if (getdns_context_set_tls_query_padding_blocksize( + context, padding_blocksize)) + return GETDNS_RETURN_GENERIC_ERROR; + goto next; case 'p': json = 0; case 'q': diff --git a/src/test/tests_transports.sh b/src/test/tests_transports.sh index 15d31341..75efe2ca 100755 --- a/src/test/tests_transports.sh +++ b/src/test/tests_transports.sh @@ -2,7 +2,7 @@ DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) SERVER_IP="8.8.8.8" -TLS_SERVER_IP="185.49.141.38~www.dnssec-name-and-shame.com" +TLS_SERVER_IP="185.49.141.38~getdnsapi.net" GOOD_RESULT_SYNC="Status was: At least one response was returned" GOOD_RESULT_ASYNC="successfull" BAD_RESULT_SYNC="1 'Generic error'" @@ -58,7 +58,7 @@ usage () { echo " -p path to getdns_query binary" echo " -s server configured for only TCP and UDP" echo " -t server configured for TLS, STARTTLS, TCP and UDP" - echo " (This must include the hostname e.g. 185.49.141.38~www.dnssec-name-and-shame.com)" + echo " (This must include the hostname e.g. 185.49.141.38~getdnsapi.net)" } while getopts ":p:s:t:dh" opt; do @@ -130,4 +130,4 @@ done echo echo "Finished transport test: did $COUNT queries, $GOOD_COUNT passes, $FAIL_COUNT failures" -echo \ No newline at end of file +echo diff --git a/src/types-internal.h b/src/types-internal.h index 11629d84..7fdd6f71 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -226,17 +226,37 @@ typedef struct getdns_network_req /* Network requests scheduled to write after me */ struct getdns_network_req *write_queue_tail; + /* Some fields to record info for return_call_debugging */ + uint64_t debug_start_time; + uint64_t debug_end_time; + size_t debug_tls_auth_status; + /* 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 *query; uint8_t *opt; /* offset of OPT RR in query */ + + /* each network_req has a set of base options that are + * specific to the query, which are static and included when + * the network_req is created. When the query is sent out to + * a given upstream, some additional options are added that + * are specific to the upstream. There can be at most + * GETDNS_MAXIMUM_UPSTREAM_OPTION_SPACE bytes of + * upstream-specific options. + + * use _getdns_network_req_clear_upstream_options() and + * _getdns_network_req_add_upstream_option() to fiddle with the + */ + size_t base_query_option_sz; size_t response_len; uint8_t *response; size_t wire_data_sz; uint8_t wire_data[]; + + } getdns_network_req; /** @@ -266,6 +286,9 @@ typedef struct getdns_dns_req { int avoid_dnssec_roadblocks; #endif int edns_cookies; + int edns_client_subnet_private; + uint16_t tls_query_padding_blocksize; + int return_call_debugging; /* Internally used by return_validation_chain */ int dnssec_ok_checking_disabled; @@ -348,5 +371,10 @@ getdns_dns_req *_getdns_dns_req_new(getdns_context *context, getdns_eventloop *l void _getdns_dns_req_free(getdns_dns_req * req); +/* network request utils */ +getdns_return_t _getdns_network_req_add_upstream_option(getdns_network_req * req, + uint16_t code, uint16_t sz, const void* data); +void _getdns_network_req_clear_upstream_options(getdns_network_req * req); + #endif /* types-internal.h */ diff --git a/src/util-internal.c b/src/util-internal.c index cc82dab1..02ae659f 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -675,6 +675,69 @@ success: return result; } +getdns_dict * +_getdns_create_call_debugging_dict( + getdns_context *context, getdns_network_req *netreq) +{ + getdns_bindata qname; + getdns_dict *netreq_debug; + getdns_dict *address_debug = NULL; + + assert(netreq); + + /* It is the responsibility of the caller to free this */ + if (!(netreq_debug = getdns_dict_create_with_context(context))) + return NULL; + + qname.data = netreq->owner->name; + qname.size = netreq->owner->name_len; + + if (getdns_dict_set_bindata(netreq_debug, "query_name", &qname) || + getdns_dict_set_int( netreq_debug, "query_type" + , netreq->request_type ) || + + /* Safe, because uint32_t facilitates RRT's of almost 50 days*/ + getdns_dict_set_int(netreq_debug, "run_time/ms", + (uint32_t)(( netreq->debug_end_time + - netreq->debug_start_time)/1000))) { + + getdns_dict_destroy(netreq_debug); + return NULL; + + } else if (!netreq->upstream) + + /* Nothing more for full recursion */ + return netreq_debug; + + + /* Stub resolver debug data */ + _getdns_sockaddr_to_dict( + context, &netreq->upstream->addr, &address_debug); + + if (getdns_dict_set_dict(netreq_debug, "query_to", address_debug) || + getdns_dict_set_int( netreq_debug, "transport" + , netreq->upstream->transport)) { + + getdns_dict_destroy(address_debug); + getdns_dict_destroy(netreq_debug); + return NULL; + } + getdns_dict_destroy(address_debug); + + if (netreq->upstream->transport != GETDNS_TRANSPORT_TLS) + return netreq_debug; + + /* Only include the auth status if TLS was used */ + if (getdns_dict_util_set_string(netreq_debug, "tls_auth_status", + netreq->debug_tls_auth_status == 0 ? + "OK: Hostname matched valid cert":"FAILED: Server not validated")){ + + getdns_dict_destroy(netreq_debug); + return NULL; + } + return netreq_debug; +} + getdns_dict * _getdns_create_getdns_response(getdns_dns_req *completed_request) { @@ -682,12 +745,14 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request) getdns_list *just_addrs = NULL; getdns_list *replies_full; getdns_list *replies_tree; + getdns_list *call_debugging = NULL; getdns_network_req *netreq, **netreq_p; int rrsigs_in_answer = 0; getdns_dict *reply; getdns_bindata *canonical_name = NULL; int nreplies = 0, nanswers = 0, nsecure = 0, ninsecure = 0, nbogus = 0; getdns_bindata full_data; + getdns_dict *netreq_debug; /* info (bools) about dns_req */ int dnssec_return_status; @@ -721,6 +786,10 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request) if (!(replies_tree = getdns_list_create_with_context(context))) goto error_free_replies_full; + if (completed_request->return_call_debugging && + !(call_debugging = getdns_list_create_with_context(context))) + goto error_free_replies_full; + for ( netreq_p = completed_request->netreqs ; (netreq = *netreq_p) ; netreq_p++) { @@ -777,6 +846,21 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request) getdns_dict_destroy(reply); goto error; } + + if (call_debugging) { + if (!(netreq_debug = + _getdns_create_call_debugging_dict(context,netreq))) + goto error; + + if (_getdns_list_append_dict( + call_debugging, netreq_debug)) { + + getdns_dict_destroy(netreq_debug); + goto error; + } + getdns_dict_destroy(netreq_debug); + } + getdns_dict_destroy(reply); /* buffer */ @@ -789,6 +873,10 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request) goto error; getdns_list_destroy(replies_tree); + if (call_debugging && + getdns_dict_set_list(result, "call_debugging", call_debugging)) + goto error_free_call_debugging; + if (getdns_dict_set_list(result, "replies_full", replies_full)) goto error_free_replies_full; getdns_list_destroy(replies_full); @@ -812,6 +900,8 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request) error: /* cleanup */ getdns_list_destroy(replies_tree); +error_free_call_debugging: + getdns_list_destroy(call_debugging); error_free_replies_full: getdns_list_destroy(replies_full); error_free_result: