- generic edns option parse and store code.

git-svn-id: file:///svn/unbound/trunk@3740 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2016-05-31 15:08:05 +00:00
parent 41b78b263d
commit 40dd2acfd9
14 changed files with 278 additions and 32 deletions

View File

@ -406,7 +406,8 @@ parse_data(struct module_qstate* qstate, struct sldns_buffer* buf)
sldns_buffer_set_limit(buf, lim); sldns_buffer_set_limit(buf, lim);
return 0; 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); sldns_buffer_set_limit(buf, lim);
return 0; return 0;
} }

View File

@ -483,7 +483,6 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
qinfo->qname_len, qinfo->qtype, qinfo->qclass, qinfo->qname_len, qinfo->qtype, qinfo->qclass,
worker->scratchpad, &msg, timenow); worker->scratchpad, &msg, timenow);
if(!dp) { /* no delegation, need to reprime */ if(!dp) { /* no delegation, need to reprime */
regional_free_all(worker->scratchpad);
return 0; return 0;
} }
if(must_validate) { if(must_validate) {
@ -491,7 +490,6 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
case sec_status_unchecked: case sec_status_unchecked:
/* some rrsets have not been verified yet, go and /* some rrsets have not been verified yet, go and
* let validator do that */ * let validator do that */
regional_free_all(worker->scratchpad);
return 0; return 0;
case sec_status_bogus: case sec_status_bogus:
/* some rrsets are bogus, reply servfail */ /* some rrsets are bogus, reply servfail */
@ -499,9 +497,10 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
edns->udp_size = EDNS_ADVERTISED_SIZE; edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0; edns->ext_rcode = 0;
edns->bits &= EDNS_DO; edns->bits &= EDNS_DO;
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
return 0;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns); &msg->qinfo, id, flags, edns);
regional_free_all(worker->scratchpad);
if(worker->stats.extended) { if(worker->stats.extended) {
worker->stats.ans_bogus++; worker->stats.ans_bogus++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL]++; worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL]++;
@ -527,6 +526,8 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
edns->udp_size = EDNS_ADVERTISED_SIZE; edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0; edns->ext_rcode = 0;
edns->bits &= EDNS_DO; edns->bits &= EDNS_DO;
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
return 0;
msg->rep->flags |= BIT_QR|BIT_RA; msg->rep->flags |= BIT_QR|BIT_RA;
if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags,
repinfo->c->buffer, 0, 1, worker->scratchpad, repinfo->c->buffer, 0, 1, worker->scratchpad,
@ -534,7 +535,6 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns); &msg->qinfo, id, flags, edns);
} }
regional_free_all(worker->scratchpad);
if(worker->stats.extended) { if(worker->stats.extended) {
if(secure) worker->stats.ans_secure++; if(secure) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer); server_stats_insrcode(&worker->stats, repinfo->c->buffer);
@ -574,7 +574,6 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
bail_out: bail_out:
rrset_array_unlock_touch(worker->env.rrset_cache, rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count); worker->scratchpad, rep->ref, rep->rrset_count);
regional_free_all(worker->scratchpad);
return 0; return 0;
} }
} }
@ -585,11 +584,12 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
edns->udp_size = EDNS_ADVERTISED_SIZE; edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0; edns->ext_rcode = 0;
edns->bits &= EDNS_DO; edns->bits &= EDNS_DO;
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
return 0;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns); qinfo, id, flags, edns);
rrset_array_unlock_touch(worker->env.rrset_cache, rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count); worker->scratchpad, rep->ref, rep->rrset_count);
regional_free_all(worker->scratchpad);
if(worker->stats.extended) { if(worker->stats.extended) {
worker->stats.ans_bogus ++; worker->stats.ans_bogus ++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++; worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++;
@ -616,6 +616,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
edns->udp_size = EDNS_ADVERTISED_SIZE; edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0; edns->ext_rcode = 0;
edns->bits &= EDNS_DO; edns->bits &= EDNS_DO;
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
return 0;
if(!reply_info_answer_encode(qinfo, rep, id, flags, if(!reply_info_answer_encode(qinfo, rep, id, flags,
repinfo->c->buffer, timenow, 1, worker->scratchpad, repinfo->c->buffer, timenow, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
@ -626,7 +628,6 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
* is bad while holding locks. */ * is bad while holding locks. */
rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad,
rep->ref, rep->rrset_count); rep->ref, rep->rrset_count);
regional_free_all(worker->scratchpad);
if(worker->stats.extended) { if(worker->stats.extended) {
if(secure) worker->stats.ans_secure++; if(secure) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer); server_stats_insrcode(&worker->stats, repinfo->c->buffer);
@ -660,7 +661,8 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
* @param edns: edns reply information. * @param edns: edns reply information.
*/ */
static void 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); size_t len = strlen(str);
unsigned int rd = LDNS_RD_WIRE(sldns_buffer_begin(pkt)); unsigned int rd = LDNS_RD_WIRE(sldns_buffer_begin(pkt));
@ -689,6 +691,8 @@ chaos_replystr(sldns_buffer* pkt, const char* str, struct edns_data* edns)
edns->edns_version = EDNS_ADVERTISED_VERSION; edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE; edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->bits &= EDNS_DO; edns->bits &= EDNS_DO;
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
edns->opt_list = NULL;
attach_edns_record(pkt, edns); attach_edns_record(pkt, edns);
} }
@ -718,13 +722,13 @@ answer_chaos(struct worker* w, struct query_info* qinfo,
char buf[MAXHOSTNAMELEN+1]; char buf[MAXHOSTNAMELEN+1];
if (gethostname(buf, MAXHOSTNAMELEN) == 0) { if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
buf[MAXHOSTNAMELEN] = 0; buf[MAXHOSTNAMELEN] = 0;
chaos_replystr(pkt, buf, edns); chaos_replystr(pkt, buf, edns, w);
} else { } else {
log_err("gethostname: %s", strerror(errno)); 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; return 1;
} }
if(query_dname_compare(qinfo->qname, if(query_dname_compare(qinfo->qname,
@ -735,8 +739,8 @@ answer_chaos(struct worker* w, struct query_info* qinfo,
if(cfg->hide_version) if(cfg->hide_version)
return 0; return 0;
if(cfg->version==NULL || cfg->version[0]==0) if(cfg->version==NULL || cfg->version[0]==0)
chaos_replystr(pkt, PACKAGE_STRING, edns); chaos_replystr(pkt, PACKAGE_STRING, edns, w);
else chaos_replystr(pkt, cfg->version, edns); else chaos_replystr(pkt, cfg->version, edns, w);
return 1; return 1;
} }
return 0; return 0;
@ -865,7 +869,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
} }
goto send_reply; 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; struct edns_data reply_edns;
verbose(VERB_ALGO, "worker parse edns: formerror."); verbose(VERB_ALGO, "worker parse edns: formerror.");
log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen); log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
@ -876,6 +880,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
error_encode(c->buffer, ret, &qinfo, error_encode(c->buffer, ret, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer), *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns); sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns);
regional_free_all(worker->scratchpad);
server_stats_insrcode(&worker->stats, c->buffer); server_stats_insrcode(&worker->stats, c->buffer);
goto send_reply; goto send_reply;
} }
@ -884,12 +889,14 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
edns.edns_version = EDNS_ADVERTISED_VERSION; edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE; edns.udp_size = EDNS_ADVERTISED_SIZE;
edns.bits &= EDNS_DO; edns.bits &= EDNS_DO;
edns.opt_list = NULL;
verbose(VERB_ALGO, "query with bad edns version."); verbose(VERB_ALGO, "query with bad edns version.");
log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen); log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo, error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer), *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), NULL); sldns_buffer_read_u16_at(c->buffer, 2), NULL);
attach_edns_record(c->buffer, &edns); attach_edns_record(c->buffer, &edns);
regional_free_all(worker->scratchpad);
goto send_reply; goto send_reply;
} }
if(edns.edns_present && edns.udp_size < NORMAL_UDP_SIZE && if(edns.edns_present && edns.udp_size < NORMAL_UDP_SIZE &&
@ -918,6 +925,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
sldns_buffer_write_at(c->buffer, 4, sldns_buffer_write_at(c->buffer, 4,
(uint8_t*)"\0\0\0\0\0\0\0\0", 8); (uint8_t*)"\0\0\0\0\0\0\0\0", 8);
sldns_buffer_flip(c->buffer); sldns_buffer_flip(c->buffer);
regional_free_all(worker->scratchpad);
goto send_reply; goto send_reply;
} }
if(worker->stats.extended) if(worker->stats.extended)
@ -928,6 +936,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo, if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
&edns, c->buffer)) { &edns, c->buffer)) {
server_stats_insrcode(&worker->stats, c->buffer); server_stats_insrcode(&worker->stats, c->buffer);
regional_free_all(worker->scratchpad);
goto send_reply; goto send_reply;
} }
if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns, if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns,
@ -945,6 +954,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
* might need to bail out based on ACLs now. */ * might need to bail out based on ACLs now. */
if((ret=deny_refuse_non_local(c, acl, worker, repinfo)) != -1) if((ret=deny_refuse_non_local(c, acl, worker, repinfo)) != -1)
{ {
regional_free_all(worker->scratchpad);
if(ret == 1) if(ret == 1)
goto send_reply; goto send_reply;
return ret; return ret;
@ -961,6 +971,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED); LDNS_RCODE_REFUSED);
sldns_buffer_flip(c->buffer); sldns_buffer_flip(c->buffer);
regional_free_all(worker->scratchpad);
server_stats_insrcode(&worker->stats, c->buffer); server_stats_insrcode(&worker->stats, c->buffer);
log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from", log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
&repinfo->addr, repinfo->addrlen); &repinfo->addr, repinfo->addrlen);
@ -984,9 +995,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
sldns_buffer_read_u16_at(c->buffer, 2), sldns_buffer_read_u16_at(c->buffer, 2),
repinfo, leeway); repinfo, leeway);
rc = 0; rc = 0;
regional_free_all(worker->scratchpad);
goto send_reply_rc; goto send_reply_rc;
} }
lock_rw_unlock(&e->lock); lock_rw_unlock(&e->lock);
regional_free_all(worker->scratchpad);
goto send_reply; goto send_reply;
} }
verbose(VERB_ALGO, "answer from the cache failed"); verbose(VERB_ALGO, "answer from the cache failed");
@ -997,6 +1010,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer), *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), repinfo, sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
&edns)) { &edns)) {
regional_free_all(worker->scratchpad);
goto send_reply; goto send_reply;
} }
verbose(VERB_ALGO, "answer norec from cache -- " verbose(VERB_ALGO, "answer norec from cache -- "
@ -1017,6 +1031,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
mesh_new_client(worker->env.mesh, &qinfo, mesh_new_client(worker->env.mesh, &qinfo,
sldns_buffer_read_u16_at(c->buffer, 2), sldns_buffer_read_u16_at(c->buffer, 2),
&edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer)); &edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer));
regional_free_all(worker->scratchpad);
worker_mem_report(worker, NULL); worker_mem_report(worker, NULL);
return 0; return 0;

