diff options
Diffstat (limited to 'util/data')
| -rw-r--r-- | util/data/msgencode.c | 20 | ||||
| -rw-r--r-- | util/data/msgparse.c | 150 | ||||
| -rw-r--r-- | util/data/msgparse.h | 23 | ||||
| -rw-r--r-- | util/data/msgreply.c | 67 | ||||
| -rw-r--r-- | util/data/msgreply.h | 27 | ||||
| -rw-r--r-- | util/data/packed_rrset.h | 8 |
6 files changed, 219 insertions, 76 deletions
diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 5f297b551bfb..fe21cfb86bd1 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -796,7 +796,10 @@ calc_edns_field_size(struct edns_data* edns) struct edns_option* opt; if(!edns || !edns->edns_present) return 0; - for(opt = edns->opt_list; opt; opt = opt->next) { + for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) { + rdatalen += 4 + opt->opt_len; + } + for(opt = edns->opt_list_out; opt; opt = opt->next) { rdatalen += 4 + opt->opt_len; } /* domain root '.' + type + class + ttl + rdatalen */ @@ -827,7 +830,17 @@ attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns, rdatapos = sldns_buffer_position(pkt); sldns_buffer_write_u16(pkt, 0); /* rdatalen */ /* write rdata */ - for(opt=edns->opt_list; opt; opt=opt->next) { + for(opt=edns->opt_list_inplace_cb_out; opt; opt=opt->next) { + if (opt->opt_code == LDNS_EDNS_PADDING) { + padding_option = opt; + continue; + } + 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); + } + for(opt=edns->opt_list_out; opt; opt=opt->next) { if (opt->opt_code == LDNS_EDNS_PADDING) { padding_option = opt; continue; @@ -860,8 +873,7 @@ attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns, sldns_buffer_skip(pkt, pad_sz); } } - if(edns->opt_list) - sldns_buffer_write_u16_at(pkt, rdatapos, + sldns_buffer_write_u16_at(pkt, rdatapos, sldns_buffer_position(pkt)-rdatapos-2); sldns_buffer_flip(pkt); } diff --git a/util/data/msgparse.c b/util/data/msgparse.c index 6ee5559db07b..a600a8c60151 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -37,10 +37,12 @@ * Routines for message parsing a packet buffer to a descriptive structure. */ #include "config.h" +#include "util/config_file.h" #include "util/data/msgparse.h" #include "util/data/msgreply.h" #include "util/data/dname.h" #include "util/data/packed_rrset.h" +#include "util/netevent.h" #include "util/storage/lookup3.h" #include "util/regional.h" #include "sldns/rrdef.h" @@ -938,11 +940,40 @@ 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 regional* region) +parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, + struct edns_data* edns, struct config_file* cfg, struct comm_point* c, + struct regional* region) { + /* 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. + * + * In the if-statement below, the option is added unsolicited. This + * means that the client has sent an KEEPALIVE option earlier. We know + * here this is true, because c->tcp_keepalive is set. + */ + 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; + } + } + /* while still more options, and have code+len to read */ /* ignores partial content (i.e. rdata len 3) */ while(rdata_len >= 4) { @@ -952,20 +983,81 @@ parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len, rdata_len -= 4; if(opt_len > rdata_len) break; /* option code partial */ - if(!edns_opt_append(edns, region, opt_code, opt_len, - rdata_ptr)) { + + /* handle parse time edns options here */ + switch(opt_code) { + case LDNS_EDNS_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 LDNS_RCODE_SERVFAIL; + } + break; + + case LDNS_EDNS_KEEPALIVE: + /* 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. + * + * This should be the first time the client sends this + * option, so c->tcp_keepalive is not set. + * Besides adding the reply KEEPALIVE option, + * c->tcp_keepalive will be set so that the + * option will be added unsolicited in subsequent + * responses (see the comment above the if-statement + * at the start of this function). + */ + if (!cfg || !cfg->do_tcp_keepalive || !c || + c->type == comm_udp || c->tcp_keepalive) + break; + 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 LDNS_RCODE_SERVFAIL; + } + c->tcp_keepalive = 1; + break; + + case LDNS_EDNS_PADDING: + 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 LDNS_RCODE_SERVFAIL; + } + edns->padding_block_size = cfg->pad_responses_block_size; + break; + + default: + 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; } rdata_ptr += opt_len; rdata_len -= opt_len; } - return 1; + return LDNS_RCODE_NOERROR; } int -parse_extract_edns(struct msg_parse* msg, struct edns_data* edns, - struct regional* region) +parse_extract_edns_from_response_msg(struct msg_parse* msg, + struct edns_data* edns, struct regional* region) { struct rrset_parse* rrset = msg->rrset_first; struct rrset_parse* prev = 0; @@ -1019,18 +1111,35 @@ parse_extract_edns(struct msg_parse* msg, struct edns_data* edns, edns->edns_version = found->rr_last->ttl_data[1]; edns->bits = sldns_read_uint16(&found->rr_last->ttl_data[2]); edns->udp_size = ntohs(found->rrset_class); - edns->opt_list = NULL; + edns->opt_list_in = NULL; + edns->opt_list_out = NULL; + edns->opt_list_inplace_cb_out = NULL; edns->padding_block_size = 0; /* 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, region)) - return 0; - /* ignore rrsigs */ + /* while still more options, and have code+len to read */ + /* ignores partial content (i.e. rdata len 3) */ + while(rdata_len >= 4) { + uint16_t opt_code = sldns_read_uint16(rdata_ptr); + uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); + rdata_ptr += 4; + rdata_len -= 4; + if(opt_len > rdata_len) + break; /* option code partial */ - return 0; + if(!edns_opt_list_append(&edns->opt_list_in, + opt_code, opt_len, rdata_ptr, region)) { + log_err("out of memory"); + break; + } + rdata_ptr += opt_len; + rdata_len -= opt_len; + } + /* ignore rrsigs */ + return LDNS_RCODE_NOERROR; } /** skip RR in packet */ @@ -1060,8 +1169,8 @@ skip_pkt_rrs(sldns_buffer* pkt, int num) } int -parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns, - struct regional* region) +parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns, + struct config_file* cfg, struct comm_point* c, struct regional* region) { size_t rdata_len; uint8_t* rdata_ptr; @@ -1093,7 +1202,9 @@ parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns, edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */ edns->edns_version = sldns_buffer_read_u8(pkt); edns->bits = sldns_buffer_read_u16(pkt); - edns->opt_list = NULL; + edns->opt_list_in = NULL; + edns->opt_list_out = NULL; + edns->opt_list_inplace_cb_out = NULL; edns->padding_block_size = 0; /* take the options */ @@ -1101,12 +1212,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, region)) - return LDNS_RCODE_SERVFAIL; - /* ignore rrsigs */ - - return 0; + return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg, + c, region); } void diff --git a/util/data/msgparse.h b/util/data/msgparse.h index d2fd9c806657..4c0559a739a4 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -70,6 +70,8 @@ struct rrset_parse; struct rr_parse; struct regional; struct edns_option; +struct config_file; +struct comm_point; /** number of buckets in parse rrset hash table. Must be power of 2. */ #define PARSE_TABLE_SIZE 32 @@ -225,8 +227,15 @@ struct edns_data { uint16_t bits; /** UDP reassembly size. */ uint16_t udp_size; - /** rdata element list, or NULL if none */ - struct edns_option* opt_list; + /** rdata element list of options of an incoming packet created at + * parse time, or NULL if none */ + struct edns_option* opt_list_in; + /** rdata element list of options to encode for outgoing packets, + * or NULL if none */ + struct edns_option* opt_list_out; + /** rdata element list of outgoing edns options from modules + * or NULL if none */ + struct edns_option* opt_list_inplace_cb_out; /** block size to pad */ uint16_t padding_block_size; }; @@ -281,8 +290,8 @@ int parse_packet(struct sldns_buffer* pkt, struct msg_parse* msg, * @return: 0 on success. or an RCODE on an error. * RCODE formerr if OPT in wrong section, and so on. */ -int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns, - struct regional* region); +int parse_extract_edns_from_response_msg(struct msg_parse* msg, + struct edns_data* edns, struct regional* region); /** * If EDNS data follows a query section, extract it and initialize edns struct. @@ -290,12 +299,14 @@ int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns, * section. At end, right after EDNS data or no movement if failed. * @param edns: the edns data allocated by the caller. Does not have to be * initialised. + * @param cfg: the configuration (with nsid value etc.) + * @param c: commpoint to determine transport (if needed) * @param region: region to alloc results in (edns option contents) * @return: 0 on success, or an RCODE on error. * RCODE formerr if OPT is badly formatted and so on. */ -int parse_edns_from_pkt(struct sldns_buffer* pkt, struct edns_data* edns, - struct regional* region); +int parse_edns_from_query_pkt(struct sldns_buffer* pkt, struct edns_data* edns, + struct config_file* cfg, struct comm_point* c, struct regional* region); /** * Calculate hash value for rrset in packet. diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 00272fd1c64e..ec46e4724780 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -166,6 +166,32 @@ reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, return 1; } +struct reply_info * +make_new_reply_info(const struct reply_info* rep, struct regional* region, + size_t an_numrrsets, size_t copy_rrsets) +{ + struct reply_info* new_rep; + size_t i; + + /* create a base struct. we specify 'insecure' security status as + * the modified response won't be DNSSEC-valid. In our faked response + * the authority and additional sections will be empty (except possible + * EDNS0 OPT RR in the additional section appended on sending it out), + * so the total number of RRsets is an_numrrsets. */ + new_rep = construct_reply_info_base(region, rep->flags, + rep->qdcount, rep->ttl, rep->prefetch_ttl, + rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets, + sec_status_insecure); + if(!new_rep) + return NULL; + if(!reply_info_alloc_rrset_keys(new_rep, NULL, region)) + return NULL; + for(i=0; i<copy_rrsets; i++) + new_rep->rrsets[i] = rep->rrsets[i]; + + return new_rep; +} + /** find the minimumttl in the rdata of SOA record */ static time_t soa_find_minttl(struct rr_parse* rr) @@ -196,13 +222,17 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, * minimum-ttl in the rdata of the SOA record */ if(*rr_ttl > soa_find_minttl(rr)) *rr_ttl = soa_find_minttl(rr); - if(*rr_ttl > MAX_NEG_TTL) - *rr_ttl = MAX_NEG_TTL; } if(!SERVE_ORIGINAL_TTL && (*rr_ttl < MIN_TTL)) *rr_ttl = MIN_TTL; if(!SERVE_ORIGINAL_TTL && (*rr_ttl > MAX_TTL)) *rr_ttl = MAX_TTL; + if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) { + /* max neg ttl overrides the min and max ttl of everything + * else, it is for a more specific record */ + if(*rr_ttl > MAX_NEG_TTL) + *rr_ttl = MAX_NEG_TTL; + } if(*rr_ttl < data->ttl) data->ttl = *rr_ttl; @@ -488,14 +518,13 @@ int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc, if((ret = parse_packet(pkt, msg, region)) != 0) { return ret; } - if((ret = parse_extract_edns(msg, edns, region)) != 0) + if((ret = parse_extract_edns_from_response_msg(msg, edns, region)) != 0) return ret; /* parse OK, allocate return structures */ /* this also performs dname decompression */ if(!parse_create_msg(pkt, msg, alloc, qinf, rep, NULL)) { query_info_clear(qinf); - reply_info_parsedelete(*rep, alloc); *rep = NULL; return LDNS_RCODE_SERVFAIL; } @@ -960,34 +989,6 @@ parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region, return rep; } -int edns_opt_append(struct edns_data* edns, struct regional* region, - uint16_t code, size_t len, uint8_t* data) -{ - struct edns_option** prevp; - struct edns_option* opt; - - /* allocate new element */ - opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); - if(!opt) - return 0; - opt->next = NULL; - opt->opt_code = code; - opt->opt_len = len; - opt->opt_data = NULL; - if(len > 0) { - opt->opt_data = regional_alloc_init(region, data, len); - if(!opt->opt_data) - return 0; - } - - /* append at end of list */ - prevp = &edns->opt_list; - while(*prevp != NULL) - prevp = &((*prevp)->next); - *prevp = opt; - return 1; -} - int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, uint8_t* data, struct regional* region) { @@ -1068,7 +1069,7 @@ static int inplace_cb_reply_call_generic( (void)(*(inplace_cb_reply_func_type*)cb->cb)(qinfo, qstate, rep, rcode, edns, &opt_list_out, repinfo, region, start_time, cb->id, cb->cb_arg); } - edns->opt_list = opt_list_out; + edns->opt_list_inplace_cb_out = opt_list_out; return 1; } diff --git a/util/data/msgreply.h b/util/data/msgreply.h index c6b220ed8d71..81c763fc7c3a 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -382,6 +382,21 @@ struct reply_info* reply_info_copy(struct reply_info* rep, int reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, struct regional* region); +/* + * Create a new reply_info based on 'rep'. The new info is based on + * the passed 'rep', but ignores any rrsets except for the first 'an_numrrsets' + * RRsets in the answer section. These answer rrsets are copied to the + * new info, up to 'copy_rrsets' rrsets (which must not be larger than + * 'an_numrrsets'). If an_numrrsets > copy_rrsets, the remaining rrsets array + * entries will be kept empty so the caller can fill them later. When rrsets + * are copied, they are shallow copied. The caller must ensure that the + * copied rrsets are valid throughout its lifetime and must provide appropriate + * mutex if it can be shared by multiple threads. + */ +struct reply_info * +make_new_reply_info(const struct reply_info* rep, struct regional* region, + size_t an_numrrsets, size_t copy_rrsets); + /** * Copy a parsed rrset into given key, decompressing and allocating rdata. * @param pkt: packet for decompression @@ -504,18 +519,6 @@ void log_query_info(enum verbosity_value v, const char* str, struct query_info* qinf); /** - * Append edns option to edns data structure - * @param edns: the edns data structure to append the edns option to. - * @param region: region to allocate the new edns option. - * @param code: the edns option's code. - * @param len: the edns option's length. - * @param data: the edns option's data. - * @return false on failure. - */ -int edns_opt_append(struct edns_data* edns, struct regional* region, - uint16_t code, size_t len, uint8_t* data); - -/** * Append edns option to edns option list * @param list: the edns option list to append the edns option to. * @param code: the edns option's code. diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h index ff95c0af0e8d..e1feb22bb7f6 100644 --- a/util/data/packed_rrset.h +++ b/util/data/packed_rrset.h @@ -61,6 +61,13 @@ typedef uint64_t rrset_id_type; * updated on encoding in a reply. This flag is not expected to be set in * cached data. */ #define PACKED_RRSET_FIXEDTTL 0x80000000 +/** This rrset is from RPZ. It is not real, it is synthesized data to block + * access. The flag makes lookups, from cache in iterator, ignore the fake + * items and only use actual data. Eg. when the iterator looksup NS, CNAME, + * A and AAAA types, it then gets items without this flag that are the + * actual network. But messages with these records in it can be stored in + * the cache and retrieved for a reply. */ +#define PACKED_RRSET_RPZ 0x8 /** number of rrs and rrsets for integer overflow protection. More than * this is not really possible (64K packet has much less RRs and RRsets) in @@ -88,6 +95,7 @@ struct packed_rrset_key { * o PACKED_RRSET_PARENT_SIDE * o PACKED_RRSET_SOA_NEG * o PACKED_RRSET_FIXEDTTL (not supposed to be cached) + * o PACKED_RRSET_RPZ */ uint32_t flags; /** the rrset type in network format */ |
