Response to BADCOOKIE extended rcode

This commit is contained in:
Willem Toorop 2020-04-08 16:08:56 +02:00
parent 563b2b113a
commit 8b62970e0c
7 changed files with 135 additions and 92 deletions

View File

@ -255,7 +255,7 @@ static struct const_name_info consts_name_info[] = {
{ "GETDNS_RCODE_BADTIME", 18 }, { "GETDNS_RCODE_BADTIME", 18 },
{ "GETDNS_RCODE_BADTRUNC", 22 }, { "GETDNS_RCODE_BADTRUNC", 22 },
{ "GETDNS_RCODE_BADVERS", 16 }, { "GETDNS_RCODE_BADVERS", 16 },
{ "GETDNS_RCODE_COOKIE", 23 }, { "GETDNS_RCODE_BADCOOKIE", 23 },
{ "GETDNS_RCODE_FORMERR", 1 }, { "GETDNS_RCODE_FORMERR", 1 },
{ "GETDNS_RCODE_NOERROR", 0 }, { "GETDNS_RCODE_NOERROR", 0 },
{ "GETDNS_RCODE_NOTAUTH", 9 }, { "GETDNS_RCODE_NOTAUTH", 9 },

View File

@ -1036,21 +1036,21 @@ static int
_getdns_print_rcode(gldns_buffer *buf, uint32_t rcode) _getdns_print_rcode(gldns_buffer *buf, uint32_t rcode)
{ {
static const char *rcodes[] = { static const char *rcodes[] = {
" GETDNS_RCODE_NOERROR" , " GETDNS_RCODE_FORMERR" , " GETDNS_RCODE_NOERROR" , " GETDNS_RCODE_FORMERR" ,
" GETDNS_RCODE_SERVFAIL", " GETDNS_RCODE_NXDOMAIN", " GETDNS_RCODE_SERVFAIL", " GETDNS_RCODE_NXDOMAIN" ,
" GETDNS_RCODE_NOTIMP" , " GETDNS_RCODE_REFUSED" , " GETDNS_RCODE_NOTIMP" , " GETDNS_RCODE_REFUSED" ,
" GETDNS_RCODE_YXDOMAIN", " GETDNS_RCODE_YXRRSET" , " GETDNS_RCODE_YXDOMAIN", " GETDNS_RCODE_YXRRSET" ,
" GETDNS_RCODE_NXRRSET" , " GETDNS_RCODE_NOTAUTH" , " GETDNS_RCODE_NXRRSET" , " GETDNS_RCODE_NOTAUTH" ,
" GETDNS_RCODE_NOTZONE" , " GETDNS_RCODE_NOTZONE" ,
" GETDNS_RCODE_BADSIG" , " GETDNS_RCODE_BADKEY" , " GETDNS_RCODE_BADSIG" , " GETDNS_RCODE_BADKEY" ,
" GETDNS_RCODE_BADTIME" , " GETDNS_RCODE_BADMODE" , " GETDNS_RCODE_BADTIME" , " GETDNS_RCODE_BADMODE" ,
" GETDNS_RCODE_BADNAME" , " GETDNS_RCODE_BADALG" , " GETDNS_RCODE_BADNAME" , " GETDNS_RCODE_BADALG" ,
" GETDNS_RCODE_BADTRUNC" " GETDNS_RCODE_BADTRUNC", " GETDNS_RCODE_BADCOOKIE"
}; };
if (rcode <= 10) if (rcode <= 10)
(void) gldns_buffer_printf(buf, "%s", rcodes[rcode]); (void) gldns_buffer_printf(buf, "%s", rcodes[rcode]);
else if (rcode >= 16 && rcode <= 22) else if (rcode >= 16 && rcode <= 23)
(void) gldns_buffer_printf(buf, "%s", rcodes[rcode-6]); (void) gldns_buffer_printf(buf, "%s", rcodes[rcode-5]);
else else
return 0; return 0;
return 1; return 1;
@ -1156,6 +1156,11 @@ getdns_pp_dict(gldns_buffer * buf, size_t indent,
if (!json && strcmp(item->node.key, "rcode") == 0 && if (!json && strcmp(item->node.key, "rcode") == 0 &&
_getdns_print_rcode(buf, item->i.data.n)) _getdns_print_rcode(buf, item->i.data.n))
break; 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( if (gldns_buffer_printf(
buf,(json < 2 ? " %d" : "%d"), item->i.data.n) < 0) buf,(json < 2 ? " %d" : "%d"), item->i.data.n) < 0)
return -1; return -1;

View File

@ -473,26 +473,26 @@ typedef enum getdns_callback_type_t {
* \defgroup rcodes Rcodes * \defgroup rcodes Rcodes
* @{ * @{
*/ */
#define GETDNS_RCODE_NOERROR 0 #define GETDNS_RCODE_NOERROR 0
#define GETDNS_RCODE_FORMERR 1 #define GETDNS_RCODE_FORMERR 1
#define GETDNS_RCODE_SERVFAIL 2 #define GETDNS_RCODE_SERVFAIL 2
#define GETDNS_RCODE_NXDOMAIN 3 #define GETDNS_RCODE_NXDOMAIN 3
#define GETDNS_RCODE_NOTIMP 4 #define GETDNS_RCODE_NOTIMP 4
#define GETDNS_RCODE_REFUSED 5 #define GETDNS_RCODE_REFUSED 5
#define GETDNS_RCODE_YXDOMAIN 6 #define GETDNS_RCODE_YXDOMAIN 6
#define GETDNS_RCODE_YXRRSET 7 #define GETDNS_RCODE_YXRRSET 7
#define GETDNS_RCODE_NXRRSET 8 #define GETDNS_RCODE_NXRRSET 8
#define GETDNS_RCODE_NOTAUTH 9 #define GETDNS_RCODE_NOTAUTH 9
#define GETDNS_RCODE_NOTZONE 10 #define GETDNS_RCODE_NOTZONE 10
#define GETDNS_RCODE_BADVERS 16 #define GETDNS_RCODE_BADVERS 16
#define GETDNS_RCODE_BADSIG 16 #define GETDNS_RCODE_BADSIG 16
#define GETDNS_RCODE_BADKEY 17 #define GETDNS_RCODE_BADKEY 17
#define GETDNS_RCODE_BADTIME 18 #define GETDNS_RCODE_BADTIME 18
#define GETDNS_RCODE_BADMODE 19 #define GETDNS_RCODE_BADMODE 19
#define GETDNS_RCODE_BADNAME 20 #define GETDNS_RCODE_BADNAME 20
#define GETDNS_RCODE_BADALG 21 #define GETDNS_RCODE_BADALG 21
#define GETDNS_RCODE_BADTRUNC 22 #define GETDNS_RCODE_BADTRUNC 22
#define GETDNS_RCODE_COOKIE 23 #define GETDNS_RCODE_BADCOOKIE 23
/** @} /** @}
*/ */

View File

@ -147,6 +147,7 @@ netreq_reset(getdns_network_req *net_req)
net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE; net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE; net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->response_len = 0; net_req->response_len = 0;
net_req->response_opt = NULL;
/* Some fields to record info for return_call_reporting */ /* Some fields to record info for return_call_reporting */
net_req->debug_start_time = 0; net_req->debug_start_time = 0;
net_req->debug_end_time = 0; net_req->debug_end_time = 0;
@ -308,6 +309,7 @@ _getdns_network_req_clear_upstream_options(getdns_network_req * req)
req->response = req->opt + 11 + req->base_query_option_sz; req->response = req->opt + 11 + req->base_query_option_sz;
pktlen = req->response - req->query; pktlen = req->response - req->query;
gldns_write_uint16(req->query - 2, (uint16_t) pktlen); gldns_write_uint16(req->query - 2, (uint16_t) pktlen);
req->response_opt = NULL;
} }
} }

View File

@ -210,62 +210,73 @@ attach_edns_cookie(getdns_network_req *req)
* and 1 on FORMERR * and 1 on FORMERR
*/ */
static int 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) const uint8_t **position, uint16_t *option_len)
{ {
_getdns_rr_iter rr_iter_storage, *rr_iter; const uint8_t *pos, *rdata_end;
const uint8_t *pos;
uint16_t rdata_len, opt_code = 0, opt_len = 0; 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) */ /* Search for the OPT RR (if any) */
for ( rr_iter = _getdns_rr_iter_init(&rr_iter_storage if (!netreq->response_opt) {
, response, response_len) _getdns_rr_iter rr_iter_storage, *rr_iter;
; rr_iter for ( rr_iter = _getdns_rr_iter_init(&rr_iter_storage
; rr_iter = _getdns_rr_iter_next(rr_iter)) { , netreq->response, netreq->response_len)
; rr_iter
; rr_iter = _getdns_rr_iter_next(rr_iter)) {
if (_getdns_rr_iter_section(rr_iter) != SECTION_ADDITIONAL) if (_getdns_rr_iter_section(rr_iter) != SECTION_ADDITIONAL)
continue; continue;
if (gldns_read_uint16(rr_iter->rr_type) != GETDNS_RRTYPE_OPT) if (gldns_read_uint16(rr_iter->rr_type) != GETDNS_RRTYPE_OPT)
continue; continue;
break;
break; }
} if (! rr_iter) {
if (! rr_iter) netreq->response_opt = NO_OPT_RR;
return 0; /* No OPT, no cookie */ return 0; /* No OPT, no options cookie */
}
pos = rr_iter->rr_type + 8; pos = netreq->response_opt = rr_iter->rr_type + 8;
#if defined(STUB_DEBUG) && STUB_DEBUG #if defined(STUB_DEBUG) && STUB_DEBUG
char str_spc[8192], *str = str_spc; char str_spc[8192], *str = str_spc;
size_t str_len = sizeof(str_spc); size_t str_len = sizeof(str_spc);
uint8_t *data = (uint8_t *)rr_iter->pos; uint8_t *data = (uint8_t *)rr_iter->pos;
size_t data_len = rr_iter->nxt - rr_iter->pos; size_t data_len = rr_iter->nxt - rr_iter->pos;
(void) gldns_wire2str_rr_scan( (void) gldns_wire2str_rr_scan(&data, &data_len, &str, &str_len,
&data, &data_len, &str, &str_len, (uint8_t *)rr_iter->pkt, rr_iter->pkt_end - rr_iter->pkt, NULL); (uint8_t *)rr_iter->pkt, rr_iter->pkt_end - rr_iter->pkt,
DEBUG_STUB("%s %-35s: OPT RR: %s", NULL);
STUB_DEBUG_READ, __FUNC__, str_spc); DEBUG_STUB("%s %-35s: OPT RR: %s",
STUB_DEBUG_READ, __FUNC__, str_spc);
#endif #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 1; /* FORMERR */
}
} else if (netreq->response_opt == NO_OPT_RR)
return 0; /* No OPT, no options */
else
/* Reuse earlier found option */
pos = netreq->response_opt;;
/* OPT found, now search for the specified option */ rdata_len = gldns_read_uint16(pos);
if (pos + 2 > rr_iter->nxt) pos += 2;
return 1; /* FORMERR */ rdata_end = pos + rdata_len;
rdata_len = gldns_read_uint16(pos); pos += 2; while (pos < rdata_end) {
if (pos + rdata_len > rr_iter->nxt)
return 1; /* FORMERR */
while (pos < rr_iter->nxt) {
opt_code = gldns_read_uint16(pos); pos += 2; opt_code = gldns_read_uint16(pos); pos += 2;
opt_len = gldns_read_uint16(pos); pos += 2; opt_len = gldns_read_uint16(pos); pos += 2;
if (pos + opt_len > rr_iter->nxt) if (pos + opt_len > rdata_end)
return 1; /* FORMERR */ return 1; /* FORMERR */
if (opt_code == code) if (opt_code == code)
break; break;
pos += opt_len; /* Skip unknown options */ pos += opt_len; /* Skip unknown options */
} }
if (pos >= rr_iter->nxt || opt_code != code) if (pos >= rdata_end || opt_code != code)
return 0; /* Everything OK, just no cookie found. */ return 0; /* Everything OK,
* the searched for option was just not found. */
*position = pos; *position = pos;
*option_len = opt_len; *option_len = opt_len;
return 2; return 2;
@ -274,12 +285,12 @@ match_edns_opt_rr(uint16_t code, uint8_t *response, size_t response_len,
/* TODO: Test combinations of EDNS0 options*/ /* TODO: Test combinations of EDNS0 options*/
static int static int
match_and_process_server_cookie( 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; const uint8_t *position = NULL;
uint16_t option_len = 0; uint16_t option_len = 0;
int found = match_edns_opt_rr(EDNS_COOKIE_OPCODE, response, int found = match_edns_opt_rr(EDNS_COOKIE_OPCODE, netreq,
response_len, &position, &option_len); &position, &option_len);
if (found != 2) if (found != 2)
return found; return found;
@ -309,14 +320,12 @@ match_and_process_server_cookie(
} }
static void static void
process_keepalive( process_keepalive( getdns_upstream *upstream, getdns_network_req *netreq)
getdns_upstream *upstream, getdns_network_req *netreq,
uint8_t *response, size_t response_len)
{ {
const uint8_t *position = NULL; const uint8_t *position = NULL;
uint16_t option_len = 0; uint16_t option_len = 0;
int found = match_edns_opt_rr(GLDNS_EDNS_KEEPALIVE, response, int found = match_edns_opt_rr(GLDNS_EDNS_KEEPALIVE, netreq,
response_len, &position, &option_len); &position, &option_len);
if (found != 2 || option_len != 2) { if (found != 2 || option_len != 2) {
if (netreq->keepalive_sent == 1) { if (netreq->keepalive_sent == 1) {
/* For TCP if no keepalive sent back, then we must use 0 idle timeout /* For TCP if no keepalive sent back, then we must use 0 idle timeout
@ -1342,12 +1351,14 @@ _getdns_get_time_as_uintt64() {
/**************************/ /**************************/
static void stub_udp_write_cb(void *userarg);
static void static void
stub_udp_read_cb(void *userarg) stub_udp_read_cb(void *userarg)
{ {
getdns_network_req *netreq = (getdns_network_req *)userarg; getdns_network_req *netreq = (getdns_network_req *)userarg;
getdns_dns_req *dnsreq = netreq->owner; getdns_dns_req *dnsreq = netreq->owner;
getdns_upstream *upstream = netreq->upstream; getdns_upstream *upstream = netreq->upstream;
int prev_server_cookie = 0;
ssize_t read; ssize_t read;
DEBUG_STUB("%s %-35s: MSG: %p \n", STUB_DEBUG_READ, DEBUG_STUB("%s %-35s: MSG: %p \n", STUB_DEBUG_READ,
__FUNC__, (void*)netreq); __FUNC__, (void*)netreq);
@ -1387,14 +1398,16 @@ stub_udp_read_cb(void *userarg)
if (GLDNS_ID_WIRE(netreq->response) != GLDNS_ID_WIRE(netreq->query)) if (GLDNS_ID_WIRE(netreq->response) != GLDNS_ID_WIRE(netreq->query))
return; /* Cache poisoning attempt ;) */ return; /* Cache poisoning attempt ;) */
if (netreq->owner->edns_cookies && match_and_process_server_cookie( if (netreq->owner->edns_cookies) {
upstream, netreq->response, read)) prev_server_cookie = upstream->has_server_cookie;
return; /* Client cookie didn't match? */ 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_CLEAR_EVENT(dnsreq->loop, &netreq->event);
_getdns_closesocket(netreq->fd);
netreq->fd = -1;
while (GLDNS_TC_WIRE(netreq->response)) { while (GLDNS_TC_WIRE(netreq->response)) {
DEBUG_STUB("%s %-35s: MSG: %p TC bit set in response \n", STUB_DEBUG_READ, DEBUG_STUB("%s %-35s: MSG: %p TC bit set in response \n", STUB_DEBUG_READ,
__FUNC__, (void*)netreq); __FUNC__, (void*)netreq);
@ -1405,6 +1418,9 @@ stub_udp_read_cb(void *userarg)
if (next_transport != GETDNS_TRANSPORT_TCP && if (next_transport != GETDNS_TRANSPORT_TCP &&
next_transport != GETDNS_TRANSPORT_TLS) next_transport != GETDNS_TRANSPORT_TLS)
break; break;
_getdns_closesocket(netreq->fd);
netreq->fd = -1;
/* For now, special case where fallback should be on the same upstream*/ /* For now, special case where fallback should be on the same upstream*/
if ((netreq->fd = upstream_connect(upstream, next_transport, if ((netreq->fd = upstream_connect(upstream, next_transport,
dnsreq)) == -1) dnsreq)) == -1)
@ -1414,10 +1430,27 @@ stub_udp_read_cb(void *userarg)
_getdns_ms_until_expiry(dnsreq->expires), _getdns_ms_until_expiry(dnsreq->expires),
getdns_eventloop_event_init(&netreq->event, getdns_eventloop_event_init(&netreq->event,
netreq, NULL, NULL, stub_timeout_cb)); netreq, NULL, NULL, stub_timeout_cb));
return; return;
} }
netreq->response_len = read; netreq->response_len = read;
if (netreq->owner->edns_cookies
&& !prev_server_cookie
&& upstream->has_server_cookie /* newly learned server cookie */
&& 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 */
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) if (!dnsreq->context->round_robin_upstreams)
dnsreq->upstreams->current_udp = 0; dnsreq->upstreams->current_udp = 0;
else { else {
@ -1428,7 +1461,7 @@ stub_udp_read_cb(void *userarg)
netreq->debug_end_time = _getdns_get_time_as_uintt64(); netreq->debug_end_time = _getdns_get_time_as_uintt64();
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED); _getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
upstream->udp_responses++; upstream->udp_responses++;
upstream->back_off = 1; upstream->back_off = 1;
if (upstream->udp_responses == 1 || if (upstream->udp_responses == 1 ||
upstream->udp_responses % 100 == 0) upstream->udp_responses % 100 == 0)
_getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO, _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO,
@ -1584,16 +1617,12 @@ upstream_read_cb(void *userarg)
upstream->tcp.read_buf = NULL; upstream->tcp.read_buf = NULL;
upstream->responses_received++; upstream->responses_received++;
/* !THIS CODE NEEDS TESTING! */
if (netreq->owner->edns_cookies && if (netreq->owner->edns_cookies &&
match_and_process_server_cookie( match_and_process_server_cookie(netreq->upstream, netreq))
netreq->upstream, upstream->tcp.read_buf,
upstream->tcp.read_pos - upstream->tcp.read_buf))
return; /* Client cookie didn't match (or FORMERR) */ return; /* Client cookie didn't match (or FORMERR) */
if (netreq->owner->context->idle_timeout != 0) if (netreq->owner->context->idle_timeout != 0)
process_keepalive(netreq->upstream, netreq, netreq->response, process_keepalive(netreq->upstream, netreq);
netreq->response_len);
netreq->debug_end_time = _getdns_get_time_as_uintt64(); netreq->debug_end_time = _getdns_get_time_as_uintt64();
/* This also reschedules events for the upstream*/ /* This also reschedules events for the upstream*/

View File

@ -267,6 +267,7 @@ typedef struct getdns_network_req
size_t base_query_option_sz; size_t base_query_option_sz;
size_t response_len; size_t response_len;
uint8_t *response; uint8_t *response;
const uint8_t *response_opt; /* offset of OPT RR in response */
size_t wire_data_sz; size_t wire_data_sz;
uint8_t wire_data[]; uint8_t wire_data[];

View File

@ -642,6 +642,12 @@ _getdns_create_reply_dict(getdns_context *context, getdns_network_req *req,
rr_type == GETDNS_RRTYPE_RRSIG && rrsigs_in_answer) rr_type == GETDNS_RRTYPE_RRSIG && rrsigs_in_answer)
*rrsigs_in_answer = 1; *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 (section != SECTION_ANSWER) {
if (_getdns_list_append_this_dict( if (_getdns_list_append_this_dict(
sections[section], rr_dict)) sections[section], rr_dict))