View File

@ -2,6 +2,7 @@
- Fix windows service to be created run with limited rights, as a - Fix windows service to be created run with limited rights, as a
network service account, from Mario Turschmann. network service account, from Mario Turschmann.
- compat strsep implementation. - compat strsep implementation.
- generic edns option parse and store code.
30 May 2016: Wouter 30 May 2016: Wouter
- Fix time in case answer comes from cache in ub_resolve_event(). - Fix time in case answer comes from cache in ub_resolve_event().

View File

@ -3124,7 +3124,8 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
goto handle_it; goto handle_it;
} }
/* edns is not examined, but removed from message to help cache */ /* 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; goto handle_it;
/* remove CD-bit, we asked for in case we handle validation ourself */ /* remove CD-bit, we asked for in case we handle validation ourself */
prs->flags &= ~BIT_CD; prs->flags &= ~BIT_CD;

View File

@ -129,7 +129,8 @@ int createResponse(struct module_qstate* qstate, sldns_buffer* pkt)
return 0; return 0;
} }
/* edns is not examined, but removed from message to help cache */ /* 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; return 0;
/* remove CD-bit, we asked for in case we handle validation ourself */ /* remove CD-bit, we asked for in case we handle validation ourself */

View File

@ -1042,7 +1042,8 @@ local_encode(struct query_info* qinfo, struct edns_data* edns,
edns->udp_size = EDNS_ADVERTISED_SIZE; edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0; edns->ext_rcode = 0;
edns->bits &= EDNS_DO; 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), *(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), sldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns, buf, 0, 0, temp, udpsize, edns,

