diff --git a/testcode/unitmain.c b/testcode/unitmain.c index 79ce45f39..4bc756a07 100644 --- a/testcode/unitmain.c +++ b/testcode/unitmain.c @@ -1092,7 +1092,7 @@ static void edns_ede_encode_notxt_fit_test( struct query_info* qinfo, { struct edns_data edns; sldns_buffer* pkt; - uint16_t edns_field_size, ede_txt_size; + size_t edns_field_size, ede_txt_size; int found_ede = 0, found_ede_other = 0, found_ede_txt = 0; int found_other_edns = 0; edns_ede_encode_setup(&edns, region); @@ -1123,7 +1123,7 @@ static void edns_ede_encode_no_fit_test( struct query_info* qinfo, { struct edns_data edns; sldns_buffer* pkt; - uint16_t edns_field_size, ede_size, ede_txt_size; + size_t edns_field_size, ede_size, ede_txt_size; int found_ede = 0, found_ede_other = 0, found_ede_txt = 0; int found_other_edns = 0; edns_ede_encode_setup(&edns, region); diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 8f4639519..10979df9c 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -820,7 +820,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, return 1; } -uint16_t +size_t calc_edns_field_size(struct edns_data* edns) { size_t rdatalen = 0; @@ -856,7 +856,7 @@ calc_edns_option_size(struct edns_data* edns, uint16_t code) } uint16_t -calc_ede_option_size(struct edns_data* edns, uint16_t* txt_size) +calc_ede_option_size(struct edns_data* edns, size_t* txt_size) { size_t rdatalen = 0; struct edns_option* opt; @@ -958,6 +958,10 @@ attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns, padding_option = opt; continue; } + if(sldns_buffer_position(pkt) + opt->opt_len + 4 > max_msg_sz) + break; /* no space for it */ + if(!sldns_buffer_available(pkt, 4 + opt->opt_len)) + break; sldns_buffer_write_u16(pkt, opt->opt_code); sldns_buffer_write_u16(pkt, opt->opt_len); if(opt->opt_len != 0) @@ -968,12 +972,18 @@ attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns, padding_option = opt; continue; } + if(sldns_buffer_position(pkt) + opt->opt_len + 4 > max_msg_sz) + break; /* no space for it */ + if(!sldns_buffer_available(pkt, 4 + opt->opt_len)) + break; sldns_buffer_write_u16(pkt, opt->opt_code); sldns_buffer_write_u16(pkt, opt->opt_len); if(opt->opt_len != 0) sldns_buffer_write(pkt, opt->opt_data, opt->opt_len); } - if (padding_option && edns->padding_block_size ) { + if (padding_option && edns->padding_block_size && + sldns_buffer_position(pkt)+4 <= max_msg_sz && + sldns_buffer_available(pkt, 4) /* if there is space for it */) { size_t pad_pos = sldns_buffer_position(pkt); size_t msg_sz = ((pad_pos + 3) / edns->padding_block_size + 1) * edns->padding_block_size; @@ -1017,7 +1027,7 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, { uint16_t flags; unsigned int attach_edns = 0; - uint16_t edns_field_size, ede_size, ede_txt_size; + size_t edns_field_size, ede_size, ede_txt_size; if(!cached || rep->authoritative) { /* original flags, copy RD and CD bits from query. */ @@ -1044,12 +1054,12 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, * calculate sizes once here */ edns_field_size = calc_edns_field_size(edns); ede_size = calc_ede_option_size(edns, &ede_txt_size); - if(sldns_buffer_capacity(pkt) < udpsize) + if(sldns_buffer_capacity(pkt) < (size_t)udpsize) udpsize = sldns_buffer_capacity(pkt); if(!edns || !edns->edns_present) { attach_edns = 0; /* EDEs are optional, try to fit anything else before them */ - } else if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) { + } else if((size_t)udpsize < (size_t)LDNS_HEADER_SIZE + edns_field_size - ede_size) { /* packet too small to contain edns, omit it. */ attach_edns = 0; } else { @@ -1063,13 +1073,13 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, return 0; } if(attach_edns) { - if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size) + if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size) attach_edns_record_max_msg_sz(pkt, edns, udpsize); - else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_txt_size) { + else if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_txt_size) { ede_trim_text(&edns->opt_list_inplace_cb_out); ede_trim_text(&edns->opt_list_out); attach_edns_record_max_msg_sz(pkt, edns, udpsize); - } else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_size) { + } else if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_size) { edns_opt_list_remove(&edns->opt_list_inplace_cb_out, LDNS_EDNS_EDE); edns_opt_list_remove(&edns->opt_list_out, LDNS_EDNS_EDE); attach_edns_record_max_msg_sz(pkt, edns, udpsize); @@ -1132,7 +1142,7 @@ extended_error_encode(sldns_buffer* buf, uint16_t rcode, } sldns_buffer_flip(buf); if(edns && edns->edns_present) { - uint16_t edns_field_size, ede_size, ede_txt_size; + size_t edns_field_size, ede_size, ede_txt_size; struct edns_data es = *edns; es.edns_version = EDNS_ADVERTISED_VERSION; es.udp_size = EDNS_ADVERTISED_SIZE; @@ -1144,13 +1154,13 @@ extended_error_encode(sldns_buffer* buf, uint16_t rcode, * to see if EDNS can fit. */ edns_field_size = calc_edns_field_size(&es); ede_size = calc_ede_option_size(&es, &ede_txt_size); - if(edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size) + if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size) attach_edns_record_max_msg_sz(buf, &es, edns->udp_size); - else if(edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_txt_size) { + else if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_txt_size) { ede_trim_text(&es.opt_list_inplace_cb_out); ede_trim_text(&es.opt_list_out); attach_edns_record_max_msg_sz(buf, &es, edns->udp_size); - } else if(edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_size) { + } else if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_size) { edns_opt_list_remove(&es.opt_list_inplace_cb_out, LDNS_EDNS_EDE); edns_opt_list_remove(&es.opt_list_out, LDNS_EDNS_EDE); attach_edns_record_max_msg_sz(buf, &es, edns->udp_size); diff --git a/util/data/msgencode.h b/util/data/msgencode.h index 0363a90bf..f561fe1dd 100644 --- a/util/data/msgencode.h +++ b/util/data/msgencode.h @@ -106,7 +106,7 @@ void qinfo_query_encode(struct sldns_buffer* pkt, struct query_info* qinfo); * @param edns: edns data or NULL. * @return octets to reserve for EDNS. */ -uint16_t calc_edns_field_size(struct edns_data* edns); +size_t calc_edns_field_size(struct edns_data* edns); /** * Calculate the size of a specific EDNS option in packet. @@ -127,7 +127,7 @@ uint16_t calc_edns_option_size(struct edns_data* edns, uint16_t code); * extra text. * @return octets the option will take up. */ -uint16_t calc_ede_option_size(struct edns_data* edns, uint16_t* txt_size); +uint16_t calc_ede_option_size(struct edns_data* edns, size_t* txt_size); /** * Attach EDNS record to buffer. Buffer has complete packet. There must diff --git a/util/data/msgparse.c b/util/data/msgparse.c index afbcbca5b..a38bed62e 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -950,6 +950,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, struct comm_reply* repinfo, uint32_t now, struct regional* region, struct cookie_secrets* cookie_secrets) { + int nsid_seen = 0, cookie_seen = 0, padding_seen = 0; /* 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 @@ -984,8 +985,9 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, /* handle parse time edns options here */ switch(opt_code) { case LDNS_EDNS_NSID: - if (!cfg || !cfg->nsid) + if (!cfg || !cfg->nsid || nsid_seen) break; + nsid_seen = 1; if(!edns_opt_list_append(&edns->opt_list_out, LDNS_EDNS_NSID, cfg->nsid_len, cfg->nsid, region)) { @@ -1027,8 +1029,9 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, case LDNS_EDNS_PADDING: if(!cfg || !cfg->pad_responses || - !c || c->type != comm_tcp ||!c->ssl) + !c || c->type != comm_tcp ||!c->ssl || padding_seen) break; + padding_seen = 1; if(!edns_opt_list_append(&edns->opt_list_out, LDNS_EDNS_PADDING, 0, NULL, region)) { @@ -1039,8 +1042,9 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, break; case LDNS_EDNS_COOKIE: - if(!cfg || !cfg->do_answer_cookie || !repinfo) + if(!cfg || !cfg->do_answer_cookie || !repinfo || cookie_seen) break; + cookie_seen = 1; if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) { verbose(VERB_ALGO, "worker request: " "badly formatted cookie");