mirror of https://github.com/getdnsapi/getdns.git
clarify per-query options vs. per-upstream options
Sending DNS cookies was overwriting any existing options (DNS OPT) in the outbound query. Also, DNS cookies may not be the only option that gets set per-upstream (instead of per-query). This changeset establishes a set of per-query options (established at the time of the query), and a buffer of additional space for adding options based on the upstream is in use. The size of this buffer is defined at configure time (defaults to 3000 octets). Just before a query is sent out, we add the per-upstream options to the query. Note: we're also standardizing the query in tls too, even though we're not sending any upstream options in that case at the moment (edns_cookies are much weaker than TLS itself)
This commit is contained in:
parent
3e90795680
commit
0b388872ea
|
@ -408,6 +408,8 @@ 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])
|
||||
|
||||
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
|
||||
|
|
|
@ -113,6 +113,7 @@ 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;
|
||||
|
||||
net_req->wire_data_sz = wire_data_sz;
|
||||
if (max_query_sz) {
|
||||
|
@ -184,6 +185,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)
|
||||
{
|
||||
|
@ -323,12 +393,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;
|
||||
}
|
||||
|
|
83
src/stub.c
83
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,13 @@ 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_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 +144,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 +153,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 +672,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 +695,15 @@ 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;
|
||||
}
|
||||
pkt_len = netreq->response - netreq->query;
|
||||
/* We have an initialized packet buffer.
|
||||
* Lets see how much of it we can write
|
||||
*/
|
||||
|
@ -1126,7 +1116,7 @@ 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;
|
||||
|
@ -1156,10 +1146,16 @@ 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 */
|
||||
}
|
||||
|
||||
pkt_len = netreq->response - netreq->query;
|
||||
/* We have an initialized packet buffer.
|
||||
* Lets see how much of it we can write */
|
||||
|
||||
|
@ -1248,25 +1244,24 @@ 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->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 */
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
|
|
@ -232,6 +232,19 @@ typedef struct getdns_network_req
|
|||
*/
|
||||
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;
|
||||
|
@ -344,5 +357,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 */
|
||||
|
|
Loading…
Reference in New Issue