View File

@ -315,6 +315,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) { if(!s) {
log_err("mesh_state_create: out of memory; SERVFAIL"); 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, error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns); qinfo, qid, qflags, edns);
comm_point_send_reply(rep); comm_point_send_reply(rep);
@ -338,6 +340,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
/* add reply to s */ /* add reply to s */
if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo->qname)) { if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo->qname)) {
log_err("mesh_new_client: out of memory; SERVFAIL"); 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, error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns); qinfo, qid, qflags, edns);
comm_point_send_reply(rep); comm_point_send_reply(rep);
@ -809,7 +813,8 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.udp_size = EDNS_ADVERTISED_SIZE;
r->edns.ext_rcode = 0; r->edns.ext_rcode = 0;
r->edns.bits &= EDNS_DO; 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, r->qflags, r->buf, 0, 1,
m->s.env->scratch, udp_size, &r->edns, m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO), secure)) (int)(r->edns.bits & EDNS_DO), secure))
@ -859,7 +864,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
if(prev && prev->qflags == r->qflags && if(prev && prev->qflags == r->qflags &&
prev->edns.edns_present == r->edns.edns_present && prev->edns.edns_present == r->edns.edns_present &&
prev->edns.bits == r->edns.bits && 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 the previous reply is identical to this one, fix ID */
if(prev->query_reply.c->buffer != r->query_reply.c->buffer) if(prev->query_reply.c->buffer != r->query_reply.c->buffer)
sldns_buffer_copy(r->query_reply.c->buffer, sldns_buffer_copy(r->query_reply.c->buffer,
@ -881,7 +887,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
r->edns.ext_rcode = 0; r->edns.ext_rcode = 0;
r->edns.bits &= EDNS_DO; r->edns.bits &= EDNS_DO;
m->s.qinfo.qname = r->qname; 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, r->qflags, r->query_reply.c->buffer, 0, 1,
m->s.env->scratch, udp_size, &r->edns, m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO), secure)) (int)(r->edns.bits & EDNS_DO), secure))
@ -973,6 +980,12 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
r->cb = cb; r->cb = cb;
r->cb_arg = cb_arg; r->cb_arg = cb_arg;
r->edns = *edns; 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->qid = qid;
r->qflags = qflags; r->qflags = qflags;
r->next = s->cb_list; r->next = s->cb_list;
@ -990,6 +1003,12 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
return 0; return 0;
r->query_reply = *rep; r->query_reply = *rep;
r->edns = *edns; 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->qid = qid;
r->qflags = qflags; r->qflags = qflags;
r->start_time = *s->s.env->now_tv; r->start_time = *s->s.env->now_tv;
@ -1000,7 +1019,6 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
return 0; return 0;
s->reply_list = r; s->reply_list = r;
return 1; return 1;
} }
/** /**

View File

@ -1077,6 +1077,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
edns.edns_version = EDNS_ADVERTISED_VERSION; edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE; edns.udp_size = EDNS_ADVERTISED_SIZE;
edns.bits = 0; edns.bits = 0;
edns.opt_list = NULL;
if(dnssec) if(dnssec)
edns.bits = EDNS_DO; edns.bits = EDNS_DO;
attach_edns_record(pend->buffer, &edns); attach_edns_record(pend->buffer, &edns);

View File

@ -717,16 +717,23 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
uint16_t uint16_t
calc_edns_field_size(struct edns_data* edns) calc_edns_field_size(struct edns_data* edns)
{ {
size_t rdatalen = 0;
struct edns_option* opt;
if(!edns || !edns->edns_present) if(!edns || !edns->edns_present)
return 0; return 0;
/* domain root '.' + type + class + ttl + rdatalen(=0) */ for(opt = edns->opt_list; opt; opt = opt->next) {
return 1 + 2 + 2 + 4 + 2; rdatalen += 4 + opt->opt_len;
}
/* domain root '.' + type + class + ttl + rdatalen */
return 1 + 2 + 2 + 4 + 2 + rdatalen;
} }
void void
attach_edns_record(sldns_buffer* pkt, struct edns_data* edns) attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
{ {
size_t len; size_t len;
size_t rdatapos;
struct edns_option* opt;
if(!edns || !edns->edns_present) if(!edns || !edns->edns_present)
return; return;
/* inc additional count */ /* inc additional count */
@ -742,7 +749,18 @@ attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */ sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
sldns_buffer_write_u8(pkt, edns->edns_version); sldns_buffer_write_u8(pkt, edns->edns_version);
sldns_buffer_write_u16(pkt, edns->bits); sldns_buffer_write_u16(pkt, edns->bits);
rdatapos = sldns_buffer_position(pkt);
sldns_buffer_write_u16(pkt, 0); /* rdatalen */ 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); sldns_buffer_flip(pkt);
} }

