sldns_buffer_set_limit(buf, lim);
return 0;
}
- if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR) {
+ if(parse_extract_edns(prs, &edns, qstate->env->scratch) !=
+ LDNS_RCODE_NOERROR) {
sldns_buffer_set_limit(buf, lim);
return 0;
}
qinfo->qname_len, qinfo->qtype, qinfo->qclass,
worker->scratchpad, &msg, timenow);
if(!dp) { /* no delegation, need to reprime */
- regional_free_all(worker->scratchpad);
return 0;
}
if(must_validate) {
case sec_status_unchecked:
/* some rrsets have not been verified yet, go and
* let validator do that */
- regional_free_all(worker->scratchpad);
return 0;
case sec_status_bogus:
/* some rrsets are bogus, reply servfail */
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(!edns_opt_inplace_reply(edns, worker->scratchpad))
+ return 0;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
- regional_free_all(worker->scratchpad);
if(worker->stats.extended) {
worker->stats.ans_bogus++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL]++;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(!edns_opt_inplace_reply(edns, worker->scratchpad))
+ return 0;
msg->rep->flags |= BIT_QR|BIT_RA;
if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags,
repinfo->c->buffer, 0, 1, worker->scratchpad,
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
}
- regional_free_all(worker->scratchpad);
if(worker->stats.extended) {
if(secure) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
bail_out:
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
- regional_free_all(worker->scratchpad);
return 0;
}
}
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(!edns_opt_inplace_reply(edns, worker->scratchpad))
+ return 0;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns);
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
- regional_free_all(worker->scratchpad);
if(worker->stats.extended) {
worker->stats.ans_bogus ++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(!edns_opt_inplace_reply(edns, worker->scratchpad))
+ return 0;
if(!reply_info_answer_encode(qinfo, rep, id, flags,
repinfo->c->buffer, timenow, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
* is bad while holding locks. */
rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad,
rep->ref, rep->rrset_count);
- regional_free_all(worker->scratchpad);
if(worker->stats.extended) {
if(secure) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
* @param edns: edns reply information.
*/
static void
-chaos_replystr(sldns_buffer* pkt, const char* str, struct edns_data* edns)
+chaos_replystr(sldns_buffer* pkt, const char* str, struct edns_data* edns,
+ struct worker* worker)
{
size_t len = strlen(str);
unsigned int rd = LDNS_RD_WIRE(sldns_buffer_begin(pkt));
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->bits &= EDNS_DO;
+ if(!edns_opt_inplace_reply(edns, worker->scratchpad))
+ edns->opt_list = NULL;
attach_edns_record(pkt, edns);
}
char buf[MAXHOSTNAMELEN+1];
if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
buf[MAXHOSTNAMELEN] = 0;
- chaos_replystr(pkt, buf, edns);
+ chaos_replystr(pkt, buf, edns, w);
} else {
log_err("gethostname: %s", strerror(errno));
- chaos_replystr(pkt, "no hostname", edns);
+ chaos_replystr(pkt, "no hostname", edns, w);
}
}
- else chaos_replystr(pkt, cfg->identity, edns);
+ else chaos_replystr(pkt, cfg->identity, edns, w);
return 1;
}
if(query_dname_compare(qinfo->qname,
if(cfg->hide_version)
return 0;
if(cfg->version==NULL || cfg->version[0]==0)
- chaos_replystr(pkt, PACKAGE_STRING, edns);
- else chaos_replystr(pkt, cfg->version, edns);
+ chaos_replystr(pkt, PACKAGE_STRING, edns, w);
+ else chaos_replystr(pkt, cfg->version, edns, w);
return 1;
}
return 0;
}
goto send_reply;
}
- if((ret=parse_edns_from_pkt(c->buffer, &edns)) != 0) {
+ if((ret=parse_edns_from_pkt(c->buffer, &edns, worker->scratchpad)) != 0) {
struct edns_data reply_edns;
verbose(VERB_ALGO, "worker parse edns: formerror.");
log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
error_encode(c->buffer, ret, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns);
+ regional_free_all(worker->scratchpad);
server_stats_insrcode(&worker->stats, c->buffer);
goto send_reply;
}
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE;
edns.bits &= EDNS_DO;
+ edns.opt_list = NULL;
verbose(VERB_ALGO, "query with bad edns version.");
log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), NULL);
attach_edns_record(c->buffer, &edns);
+ regional_free_all(worker->scratchpad);
goto send_reply;
}
if(edns.edns_present && edns.udp_size < NORMAL_UDP_SIZE &&
sldns_buffer_write_at(c->buffer, 4,
(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
sldns_buffer_flip(c->buffer);
+ regional_free_all(worker->scratchpad);
goto send_reply;
}
if(worker->stats.extended)
if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
&edns, c->buffer)) {
server_stats_insrcode(&worker->stats, c->buffer);
+ regional_free_all(worker->scratchpad);
goto send_reply;
}
if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns,
* might need to bail out based on ACLs now. */
if((ret=deny_refuse_non_local(c, acl, worker, repinfo)) != -1)
{
+ regional_free_all(worker->scratchpad);
if(ret == 1)
goto send_reply;
return ret;
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
sldns_buffer_flip(c->buffer);
+ regional_free_all(worker->scratchpad);
server_stats_insrcode(&worker->stats, c->buffer);
log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
&repinfo->addr, repinfo->addrlen);
sldns_buffer_read_u16_at(c->buffer, 2),
repinfo, leeway);
rc = 0;
+ regional_free_all(worker->scratchpad);
goto send_reply_rc;
}
lock_rw_unlock(&e->lock);
+ regional_free_all(worker->scratchpad);
goto send_reply;
}
verbose(VERB_ALGO, "answer from the cache failed");
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
&edns)) {
+ regional_free_all(worker->scratchpad);
goto send_reply;
}
verbose(VERB_ALGO, "answer norec from cache -- "
mesh_new_client(worker->env.mesh, &qinfo,
sldns_buffer_read_u16_at(c->buffer, 2),
&edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer));
+ regional_free_all(worker->scratchpad);
worker_mem_report(worker, NULL);
return 0;
- Fix windows service to be created run with limited rights, as a
network service account, from Mario Turschmann.
- compat strsep implementation.
+ - generic edns option parse and store code.
30 May 2016: Wouter
- Fix time in case answer comes from cache in ub_resolve_event().
goto handle_it;
}
/* edns is not examined, but removed from message to help cache */
- if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR)
+ if(parse_extract_edns(prs, &edns, qstate->env->scratch) !=
+ LDNS_RCODE_NOERROR)
goto handle_it;
/* remove CD-bit, we asked for in case we handle validation ourself */
prs->flags &= ~BIT_CD;
return 0;
}
/* edns is not examined, but removed from message to help cache */
- if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR)
+ if(parse_extract_edns(prs, &edns, qstate->env->scratch) !=
+ LDNS_RCODE_NOERROR)
return 0;
/* remove CD-bit, we asked for in case we handle validation ourself */
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
- if(!reply_info_answer_encode(qinfo, &rep,
+ if(!edns_opt_inplace_reply(edns, temp) ||
+ !reply_info_answer_encode(qinfo, &rep,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns,
s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) {
log_err("mesh_state_create: out of memory; SERVFAIL");
+ if(!edns_opt_inplace_reply(edns, mesh->env->scratch))
+ edns->opt_list = NULL;
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
/* add reply to s */
if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo->qname)) {
log_err("mesh_new_client: out of memory; SERVFAIL");
+ if(!edns_opt_inplace_reply(edns, mesh->env->scratch))
+ edns->opt_list = NULL;
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
r->edns.udp_size = EDNS_ADVERTISED_SIZE;
r->edns.ext_rcode = 0;
r->edns.bits &= EDNS_DO;
- if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
+ if(!edns_opt_inplace_reply(&r->edns, m->s.region) ||
+ !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r->buf, 0, 1,
m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO), secure))
if(prev && prev->qflags == r->qflags &&
prev->edns.edns_present == r->edns.edns_present &&
prev->edns.bits == r->edns.bits &&
- prev->edns.udp_size == r->edns.udp_size) {
+ prev->edns.udp_size == r->edns.udp_size &&
+ edns_opt_list_equal(prev->edns.opt_list, r->edns.opt_list)) {
/* if the previous reply is identical to this one, fix ID */
if(prev->query_reply.c->buffer != r->query_reply.c->buffer)
sldns_buffer_copy(r->query_reply.c->buffer,
r->edns.ext_rcode = 0;
r->edns.bits &= EDNS_DO;
m->s.qinfo.qname = r->qname;
- if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
+ if(!edns_opt_inplace_reply(&r->edns, m->s.region) ||
+ !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r->query_reply.c->buffer, 0, 1,
m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO), secure))
r->cb = cb;
r->cb_arg = cb_arg;
r->edns = *edns;
+ if(edns->opt_list) {
+ r->edns.opt_list = edns_opt_copy_region(edns->opt_list,
+ s->s.region);
+ if(!r->edns.opt_list)
+ return 0;
+ }
r->qid = qid;
r->qflags = qflags;
r->next = s->cb_list;
return 0;
r->query_reply = *rep;
r->edns = *edns;
+ if(edns->opt_list) {
+ r->edns.opt_list = edns_opt_copy_region(edns->opt_list,
+ s->s.region);
+ if(!r->edns.opt_list)
+ return 0;
+ }
r->qid = qid;
r->qflags = qflags;
r->start_time = *s->s.env->now_tv;
return 0;
s->reply_list = r;
return 1;
-
}
/**
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE;
edns.bits = 0;
+ edns.opt_list = NULL;
if(dnssec)
edns.bits = EDNS_DO;
attach_edns_record(pend->buffer, &edns);
uint16_t
calc_edns_field_size(struct edns_data* edns)
{
+ size_t rdatalen = 0;
+ struct edns_option* opt;
if(!edns || !edns->edns_present)
return 0;
- /* domain root '.' + type + class + ttl + rdatalen(=0) */
- return 1 + 2 + 2 + 4 + 2;
+ for(opt = edns->opt_list; opt; opt = opt->next) {
+ rdatalen += 4 + opt->opt_len;
+ }
+ /* domain root '.' + type + class + ttl + rdatalen */
+ return 1 + 2 + 2 + 4 + 2 + rdatalen;
}
void
attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
{
size_t len;
+ size_t rdatapos;
+ struct edns_option* opt;
if(!edns || !edns->edns_present)
return;
/* inc additional count */
sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
sldns_buffer_write_u8(pkt, edns->edns_version);
sldns_buffer_write_u16(pkt, edns->bits);
+ rdatapos = sldns_buffer_position(pkt);
sldns_buffer_write_u16(pkt, 0); /* rdatalen */
+ /* write rdata */
+ for(opt=edns->opt_list; opt; opt=opt->next) {
+ 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(edns->opt_list)
+ sldns_buffer_write_u16_at(pkt, rdatapos,
+ sldns_buffer_position(pkt)-rdatapos-2);
sldns_buffer_flip(pkt);
}
*/
#include "config.h"
#include "util/data/msgparse.h"
+#include "util/data/msgreply.h"
#include "util/data/dname.h"
#include "util/data/packed_rrset.h"
#include "util/storage/lookup3.h"
return 0;
}
+/** 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)
+{
+ /* 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 */
+ if(!edns_opt_append(edns, region, opt_code, opt_len,
+ rdata_ptr)) {
+ log_err("out of memory");
+ return 0;
+ }
+ rdata_ptr += opt_len;
+ rdata_len -= opt_len;
+ }
+ return 1;
+}
+
int
-parse_extract_edns(struct msg_parse* msg, struct edns_data* edns)
+parse_extract_edns(struct msg_parse* msg, struct edns_data* edns,
+ struct regional* region)
{
struct rrset_parse* rrset = msg->rrset_first;
struct rrset_parse* prev = 0;
struct rrset_parse* found = 0;
struct rrset_parse* found_prev = 0;
+ size_t rdata_len;
+ uint8_t* rdata_ptr;
/* since the class encodes the UDP size, we cannot use hash table to
* find the EDNS OPT record. Scan the packet. */
while(rrset) {
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);
- /* ignore rdata and rrsigs */
+ edns->opt_list = NULL;
+
+ /* take the options */
+ rdata_len = found->rr_first->size;
+ rdata_ptr = found->rr_first->ttl_data+6;
+ if(!parse_edns_options(rdata_ptr, rdata_len, edns, region))
+ return 0;
+
+ /* ignore rrsigs */
+
return 0;
}
int
-parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns)
+parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns,
+ struct regional* region)
{
+ size_t rdata_len;
+ uint8_t* rdata_ptr;
log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1);
log_assert(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0);
log_assert(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) == 0);
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);
- /* ignore rdata and rrsigs */
+ edns->opt_list = NULL;
+
+ /* take the options */
+ rdata_len = sldns_buffer_read_u16(pkt);
+ 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;
}
struct rrset_parse;
struct rr_parse;
struct regional;
+struct edns_option;
/** number of buckets in parse rrset hash table. Must be power of 2. */
#define PARSE_TABLE_SIZE 32
/**
* EDNS data storage
- * EDNS rdata is ignored.
+ * rdata is parsed in a list (has accessor functions). allocated in a
+ * region.
*/
struct edns_data {
/** if EDNS OPT record was present */
uint16_t bits;
/** UDP reassembly size. */
uint16_t udp_size;
+ /** rdata element list, or NULL if none */
+ struct edns_option* opt_list;
+};
+
+/**
+ * EDNS option
+ */
+struct edns_option {
+ /** next item in list */
+ struct edns_option* next;
+ /** type of this edns option */
+ uint16_t opt_code;
+ /** length of this edns option (cannot exceed uint16 in encoding) */
+ size_t opt_len;
+ /** data of this edns option; allocated in region, or NULL if len=0 */
+ uint8_t* opt_data;
};
/**
* @param msg: parsed message structure. Modified on exit, if EDNS was present
* it is removed from the additional section.
* @param edns: the edns data is stored here. Does not have to be initialised.
+ * @param region: region to alloc results in (edns option contents)
* @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);
+int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns,
+ struct regional* region);
/**
* If EDNS data follows a query section, extract it and initialize edns struct.
* 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 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);
+int parse_edns_from_pkt(struct sldns_buffer* pkt, struct edns_data* edns,
+ struct regional* region);
/**
* Calculate hash value for rrset in packet.
if((ret = parse_packet(pkt, msg, region)) != 0) {
return ret;
}
- if((ret = parse_extract_edns(msg, edns)) != 0)
+ if((ret = parse_extract_edns(msg, edns, region)) != 0)
return ret;
/* parse OK, allocate return structures */
}
return 1;
}
+
+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 = 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_inplace_reply(struct edns_data* edns, struct regional* region)
+{
+ (void)region;
+ /* remove all edns options from the reply, because only the
+ * options that we understand should be in the reply
+ * (sec 6.1.2 RFC 6891) */
+ edns->opt_list = NULL;
+ return 1;
+}
+
+struct edns_option* edns_opt_copy_region(struct edns_option* list,
+ struct regional* region)
+{
+ struct edns_option* result = NULL, *cur = NULL, *s;
+ while(list) {
+ /* copy edns option structure */
+ s = regional_alloc_init(region, list, sizeof(*list));
+ if(!s) return NULL;
+ s->next = NULL;
+
+ /* copy option data */
+ if(s->opt_data) {
+ s->opt_data = regional_alloc_init(region, s->opt_data,
+ s->opt_len);
+ if(!s->opt_data)
+ return NULL;
+ }
+
+ /* link into list */
+ if(cur)
+ cur->next = s;
+ else result = s;
+ cur = s;
+
+ /* examine next element */
+ list = list->next;
+ }
+ return result;
+}
+
+int edns_opt_list_equal(struct edns_option* p, struct edns_option* q)
+{
+ while(p && q) {
+ /* compare elements */
+ if(p->opt_code != q->opt_code ||
+ p->opt_len != q->opt_len)
+ return 0;
+ if(p->opt_len > 0 && q->opt_len > 0) {
+ if(memcmp(p->opt_data, q->opt_data, p->opt_len) != 0)
+ return 0;
+ }
+
+ p = p->next;
+ q = q->next;
+ }
+ if(p || q)
+ return 0; /* uneven length lists */
+ return 1;
+}
void log_query_info(enum verbosity_value v, const char* str,
struct query_info* qinf);
+/**
+ * Append edns option to edns data structure
+ */
+int edns_opt_append(struct edns_data* edns, struct regional* region,
+ uint16_t code, size_t len, uint8_t* data);
+
+/**
+ * Transform edns data structure from query structure into reply structure.
+ * In place transform, for errors and cache replies.
+ * @param edns: on input contains the edns from the query. On output contains
+ * the edns for the answer. Add new options to the opt_list to put them
+ * in the answer (allocated in the region, with edns_opt_append).
+ * @param region: to allocate stuff in.
+ * @return false on failure (servfail to client, or for some error encodings,
+ * no EDNS options in the answer).
+ */
+int edns_opt_inplace_reply(struct edns_data* edns, struct regional* region);
+
+/**
+ * Copy edns option list allocated to the new region
+ */
+struct edns_option* edns_opt_copy_region(struct edns_option* list,
+ struct regional* region);
+
+/**
+ * See if edns option lists are equal, also order and contents of options.
+ */
+int edns_opt_list_equal(struct edns_option* p, struct edns_option* q);
+
#endif /* UTIL_DATA_MSGREPLY_H */
edns.ext_rcode = 0;
edns.edns_version = 0;
edns.bits = EDNS_DO;
+ edns.opt_list = NULL;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535;