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;
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) {
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) */
/* 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;
* 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;
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
/* 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 */
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);
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 */