View File

@ -38,6 +38,7 @@
*/ */
#include "config.h" #include "config.h"
#include "util/data/msgparse.h" #include "util/data/msgparse.h"
#include "util/data/msgreply.h"
#include "util/data/dname.h" #include "util/data/dname.h"
#include "util/data/packed_rrset.h" #include "util/data/packed_rrset.h"
#include "util/storage/lookup3.h" #include "util/storage/lookup3.h"
@ -933,13 +934,41 @@ parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region)
return 0; 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 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* rrset = msg->rrset_first;
struct rrset_parse* prev = 0; struct rrset_parse* prev = 0;
struct rrset_parse* found = 0; struct rrset_parse* found = 0;
struct rrset_parse* found_prev = 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 /* since the class encodes the UDP size, we cannot use hash table to
* find the EDNS OPT record. Scan the packet. */ * find the EDNS OPT record. Scan the packet. */
while(rrset) { while(rrset) {
@ -986,13 +1015,25 @@ parse_extract_edns(struct msg_parse* msg, struct edns_data* edns)
edns->edns_version = found->rr_last->ttl_data[1]; edns->edns_version = found->rr_last->ttl_data[1];
edns->bits = sldns_read_uint16(&found->rr_last->ttl_data[2]); edns->bits = sldns_read_uint16(&found->rr_last->ttl_data[2]);
edns->udp_size = ntohs(found->rrset_class); 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; return 0;
} }
int 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_QDCOUNT(sldns_buffer_begin(pkt)) == 1);
log_assert(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0); log_assert(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0);
log_assert(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) == 0); log_assert(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) == 0);
@ -1017,6 +1058,17 @@ parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns)
edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */ edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */
edns->edns_version = sldns_buffer_read_u8(pkt); edns->edns_version = sldns_buffer_read_u8(pkt);
edns->bits = sldns_buffer_read_u16(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; return 0;
} }

