mirror of https://github.com/getdnsapi/getdns.git
Merge pull request #471 from getdnsapi/feature/privacy-aware-cookies
Privacy aware DNS Cookies
This commit is contained in:
commit
5c79e2c731
|
@ -248,6 +248,7 @@ static struct const_name_info consts_name_info[] = {
|
|||
{ "GETDNS_OPCODE_STATUS", 2 },
|
||||
{ "GETDNS_OPCODE_UPDATE", 5 },
|
||||
{ "GETDNS_RCODE_BADALG", 21 },
|
||||
{ "GETDNS_RCODE_BADCOOKIE", 23 },
|
||||
{ "GETDNS_RCODE_BADKEY", 17 },
|
||||
{ "GETDNS_RCODE_BADMODE", 19 },
|
||||
{ "GETDNS_RCODE_BADNAME", 20 },
|
||||
|
@ -255,7 +256,6 @@ static struct const_name_info consts_name_info[] = {
|
|||
{ "GETDNS_RCODE_BADTIME", 18 },
|
||||
{ "GETDNS_RCODE_BADTRUNC", 22 },
|
||||
{ "GETDNS_RCODE_BADVERS", 16 },
|
||||
{ "GETDNS_RCODE_COOKIE", 23 },
|
||||
{ "GETDNS_RCODE_FORMERR", 1 },
|
||||
{ "GETDNS_RCODE_NOERROR", 0 },
|
||||
{ "GETDNS_RCODE_NOTAUTH", 9 },
|
||||
|
|
|
@ -953,9 +953,12 @@ upstream_init(getdns_upstream *upstream,
|
|||
(void) getdns_eventloop_event_init(
|
||||
&upstream->finished_event, upstream, NULL, NULL, NULL);
|
||||
|
||||
upstream->has_client_cookie = 0;
|
||||
upstream->has_prev_client_cookie = 0;
|
||||
upstream->has_server_cookie = 0;
|
||||
upstream->server_cookie_len = 0;
|
||||
(void) memset(&upstream->server_cookie, 0,
|
||||
sizeof(upstream->server_cookie));
|
||||
upstream->src_addr_checked = 0;
|
||||
(void) memset(&upstream->src_addr, 0, sizeof(upstream->src_addr));
|
||||
upstream->src_addr_len = 0;
|
||||
|
||||
upstream->tsig_alg = GETDNS_NO_TSIG;
|
||||
upstream->tsig_dname_len = 0;
|
||||
|
|
|
@ -243,15 +243,13 @@ typedef struct getdns_upstream {
|
|||
unsigned is_sync_loop : 1;
|
||||
|
||||
/* EDNS cookies */
|
||||
uint32_t secret;
|
||||
uint8_t client_cookie[8];
|
||||
uint8_t prev_client_cookie[8];
|
||||
uint8_t server_cookie[32];
|
||||
|
||||
unsigned has_client_cookie : 1;
|
||||
unsigned has_prev_client_cookie : 1;
|
||||
unsigned has_server_cookie : 1;
|
||||
unsigned server_cookie_len : 5;
|
||||
uint8_t server_cookie[40];
|
||||
size_t server_cookie_len;
|
||||
|
||||
uint64_t src_addr_checked;
|
||||
struct sockaddr_storage src_addr;
|
||||
socklen_t src_addr_len;
|
||||
char src_addr_str[INET6_ADDRSTRLEN];
|
||||
|
||||
/* TSIG */
|
||||
uint8_t tsig_dname[256];
|
||||
|
|
27
src/dict.c
27
src/dict.c
|
@ -1036,21 +1036,21 @@ static int
|
|||
_getdns_print_rcode(gldns_buffer *buf, uint32_t rcode)
|
||||
{
|
||||
static const char *rcodes[] = {
|
||||
" GETDNS_RCODE_NOERROR" , " GETDNS_RCODE_FORMERR" ,
|
||||
" GETDNS_RCODE_SERVFAIL", " GETDNS_RCODE_NXDOMAIN",
|
||||
" GETDNS_RCODE_NOTIMP" , " GETDNS_RCODE_REFUSED" ,
|
||||
" GETDNS_RCODE_YXDOMAIN", " GETDNS_RCODE_YXRRSET" ,
|
||||
" GETDNS_RCODE_NXRRSET" , " GETDNS_RCODE_NOTAUTH" ,
|
||||
" GETDNS_RCODE_NOERROR" , " GETDNS_RCODE_FORMERR" ,
|
||||
" GETDNS_RCODE_SERVFAIL", " GETDNS_RCODE_NXDOMAIN" ,
|
||||
" GETDNS_RCODE_NOTIMP" , " GETDNS_RCODE_REFUSED" ,
|
||||
" GETDNS_RCODE_YXDOMAIN", " GETDNS_RCODE_YXRRSET" ,
|
||||
" GETDNS_RCODE_NXRRSET" , " GETDNS_RCODE_NOTAUTH" ,
|
||||
" GETDNS_RCODE_NOTZONE" ,
|
||||
" GETDNS_RCODE_BADSIG" , " GETDNS_RCODE_BADKEY" ,
|
||||
" GETDNS_RCODE_BADTIME" , " GETDNS_RCODE_BADMODE" ,
|
||||
" GETDNS_RCODE_BADNAME" , " GETDNS_RCODE_BADALG" ,
|
||||
" GETDNS_RCODE_BADTRUNC"
|
||||
" GETDNS_RCODE_BADSIG" , " GETDNS_RCODE_BADKEY" ,
|
||||
" GETDNS_RCODE_BADTIME" , " GETDNS_RCODE_BADMODE" ,
|
||||
" GETDNS_RCODE_BADNAME" , " GETDNS_RCODE_BADALG" ,
|
||||
" GETDNS_RCODE_BADTRUNC", " GETDNS_RCODE_BADCOOKIE"
|
||||
};
|
||||
if (rcode <= 10)
|
||||
(void) gldns_buffer_printf(buf, "%s", rcodes[rcode]);
|
||||
else if (rcode >= 16 && rcode <= 22)
|
||||
(void) gldns_buffer_printf(buf, "%s", rcodes[rcode-6]);
|
||||
else if (rcode >= 16 && rcode <= 23)
|
||||
(void) gldns_buffer_printf(buf, "%s", rcodes[rcode-5]);
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
|
@ -1156,6 +1156,11 @@ getdns_pp_dict(gldns_buffer * buf, size_t indent,
|
|||
if (!json && strcmp(item->node.key, "rcode") == 0 &&
|
||||
_getdns_print_rcode(buf, item->i.data.n))
|
||||
break;
|
||||
if (!json &&
|
||||
strcmp(item->node.key, "extended_rcode") == 0 &&
|
||||
item->i.data.n >= 16 &&
|
||||
_getdns_print_rcode(buf, item->i.data.n))
|
||||
break;
|
||||
if (gldns_buffer_printf(
|
||||
buf,(json < 2 ? " %d" : "%d"), item->i.data.n) < 0)
|
||||
return -1;
|
||||
|
|
|
@ -473,26 +473,26 @@ typedef enum getdns_callback_type_t {
|
|||
* \defgroup rcodes Rcodes
|
||||
* @{
|
||||
*/
|
||||
#define GETDNS_RCODE_NOERROR 0
|
||||
#define GETDNS_RCODE_FORMERR 1
|
||||
#define GETDNS_RCODE_SERVFAIL 2
|
||||
#define GETDNS_RCODE_NXDOMAIN 3
|
||||
#define GETDNS_RCODE_NOTIMP 4
|
||||
#define GETDNS_RCODE_REFUSED 5
|
||||
#define GETDNS_RCODE_YXDOMAIN 6
|
||||
#define GETDNS_RCODE_YXRRSET 7
|
||||
#define GETDNS_RCODE_NXRRSET 8
|
||||
#define GETDNS_RCODE_NOTAUTH 9
|
||||
#define GETDNS_RCODE_NOTZONE 10
|
||||
#define GETDNS_RCODE_BADVERS 16
|
||||
#define GETDNS_RCODE_BADSIG 16
|
||||
#define GETDNS_RCODE_BADKEY 17
|
||||
#define GETDNS_RCODE_BADTIME 18
|
||||
#define GETDNS_RCODE_BADMODE 19
|
||||
#define GETDNS_RCODE_BADNAME 20
|
||||
#define GETDNS_RCODE_BADALG 21
|
||||
#define GETDNS_RCODE_BADTRUNC 22
|
||||
#define GETDNS_RCODE_COOKIE 23
|
||||
#define GETDNS_RCODE_NOERROR 0
|
||||
#define GETDNS_RCODE_FORMERR 1
|
||||
#define GETDNS_RCODE_SERVFAIL 2
|
||||
#define GETDNS_RCODE_NXDOMAIN 3
|
||||
#define GETDNS_RCODE_NOTIMP 4
|
||||
#define GETDNS_RCODE_REFUSED 5
|
||||
#define GETDNS_RCODE_YXDOMAIN 6
|
||||
#define GETDNS_RCODE_YXRRSET 7
|
||||
#define GETDNS_RCODE_NXRRSET 8
|
||||
#define GETDNS_RCODE_NOTAUTH 9
|
||||
#define GETDNS_RCODE_NOTZONE 10
|
||||
#define GETDNS_RCODE_BADVERS 16
|
||||
#define GETDNS_RCODE_BADSIG 16
|
||||
#define GETDNS_RCODE_BADKEY 17
|
||||
#define GETDNS_RCODE_BADTIME 18
|
||||
#define GETDNS_RCODE_BADMODE 19
|
||||
#define GETDNS_RCODE_BADNAME 20
|
||||
#define GETDNS_RCODE_BADALG 21
|
||||
#define GETDNS_RCODE_BADTRUNC 22
|
||||
#define GETDNS_RCODE_BADCOOKIE 23
|
||||
/** @}
|
||||
*/
|
||||
|
||||
|
|
|
@ -147,6 +147,7 @@ netreq_reset(getdns_network_req *net_req)
|
|||
net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE;
|
||||
net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE;
|
||||
net_req->response_len = 0;
|
||||
net_req->response_opt = NULL;
|
||||
/* Some fields to record info for return_call_reporting */
|
||||
net_req->debug_start_time = 0;
|
||||
net_req->debug_end_time = 0;
|
||||
|
@ -208,6 +209,9 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
|
|||
net_req->transport_current = 0;
|
||||
memset(&net_req->event, 0, sizeof(net_req->event));
|
||||
net_req->keepalive_sent = 0;
|
||||
net_req->badcookie_retry = 0;
|
||||
net_req->cookie_sent = 0;
|
||||
memset(&net_req->client_cookie, 0, sizeof(net_req->client_cookie));
|
||||
net_req->write_queue_tail = NULL;
|
||||
/* Some fields to record info for return_call_reporting */
|
||||
net_req->debug_tls_auth_status = GETDNS_AUTH_NONE;
|
||||
|
@ -308,6 +312,7 @@ _getdns_network_req_clear_upstream_options(getdns_network_req * req)
|
|||
req->response = req->opt + 11 + req->base_query_option_sz;
|
||||
pktlen = req->response - req->query;
|
||||
gldns_write_uint16(req->query - 2, (uint16_t) pktlen);
|
||||
req->response_opt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
415
src/stub.c
415
src/stub.c
|
@ -33,6 +33,10 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef USE_WINSOCK
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
/* Intercept and do not sent out COM DS queries with TLS
|
||||
* For debugging purposes only. Never commit with this turned on.
|
||||
*/
|
||||
|
@ -53,6 +57,7 @@
|
|||
#include "general.h"
|
||||
#include "pubkey-pinning.h"
|
||||
|
||||
|
||||
/* WSA TODO:
|
||||
* STUB_TCP_RETRY added to deal with edge triggered event loops (versus
|
||||
* level triggered). See also lines containing WSA TODO below...
|
||||
|
@ -73,10 +78,6 @@
|
|||
/* Arbritray number of message for EDNS keepalive resend*/
|
||||
#define EDNS_KEEPALIVE_RESEND 5
|
||||
|
||||
static time_t secret_rollover_time = 0;
|
||||
static uint32_t secret = 0;
|
||||
static uint32_t prev_secret = 0;
|
||||
|
||||
static void upstream_read_cb(void *userarg);
|
||||
static void upstream_write_cb(void *userarg);
|
||||
static void upstream_idle_timeout_cb(void *userarg);
|
||||
|
@ -97,47 +98,6 @@ uint64_t _getdns_get_time_as_uintt64();
|
|||
/* General utility functions */
|
||||
/*****************************/
|
||||
|
||||
static void
|
||||
rollover_secret()
|
||||
{
|
||||
time_t now = 0;
|
||||
|
||||
/* Create and roll server secrets */
|
||||
if (time(&now) <= secret_rollover_time)
|
||||
return;
|
||||
|
||||
/* Remember previous secret, in to keep answering on rollover
|
||||
* boundary with old cookie.
|
||||
*/
|
||||
prev_secret = secret;
|
||||
secret = arc4random();
|
||||
|
||||
/* Next rollover over EDNS_COOKIE_ROLLOVER_TIME with 30% jitter,
|
||||
* I.e. some offset + or - 15% of the future point in time.
|
||||
*/
|
||||
secret_rollover_time = now + (EDNS_COOKIE_ROLLOVER_TIME / 20 * 17)
|
||||
+ arc4random_uniform(EDNS_COOKIE_ROLLOVER_TIME / 10 * 3);
|
||||
}
|
||||
|
||||
static void
|
||||
calc_new_cookie(getdns_upstream *upstream, uint8_t *cookie)
|
||||
{
|
||||
unsigned char md_value[GETDNS_TLS_MAX_DIGEST_LENGTH];
|
||||
size_t md_len;
|
||||
size_t i;
|
||||
sa_family_t af = upstream->addr.ss_family;
|
||||
void *sa_addr = ((struct sockaddr*)&upstream->addr)->sa_data;
|
||||
size_t addr_len = ( af == AF_INET6 ? sizeof(struct sockaddr_in6)
|
||||
: af == AF_INET ? sizeof(struct sockaddr_in)
|
||||
: 0 ) - sizeof(sa_family_t);
|
||||
|
||||
_getdns_tls_cookie_sha256(secret, sa_addr, addr_len, md_value, &md_len);
|
||||
|
||||
(void) memset(cookie, 0, 8);
|
||||
for (i = 0; i < md_len; i++)
|
||||
cookie[i % 8] ^= md_value[i];
|
||||
}
|
||||
|
||||
static getdns_return_t
|
||||
attach_edns_client_subnet_private(getdns_network_req *req)
|
||||
{
|
||||
|
@ -169,154 +129,269 @@ 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) {
|
||||
calc_new_cookie(upstream, upstream->client_cookie);
|
||||
upstream->secret = secret;
|
||||
upstream->has_client_cookie = 1;
|
||||
if (upstream->server_cookie_len) {
|
||||
if (!req->badcookie_retry && bind(req->fd, (struct sockaddr *)
|
||||
&upstream->src_addr, upstream->src_addr_len)) {
|
||||
|
||||
sz = 8;
|
||||
val = upstream->client_cookie;
|
||||
} else if (upstream->secret != secret) {
|
||||
memcpy( upstream->prev_client_cookie
|
||||
, upstream->client_cookie, 8);
|
||||
upstream->has_prev_client_cookie = 1;
|
||||
calc_new_cookie(upstream, upstream->client_cookie);
|
||||
upstream->secret = secret;
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO,
|
||||
"%-40s : Upstream : %s with source address: %s. "
|
||||
"Renewing Client Cookie.\n", upstream->addr_str,
|
||||
strerror(errno), upstream->src_addr_str);
|
||||
|
||||
sz = 8;
|
||||
val = upstream->client_cookie;
|
||||
} else if (!upstream->has_server_cookie) {
|
||||
sz = 8;
|
||||
val = upstream->client_cookie;
|
||||
} else {
|
||||
sz = 8 + upstream->server_cookie_len;
|
||||
memcpy(buf, upstream->client_cookie, 8);
|
||||
memcpy(buf+8, upstream->server_cookie, upstream->server_cookie_len);
|
||||
val = buf;
|
||||
upstream->server_cookie_len = 0;
|
||||
upstream->src_addr_checked = 0;
|
||||
} else
|
||||
return _getdns_network_req_add_upstream_option(
|
||||
req, EDNS_COOKIE_OPCODE,
|
||||
upstream->server_cookie_len,
|
||||
upstream->server_cookie);
|
||||
}
|
||||
return _getdns_network_req_add_upstream_option(req, EDNS_COOKIE_OPCODE, sz, val);
|
||||
if (_getdns_get_now_ms() - upstream->src_addr_checked < 3600000) {
|
||||
/* This upstream has been registered to *not* support cookies.
|
||||
* We will recheck one hour after this was registered.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
/* Try to get a new server cookie for this upstream
|
||||
* Explicitly connect on UDP only.
|
||||
*/
|
||||
if (req->fd >= 0 && connect(req->fd,
|
||||
(struct sockaddr*)&upstream->addr, upstream->addr_len)) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_WARNING,
|
||||
"%-40s : Upstream : Could not connect(): %s. "
|
||||
"Required for privacy aware Cookies. Cookies disabled.\n",
|
||||
upstream->addr_str, strerror(errno));
|
||||
|
||||
/* Don't recheck for another hour */
|
||||
upstream->src_addr_checked = _getdns_get_now_ms();
|
||||
return 0;
|
||||
}
|
||||
/* Create new client cookie */
|
||||
req->cookie_sent = 1;
|
||||
gldns_write_uint32(req->client_cookie, arc4random());
|
||||
gldns_write_uint32(req->client_cookie + 4, arc4random());
|
||||
return _getdns_network_req_add_upstream_option(
|
||||
req, EDNS_COOKIE_OPCODE, 8, req->client_cookie);
|
||||
}
|
||||
|
||||
/* Will find a matching OPT RR, but leaves the caller to validate it
|
||||
*
|
||||
* Returns 2 when found
|
||||
* 0 when not found
|
||||
* and 1 on FORMERR
|
||||
*/
|
||||
#define MATCH_OPT_FOUND 2
|
||||
#define MATCH_OPT_NOT_FOUND 0
|
||||
#define MATCH_OPT_ERROR 1
|
||||
static int
|
||||
match_edns_opt_rr(uint16_t code, uint8_t *response, size_t response_len,
|
||||
match_edns_opt_rr(uint16_t code, getdns_network_req *netreq,
|
||||
const uint8_t **position, uint16_t *option_len)
|
||||
{
|
||||
_getdns_rr_iter rr_iter_storage, *rr_iter;
|
||||
const uint8_t *pos;
|
||||
const uint8_t *pos, *rdata_end;
|
||||
uint16_t rdata_len, opt_code = 0, opt_len = 0;
|
||||
static uint8_t *NO_OPT_RR = (uint8_t *)"\x00\x00";
|
||||
|
||||
/* Search for the OPT RR (if any) */
|
||||
for ( rr_iter = _getdns_rr_iter_init(&rr_iter_storage
|
||||
, response, response_len)
|
||||
; rr_iter
|
||||
; rr_iter = _getdns_rr_iter_next(rr_iter)) {
|
||||
if (!netreq->response_opt) {
|
||||
_getdns_rr_iter rr_iter_storage, *rr_iter;
|
||||
for ( rr_iter = _getdns_rr_iter_init(&rr_iter_storage
|
||||
, netreq->response, netreq->response_len)
|
||||
; rr_iter
|
||||
; rr_iter = _getdns_rr_iter_next(rr_iter)) {
|
||||
|
||||
if (_getdns_rr_iter_section(rr_iter) != SECTION_ADDITIONAL)
|
||||
continue;
|
||||
if (_getdns_rr_iter_section(rr_iter) != SECTION_ADDITIONAL)
|
||||
continue;
|
||||
|
||||
if (gldns_read_uint16(rr_iter->rr_type) != GETDNS_RRTYPE_OPT)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
if (! rr_iter)
|
||||
return 0; /* No OPT, no cookie */
|
||||
|
||||
pos = rr_iter->rr_type + 8;
|
||||
if (gldns_read_uint16(rr_iter->rr_type) != GETDNS_RRTYPE_OPT)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (! rr_iter) {
|
||||
netreq->response_opt = NO_OPT_RR;
|
||||
return MATCH_OPT_NOT_FOUND;
|
||||
}
|
||||
pos = netreq->response_opt = rr_iter->rr_type + 8;
|
||||
|
||||
#if defined(STUB_DEBUG) && STUB_DEBUG
|
||||
char str_spc[8192], *str = str_spc;
|
||||
size_t str_len = sizeof(str_spc);
|
||||
uint8_t *data = (uint8_t *)rr_iter->pos;
|
||||
size_t data_len = rr_iter->nxt - rr_iter->pos;
|
||||
(void) gldns_wire2str_rr_scan(
|
||||
&data, &data_len, &str, &str_len, (uint8_t *)rr_iter->pkt, rr_iter->pkt_end - rr_iter->pkt, NULL);
|
||||
DEBUG_STUB("%s %-35s: OPT RR: %s",
|
||||
STUB_DEBUG_READ, __FUNC__, str_spc);
|
||||
char str_spc[8192], *str = str_spc;
|
||||
size_t str_len = sizeof(str_spc);
|
||||
uint8_t *data = (uint8_t *)rr_iter->pos;
|
||||
size_t data_len = rr_iter->nxt - rr_iter->pos;
|
||||
(void) gldns_wire2str_rr_scan(&data, &data_len, &str, &str_len,
|
||||
(uint8_t *)rr_iter->pkt, rr_iter->pkt_end - rr_iter->pkt,
|
||||
NULL);
|
||||
DEBUG_STUB("%s %-35s: OPT RR: %s",
|
||||
STUB_DEBUG_READ, __FUNC__, str_spc);
|
||||
#endif
|
||||
/* Check limits only the first time*/
|
||||
if (pos + 2 > rr_iter->nxt
|
||||
|| pos + 2 + gldns_read_uint16(pos) > rr_iter->nxt) {
|
||||
netreq->response_opt = NO_OPT_RR;
|
||||
return MATCH_OPT_ERROR;
|
||||
}
|
||||
} else if (netreq->response_opt == NO_OPT_RR)
|
||||
return MATCH_OPT_NOT_FOUND;
|
||||
else
|
||||
/* Reuse earlier found option */
|
||||
pos = netreq->response_opt;;
|
||||
|
||||
/* OPT found, now search for the specified option */
|
||||
if (pos + 2 > rr_iter->nxt)
|
||||
return 1; /* FORMERR */
|
||||
rdata_len = gldns_read_uint16(pos);
|
||||
pos += 2;
|
||||
rdata_end = pos + rdata_len;
|
||||
|
||||
rdata_len = gldns_read_uint16(pos); pos += 2;
|
||||
if (pos + rdata_len > rr_iter->nxt)
|
||||
return 1; /* FORMERR */
|
||||
|
||||
while (pos < rr_iter->nxt) {
|
||||
while (pos < rdata_end) {
|
||||
opt_code = gldns_read_uint16(pos); pos += 2;
|
||||
opt_len = gldns_read_uint16(pos); pos += 2;
|
||||
if (pos + opt_len > rr_iter->nxt)
|
||||
return 1; /* FORMERR */
|
||||
if (pos + opt_len > rdata_end)
|
||||
return MATCH_OPT_ERROR;
|
||||
if (opt_code == code)
|
||||
break;
|
||||
pos += opt_len; /* Skip unknown options */
|
||||
}
|
||||
if (pos >= rr_iter->nxt || opt_code != code)
|
||||
return 0; /* Everything OK, just no cookie found. */
|
||||
if (pos >= rdata_end || opt_code != code)
|
||||
return MATCH_OPT_NOT_FOUND;
|
||||
|
||||
*position = pos;
|
||||
*option_len = opt_len;
|
||||
return 2;
|
||||
return MATCH_OPT_FOUND;
|
||||
}
|
||||
|
||||
/* TODO: Test combinations of EDNS0 options*/
|
||||
static int
|
||||
match_and_process_server_cookie(
|
||||
getdns_upstream *upstream, uint8_t *response, size_t response_len)
|
||||
getdns_upstream *upstream, getdns_network_req *netreq)
|
||||
{
|
||||
const uint8_t *position = NULL;
|
||||
uint16_t option_len = 0;
|
||||
int found = match_edns_opt_rr(EDNS_COOKIE_OPCODE, response,
|
||||
response_len, &position, &option_len);
|
||||
if (found != 2)
|
||||
return found;
|
||||
int gai_r;
|
||||
int found = match_edns_opt_rr(EDNS_COOKIE_OPCODE, netreq,
|
||||
&position, &option_len);
|
||||
if (found == MATCH_OPT_ERROR) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_WARNING,
|
||||
"%-40s : Upstream : Error getting Cookie option.\n",
|
||||
upstream->addr_str);
|
||||
|
||||
if (option_len < 16 || option_len > 40)
|
||||
return 1; /* FORMERR */
|
||||
|
||||
if (!upstream->has_client_cookie)
|
||||
return 1; /* Cookie reply, but we didn't sent one */
|
||||
|
||||
if (memcmp(upstream->client_cookie, position, 8) != 0) {
|
||||
if (!upstream->has_prev_client_cookie)
|
||||
return 1; /* Cookie didn't match */
|
||||
if (memcmp(upstream->prev_client_cookie, position, 8) != 0)
|
||||
return 1; /* Previous cookie didn't match either */
|
||||
|
||||
upstream->has_server_cookie = 0;
|
||||
return 0; /* Don't store server cookie, because it
|
||||
* is for our previous client cookie
|
||||
*/
|
||||
return 1; /* Discard and wait for better response */
|
||||
}
|
||||
else if (found == MATCH_OPT_NOT_FOUND) {
|
||||
/* Option not found, server does not support cookies */
|
||||
if (upstream->server_cookie_len > 8) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR,
|
||||
"%-40s : Upstream : Server did not return "
|
||||
" DNS Cookie. Response discarded.\n",
|
||||
upstream->addr_str);
|
||||
|
||||
return 1; /* Discard response */
|
||||
|
||||
} else if (netreq->cookie_sent) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO,
|
||||
"%-40s : Upstream : Does not support DNS Cookies"
|
||||
". Retrying in one hour.\n", upstream->addr_str);
|
||||
|
||||
upstream->src_addr_checked = _getdns_get_now_ms();
|
||||
}
|
||||
return 0; /* Use response */
|
||||
}
|
||||
assert(found == MATCH_OPT_FOUND);
|
||||
|
||||
if (option_len < 16 || option_len > 40) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_WARNING,
|
||||
"%-40s : Upstream : "
|
||||
"Unsupported Server Cookie size: %d.\n",
|
||||
upstream->addr_str, (int)option_len);
|
||||
|
||||
return 1; /* Discard and wait for better response */
|
||||
}
|
||||
|
||||
if (upstream->server_cookie_len > 8) {
|
||||
if (memcmp(upstream->server_cookie, position, 8)) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_WARNING,
|
||||
"%-40s : Upstream : DNS Cookie did not match.\n",
|
||||
upstream->addr_str);
|
||||
|
||||
return 1; /* Discard and wait for better response */
|
||||
}
|
||||
/* Update server cookie */
|
||||
upstream->server_cookie_len = option_len;
|
||||
(void) memcpy(upstream->server_cookie, position, option_len);
|
||||
return 0; /* Use response */
|
||||
|
||||
} else if (!netreq->cookie_sent) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_WARNING,
|
||||
"%-40s : Upstream : "
|
||||
"DNS Cookie received but none sent.\n",upstream->addr_str);
|
||||
|
||||
return 1; /* Discard and wait for better response */
|
||||
}
|
||||
if (memcmp(netreq->client_cookie, position, 8)) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_WARNING,
|
||||
"%-40s : Upstream : DNS Cookie did not match.\n",
|
||||
upstream->addr_str);
|
||||
|
||||
return 1; /* Discard and wait for better response */
|
||||
}
|
||||
/* A new client cookie matched, store server cookie but only if we
|
||||
* can get the source IP address.
|
||||
*/
|
||||
upstream->src_addr_checked = _getdns_get_now_ms();
|
||||
|
||||
upstream->src_addr_len = sizeof(upstream->src_addr);
|
||||
if (getsockname(netreq->fd,(struct sockaddr*)
|
||||
&upstream->src_addr, &upstream->src_addr_len)) {
|
||||
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_WARNING,
|
||||
"%-40s : Upstream : Could not get source address: %s. "
|
||||
"Privacy aware DNS Cookies not supported.\n",
|
||||
upstream->addr_str, strerror(errno));
|
||||
|
||||
} else if ((gai_r = getnameinfo((struct sockaddr *)
|
||||
&upstream->src_addr, upstream->src_addr_len,
|
||||
upstream->src_addr_str, sizeof(upstream->src_addr_str),
|
||||
NULL, 0, NI_NUMERICHOST))) {
|
||||
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_WARNING,
|
||||
"%-40s : Upstream : Could not print source address: %s. "
|
||||
"Privacy aware DNS Cookies not supported.\n",
|
||||
upstream->addr_str, gai_strerror(gai_r));
|
||||
} else {
|
||||
switch (upstream->src_addr.ss_family) {
|
||||
case AF_INET:
|
||||
((struct sockaddr_in *)&upstream->src_addr)->sin_port = 0;
|
||||
break;
|
||||
case AF_INET6:
|
||||
((struct sockaddr_in6*)&upstream->src_addr)->sin6_port = 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG,
|
||||
"%-40s : Upstream : "
|
||||
"Registering new Server Cookie for source address: %s.\n",
|
||||
upstream->addr_str, upstream->src_addr_str);
|
||||
|
||||
upstream->server_cookie_len = option_len;
|
||||
(void) memcpy(upstream->server_cookie, position, option_len);
|
||||
return 0;
|
||||
}
|
||||
position += 8;
|
||||
option_len -= 8;
|
||||
upstream->has_server_cookie = 1;
|
||||
upstream->server_cookie_len = option_len;
|
||||
(void) memcpy(upstream->server_cookie, position, option_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
process_keepalive(
|
||||
getdns_upstream *upstream, getdns_network_req *netreq,
|
||||
uint8_t *response, size_t response_len)
|
||||
process_keepalive( getdns_upstream *upstream, getdns_network_req *netreq)
|
||||
{
|
||||
const uint8_t *position = NULL;
|
||||
uint16_t option_len = 0;
|
||||
int found = match_edns_opt_rr(GLDNS_EDNS_KEEPALIVE, response,
|
||||
response_len, &position, &option_len);
|
||||
int found = match_edns_opt_rr(GLDNS_EDNS_KEEPALIVE, netreq,
|
||||
&position, &option_len);
|
||||
if (found != 2 || option_len != 2) {
|
||||
if (netreq->keepalive_sent == 1) {
|
||||
/* For TCP if no keepalive sent back, then we must use 0 idle timeout
|
||||
|
@ -1342,6 +1417,7 @@ _getdns_get_time_as_uintt64() {
|
|||
/**************************/
|
||||
|
||||
|
||||
static void stub_udp_write_cb(void *userarg);
|
||||
static void
|
||||
stub_udp_read_cb(void *userarg)
|
||||
{
|
||||
|
@ -1387,14 +1463,15 @@ stub_udp_read_cb(void *userarg)
|
|||
if (GLDNS_ID_WIRE(netreq->response) != GLDNS_ID_WIRE(netreq->query))
|
||||
return; /* Cache poisoning attempt ;) */
|
||||
|
||||
if (netreq->owner->edns_cookies && match_and_process_server_cookie(
|
||||
upstream, netreq->response, read))
|
||||
return; /* Client cookie didn't match? */
|
||||
|
||||
if (netreq->owner->edns_cookies) {
|
||||
netreq->response_len = read;
|
||||
if (match_and_process_server_cookie(upstream, netreq)) {
|
||||
netreq->response_len = 0; /* 0 means error */
|
||||
return; /* Client cookie didn't match? */
|
||||
}
|
||||
netreq->response_len = 0; /* 0 means error */
|
||||
}
|
||||
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
||||
|
||||
_getdns_closesocket(netreq->fd);
|
||||
netreq->fd = -1;
|
||||
while (GLDNS_TC_WIRE(netreq->response)) {
|
||||
DEBUG_STUB("%s %-35s: MSG: %p TC bit set in response \n", STUB_DEBUG_READ,
|
||||
__FUNC__, (void*)netreq);
|
||||
|
@ -1405,6 +1482,9 @@ stub_udp_read_cb(void *userarg)
|
|||
if (next_transport != GETDNS_TRANSPORT_TCP &&
|
||||
next_transport != GETDNS_TRANSPORT_TLS)
|
||||
break;
|
||||
|
||||
_getdns_closesocket(netreq->fd);
|
||||
netreq->fd = -1;
|
||||
/* For now, special case where fallback should be on the same upstream*/
|
||||
if ((netreq->fd = upstream_connect(upstream, next_transport,
|
||||
dnsreq)) == -1)
|
||||
|
@ -1414,10 +1494,27 @@ stub_udp_read_cb(void *userarg)
|
|||
_getdns_ms_until_expiry(dnsreq->expires),
|
||||
getdns_eventloop_event_init(&netreq->event,
|
||||
netreq, NULL, NULL, stub_timeout_cb));
|
||||
|
||||
return;
|
||||
}
|
||||
netreq->response_len = read;
|
||||
if (netreq->owner->edns_cookies
|
||||
&& !netreq->badcookie_retry
|
||||
&& netreq->response_opt /* actually: assert(netreq->response_opt) */
|
||||
&& (netreq->response_opt[-4] << 4 | GLDNS_RCODE_WIRE(netreq->response))
|
||||
== GETDNS_RCODE_BADCOOKIE
|
||||
&& netreq->fd >= 0) {
|
||||
|
||||
/* Retry over UDP with the newly learned Cookie */
|
||||
netreq->badcookie_retry = 1;
|
||||
GETDNS_SCHEDULE_EVENT(dnsreq->loop, netreq->fd,
|
||||
_getdns_ms_until_expiry(dnsreq->expires),
|
||||
getdns_eventloop_event_init(&netreq->event, netreq,
|
||||
NULL, stub_udp_write_cb, stub_timeout_cb));
|
||||
return;
|
||||
}
|
||||
_getdns_closesocket(netreq->fd);
|
||||
netreq->fd = -1;
|
||||
|
||||
if (!dnsreq->context->round_robin_upstreams)
|
||||
dnsreq->upstreams->current_udp = 0;
|
||||
else {
|
||||
|
@ -1428,7 +1525,7 @@ stub_udp_read_cb(void *userarg)
|
|||
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
||||
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
|
||||
upstream->udp_responses++;
|
||||
upstream->back_off = 1;
|
||||
upstream->back_off = 1;
|
||||
if (upstream->udp_responses == 1 ||
|
||||
upstream->udp_responses % 100 == 0)
|
||||
_getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO,
|
||||
|
@ -1584,16 +1681,12 @@ upstream_read_cb(void *userarg)
|
|||
upstream->tcp.read_buf = NULL;
|
||||
upstream->responses_received++;
|
||||
|
||||
/* !THIS CODE NEEDS TESTING! */
|
||||
if (netreq->owner->edns_cookies &&
|
||||
match_and_process_server_cookie(
|
||||
netreq->upstream, upstream->tcp.read_buf,
|
||||
upstream->tcp.read_pos - upstream->tcp.read_buf))
|
||||
match_and_process_server_cookie(netreq->upstream, netreq))
|
||||
return; /* Client cookie didn't match (or FORMERR) */
|
||||
|
||||
if (netreq->owner->context->idle_timeout != 0)
|
||||
process_keepalive(netreq->upstream, netreq, netreq->response,
|
||||
netreq->response_len);
|
||||
process_keepalive(netreq->upstream, netreq);
|
||||
|
||||
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
||||
/* This also reschedules events for the upstream*/
|
||||
|
|
|
@ -233,8 +233,6 @@ typedef struct getdns_network_req
|
|||
int edns_maximum_udp_payload_size;
|
||||
uint16_t max_udp_payload_size;
|
||||
|
||||
size_t keepalive_sent;
|
||||
|
||||
/* Network requests scheduled to write after me */
|
||||
struct getdns_network_req *write_queue_tail;
|
||||
|
||||
|
@ -244,7 +242,13 @@ typedef struct getdns_network_req
|
|||
getdns_auth_state_t debug_tls_auth_status;
|
||||
getdns_bindata debug_tls_peer_cert;
|
||||
const char *debug_tls_version;
|
||||
size_t debug_udp;
|
||||
|
||||
/* Some booleans */
|
||||
unsigned debug_udp : 1;
|
||||
unsigned keepalive_sent : 1;
|
||||
unsigned badcookie_retry: 1;
|
||||
unsigned cookie_sent : 1;
|
||||
uint8_t client_cookie[8];
|
||||
|
||||
/* When more space is needed for the wire_data response than is
|
||||
* available in wire_data[], it will be allocated separately.
|
||||
|
@ -267,6 +271,7 @@ typedef struct getdns_network_req
|
|||
size_t base_query_option_sz;
|
||||
size_t response_len;
|
||||
uint8_t *response;
|
||||
const uint8_t *response_opt; /* offset of OPT RR in response */
|
||||
size_t wire_data_sz;
|
||||
uint8_t wire_data[];
|
||||
|
||||
|
|
|
@ -642,6 +642,12 @@ _getdns_create_reply_dict(getdns_context *context, getdns_network_req *req,
|
|||
rr_type == GETDNS_RRTYPE_RRSIG && rrsigs_in_answer)
|
||||
*rrsigs_in_answer = 1;
|
||||
|
||||
if (section == SECTION_ADDITIONAL &&
|
||||
rr_type == GETDNS_RRTYPE_OPT &&
|
||||
getdns_dict_set_int( result, "/header/extended_rcode"
|
||||
, (uint32_t)rr_iter->rr_type[4] << 4
|
||||
| GLDNS_RCODE_WIRE(req->response)))
|
||||
goto error;
|
||||
if (section != SECTION_ANSWER) {
|
||||
if (_getdns_list_append_this_dict(
|
||||
sections[section], rr_dict))
|
||||
|
|
|
@ -213,6 +213,17 @@ INLINE uint64_t _getdns_ms_until_expiry(uint64_t expires)
|
|||
return now_ms >= expires ? 0 : expires - now_ms;
|
||||
}
|
||||
|
||||
INLINE uint64_t _getdns_get_now_ms2(uint64_t *now_ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
if (!now_ms) return _getdns_get_now_ms();
|
||||
if (*now_ms) return *now_ms;
|
||||
|
||||
(void) gettimeofday(&tv, NULL);
|
||||
return (*now_ms = (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000);
|
||||
}
|
||||
|
||||
INLINE uint64_t _getdns_ms_until_expiry2(uint64_t expires, uint64_t *now_ms)
|
||||
{
|
||||
if (*now_ms == 0) *now_ms = _getdns_get_now_ms();
|
||||
|
|
Loading…
Reference in New Issue