summaryrefslogtreecommitdiff
path: root/util/data
diff options
context:
space:
mode:
Diffstat (limited to 'util/data')
-rw-r--r--util/data/msgencode.c20
-rw-r--r--util/data/msgparse.c150
-rw-r--r--util/data/msgparse.h23
-rw-r--r--util/data/msgreply.c67
-rw-r--r--util/data/msgreply.h27
-rw-r--r--util/data/packed_rrset.h8
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 */