View File

@ -69,6 +69,7 @@ struct sldns_buffer;
struct rrset_parse; struct rrset_parse;
struct rr_parse; struct rr_parse;
struct regional; struct regional;
struct edns_option;
/** number of buckets in parse rrset hash table. Must be power of 2. */ /** number of buckets in parse rrset hash table. Must be power of 2. */
#define PARSE_TABLE_SIZE 32 #define PARSE_TABLE_SIZE 32
@ -202,7 +203,8 @@ struct rr_parse {
/** /**
* EDNS data storage * EDNS data storage
* EDNS rdata is ignored. * rdata is parsed in a list (has accessor functions). allocated in a
* region.
*/ */
struct edns_data { struct edns_data {
/** if EDNS OPT record was present */ /** if EDNS OPT record was present */
@ -215,6 +217,22 @@ struct edns_data {
uint16_t bits; uint16_t bits;
/** UDP reassembly size. */ /** UDP reassembly size. */
uint16_t udp_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;
}; };
/** /**
@ -249,10 +267,12 @@ int parse_packet(struct sldns_buffer* pkt, struct msg_parse* msg,
* @param msg: parsed message structure. Modified on exit, if EDNS was present * @param msg: parsed message structure. Modified on exit, if EDNS was present
* it is removed from the additional section. * it is removed from the additional section.
* @param edns: the edns data is stored here. Does not have to be initialised. * @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. * @return: 0 on success. or an RCODE on an error.
* RCODE formerr if OPT in wrong section, and so on. * 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. * If EDNS data follows a query section, extract it and initialize edns struct.
@ -260,10 +280,12 @@ int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns);
* section. At end, right after EDNS data or no movement if failed. * 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 * @param edns: the edns data allocated by the caller. Does not have to be
* initialised. * initialised.
* @param region: region to alloc results in (edns option contents)
* @return: 0 on success, or an RCODE on error. * @return: 0 on success, or an RCODE on error.
* RCODE formerr if OPT is badly formatted and so on. * 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. * Calculate hash value for rrset in packet.

View File

@ -461,7 +461,7 @@ int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc,
if((ret = parse_packet(pkt, msg, region)) != 0) { if((ret = parse_packet(pkt, msg, region)) != 0) {
return ret; return ret;
} }
if((ret = parse_extract_edns(msg, edns)) != 0) if((ret = parse_extract_edns(msg, edns, region)) != 0)
return ret; return ret;
/* parse OK, allocate return structures */ /* parse OK, allocate return structures */
@ -857,3 +857,88 @@ reply_all_rrsets_secure(struct reply_info* rep)
} }
return 1; 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;
}

View File

@ -437,4 +437,33 @@ void log_dns_msg(const char* str, struct query_info* qinfo,
void log_query_info(enum verbosity_value v, const char* str, void log_query_info(enum verbosity_value v, const char* str,
struct query_info* qinf); 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 */ #endif /* UTIL_DATA_MSGREPLY_H */

View File

@ -2333,6 +2333,7 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp)
edns.ext_rcode = 0; edns.ext_rcode = 0;
edns.edns_version = 0; edns.edns_version = 0;
edns.bits = EDNS_DO; edns.bits = EDNS_DO;
edns.opt_list = NULL;
if(sldns_buffer_capacity(buf) < 65535) if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535; else edns.udp_size = 65535;