From: Wouter Wijngaards Date: Tue, 31 May 2016 15:08:05 +0000 (+0000) Subject: - generic edns option parse and store code. X-Git-Tag: release-1.5.9rc1~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=40dd2acfd9a1409bbbf59ff00f0796dd48f8f378;p=thirdparty%2Funbound.git - generic edns option parse and store code. git-svn-id: file:///svn/unbound/trunk@3740 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index 3ddd72aef..76d9afa47 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -406,7 +406,8 @@ parse_data(struct module_qstate* qstate, struct sldns_buffer* buf) 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; } diff --git a/daemon/worker.c b/daemon/worker.c index a74931f02..b08474fb1 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -483,7 +483,6 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, 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) { @@ -491,7 +490,6 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, 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 */ @@ -499,9 +497,10 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, 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]++; @@ -527,6 +526,8 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, 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, @@ -534,7 +535,6 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, 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); @@ -574,7 +574,6 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, bail_out: rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); - regional_free_all(worker->scratchpad); return 0; } } @@ -585,11 +584,12 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, 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] ++; @@ -616,6 +616,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, 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)) { @@ -626,7 +628,6 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, * 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); @@ -660,7 +661,8 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo, * @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)); @@ -689,6 +691,8 @@ chaos_replystr(sldns_buffer* pkt, const char* str, struct edns_data* edns) 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); } @@ -718,13 +722,13 @@ answer_chaos(struct worker* w, struct query_info* qinfo, 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, @@ -735,8 +739,8 @@ answer_chaos(struct worker* w, struct query_info* qinfo, 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; @@ -865,7 +869,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, } 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); @@ -876,6 +880,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, 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; } @@ -884,12 +889,14 @@ worker_handle_request(struct comm_point* c, void* arg, int error, 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 && @@ -918,6 +925,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, 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) @@ -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, &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, @@ -945,6 +954,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, * 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; @@ -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_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); @@ -984,9 +995,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error, 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"); @@ -997,6 +1010,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, *(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 -- " @@ -1017,6 +1031,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, 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; diff --git a/doc/Changelog b/doc/Changelog index a8efc359a..6293ce7aa 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,7 @@ - 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(). diff --git a/iterator/iterator.c b/iterator/iterator.c index f521940a7..bcce7e6fc 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -3124,7 +3124,8 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, 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; diff --git a/pythonmod/pythonmod_utils.c b/pythonmod/pythonmod_utils.c index 5120074e8..ae694d576 100644 --- a/pythonmod/pythonmod_utils.c +++ b/pythonmod/pythonmod_utils.c @@ -129,7 +129,8 @@ int createResponse(struct module_qstate* qstate, sldns_buffer* pkt) 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 */ diff --git a/services/localzone.c b/services/localzone.c index 3c966243d..fcf6e8dfd 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1042,7 +1042,8 @@ local_encode(struct query_info* qinfo, struct edns_data* edns, 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, diff --git a/services/mesh.c b/services/mesh.c index 8076874ae..2df5ad62f 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -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); 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); @@ -338,6 +340,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, /* 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); @@ -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.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)) @@ -859,7 +864,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, 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, @@ -881,7 +887,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, 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)) @@ -973,6 +980,12 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, 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; @@ -990,6 +1003,12 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, 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; @@ -1000,7 +1019,6 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, return 0; s->reply_list = r; return 1; - } /** diff --git a/testcode/fake_event.c b/testcode/fake_event.c index c786bded3..d50f8b03c 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1077,6 +1077,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, 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); diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 43464e9bb..034bb24bd 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -717,16 +717,23 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, 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 */ @@ -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->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); } diff --git a/util/data/msgparse.c b/util/data/msgparse.c index 108c9dacb..1d565c1ea 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -38,6 +38,7 @@ */ #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" @@ -933,13 +934,41 @@ parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region) 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) { @@ -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->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); @@ -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->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; } diff --git a/util/data/msgparse.h b/util/data/msgparse.h index 44497c8ca..cae988ff9 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -69,6 +69,7 @@ struct sldns_buffer; 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 @@ -202,7 +203,8 @@ struct rr_parse { /** * 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 */ @@ -215,6 +217,22 @@ 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; +}; + +/** + * 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 * 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. @@ -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. * @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. diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 06593ffe1..4cfc37d71 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -461,7 +461,7 @@ 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)) != 0) + if((ret = parse_extract_edns(msg, edns, region)) != 0) return ret; /* parse OK, allocate return structures */ @@ -857,3 +857,88 @@ reply_all_rrsets_secure(struct reply_info* rep) } 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; +} diff --git a/util/data/msgreply.h b/util/data/msgreply.h index 708897950..32a822706 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -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, 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 */ diff --git a/validator/autotrust.c b/validator/autotrust.c index f8c9c8c63..232397c31 100644 --- a/validator/autotrust.c +++ b/validator/autotrust.c @@ -2333,6 +2333,7 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp) 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;