From cb48d9e4a1a834419ce366a7b4ead167b8fb8724 Mon Sep 17 00:00:00 2001 From: Tom Carpay Date: Mon, 1 Nov 2021 15:01:07 +0000 Subject: [PATCH] Fix keepalive logic --- daemon/worker.c | 24 ---------------- util/data/msgparse.c | 66 ++++++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index 67b2e3166..6a13dbe29 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1258,7 +1258,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error, goto send_reply; } if(edns.edns_present) { - struct edns_option* edns_opt; if(edns.edns_version != 0) { edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4); edns.edns_version = EDNS_ADVERTISED_VERSION; @@ -1286,29 +1285,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error, log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen); edns.udp_size = NORMAL_UDP_SIZE; } - if(c->type != comm_udp) { - /* @TODO reuse what we found at parse time */ - edns_opt = edns_opt_list_find(edns.opt_list_in, LDNS_EDNS_KEEPALIVE); - if(edns_opt && edns_opt->opt_len > 0) { - edns.ext_rcode = 0; - edns.edns_version = EDNS_ADVERTISED_VERSION; - edns.udp_size = EDNS_ADVERTISED_SIZE; - edns.bits &= EDNS_DO; - edns.opt_list_in = NULL; - edns.opt_list_out = NULL; - edns.opt_list_modules_out = NULL; - verbose(VERB_ALGO, "query with bad edns keepalive."); - log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen); - error_encode(c->buffer, LDNS_RCODE_FORMERR, &qinfo, - *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), - sldns_buffer_read_u16_at(c->buffer, 2), NULL); - if(sldns_buffer_capacity(c->buffer) >= - sldns_buffer_limit(c->buffer)+calc_edns_field_size(&edns)) - attach_edns_record(c->buffer, &edns); - regional_free_all(worker->scratchpad); - goto send_reply; - } - } } if(edns.udp_size > worker->daemon->cfg->max_udp_size && c->type == comm_udp) { diff --git a/util/data/msgparse.c b/util/data/msgparse.c index 1fc7f2d55..35f58c2fe 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -940,14 +940,36 @@ parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region) return 0; } +static int +edns_opt_list_append_keepalive(struct edns_option** list, int msec, + struct regional* region) +{ + uint8_t data[2]; /* For keepalive value */ + data[0] = (uint8_t)((msec >> 8) & 0xff); + data[1] = (uint8_t)(msec & 0xff); + return edns_opt_list_append(list, LDNS_EDNS_KEEPALIVE, sizeof(data), + data, region); +} + /** parse EDNS options from EDNS wireformat rdata */ static int parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len, struct edns_data* edns, struct config_file* cfg, struct comm_point* c, struct regional* region) { - int keepalive; - uint8_t data[2]; /* For keepalive value */ + /* To respond with a Keepalive option, the client connection must have + * received one message with a TCP Keepalive EDNS option, and that + * option must have 0 length data. Subsequent messages sent on that + * connection will have a TCP Keepalive option. + */ + if (cfg && cfg->do_tcp_keepalive && c && c->type != comm_udp && c->tcp_keepalive) { + if(!edns_opt_list_append_keepalive(&edns->opt_list_out, + c->tcp_timeout_msec / 100, region)) { + log_err("out of memory"); + return LDNS_RCODE_SERVFAIL; + } + c->tcp_keepalive = 1; + } /* while still more options, and have code+len to read */ /* ignores partial content (i.e. rdata len 3) */ @@ -962,13 +984,13 @@ parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len, /* handle parse time edns options here */ switch(opt_code) { case LDNS_EDNS_NSID: - if (!cfg->nsid) + if (!cfg || !cfg->nsid) break; if(!edns_opt_list_append(&edns->opt_list_out, LDNS_EDNS_NSID, cfg->nsid_len, cfg->nsid, region)) { log_err("out of memory"); - return 0; + return LDNS_RCODE_SERVFAIL; } break; @@ -979,29 +1001,31 @@ parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len, * length data. Subsequent messages sent on that * connection will have a TCP Keepalive option. */ - if (!cfg->do_tcp_keepalive || c->type != comm_udp || - !c->tcp_keepalive) + if (!cfg || !cfg->do_tcp_keepalive || !c || + c->type == comm_udp || c->tcp_keepalive) break; - keepalive = c->tcp_timeout_msec / 100; - data[0] = (uint8_t)((keepalive >> 8) & 0xff); - data[1] = (uint8_t)(keepalive & 0xff); - if(!edns_opt_list_append(&edns->opt_list_out, - LDNS_EDNS_KEEPALIVE, - sizeof(data), data, region)) { + if(opt_len) { + verbose(VERB_ALGO, "query with bad edns keepalive."); + return LDNS_RCODE_FORMERR; + } + if(!edns_opt_list_append_keepalive(&edns->opt_list_out, + c->tcp_timeout_msec / 100, + region)) { log_err("out of memory"); - return 0; + return LDNS_RCODE_SERVFAIL; } c->tcp_keepalive = 1; break; case LDNS_EDNS_PADDING: - if(!cfg->pad_responses || c->type != comm_tcp ||!c->ssl) + if(!cfg || !cfg->pad_responses || + !c || c->type != comm_tcp ||!c->ssl) break; if(!edns_opt_list_append(&edns->opt_list_out, LDNS_EDNS_PADDING, 0, NULL, region)) { log_err("out of memory"); - return 0; + return LDNS_RCODE_SERVFAIL; } edns->padding_block_size = cfg->pad_responses_block_size; break; @@ -1010,14 +1034,14 @@ parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len, if(!edns_opt_list_append(&edns->opt_list_in, opt_code, opt_len, rdata_ptr, region)) { log_err("out of memory"); - return 0; + return LDNS_RCODE_SERVFAIL; } break; } rdata_ptr += opt_len; rdata_len -= opt_len; } - return 1; + return 0; } int @@ -1084,7 +1108,7 @@ parse_extract_edns(struct msg_parse* msg, struct edns_data* edns, /* take the options */ rdata_len = found->rr_first->size-2; rdata_ptr = found->rr_first->ttl_data+6; - if(!parse_edns_options(rdata_ptr, rdata_len, edns, NULL, NULL, region)) + if(parse_edns_options(rdata_ptr, rdata_len, edns, NULL, NULL, region)) return 0; /* ignore rrsigs */ @@ -1122,6 +1146,7 @@ int parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns, struct config_file* cfg, struct comm_point* c, struct regional* region) { + int rcode; size_t rdata_len; uint8_t* rdata_ptr; log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1); @@ -1162,8 +1187,9 @@ parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns, if(sldns_buffer_remaining(pkt) < rdata_len) return LDNS_RCODE_FORMERR; rdata_ptr = sldns_buffer_current(pkt); - if(!parse_edns_options(rdata_ptr, rdata_len, edns, cfg, c, region)) - return LDNS_RCODE_SERVFAIL; + rcode = parse_edns_options(rdata_ptr, rdata_len, edns, cfg, c, region); + if(rcode) + return rcode; /* ignore rrsigs */