]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- generic edns option parse and store code.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 31 May 2016 15:08:05 +0000 (15:08 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 31 May 2016 15:08:05 +0000 (15:08 +0000)
git-svn-id: file:///svn/unbound/trunk@3740 be551aaa-1e26-0410-a405-d3ace91eadb9

14 files changed:
cachedb/cachedb.c
daemon/worker.c
doc/Changelog
iterator/iterator.c
pythonmod/pythonmod_utils.c
services/localzone.c
services/mesh.c
testcode/fake_event.c
util/data/msgencode.c
util/data/msgparse.c
util/data/msgparse.h
util/data/msgreply.c
util/data/msgreply.h
validator/autotrust.c

index 3ddd72aef4f3450b23843a3c486b9f1467a16194..76d9afa47c8fc282e4159b53c85b9e78177c5f2f 100644 (file)
@@ -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;
        }
index a74931f02c9ba52bf208322ed1e448a8be6281a6..b08474fb1e873193083f84ace4d9a27e3558e33b 100644 (file)
@@ -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;
 
index a8efc359af8ee5eb97c1fbdae765005657e22824..6293ce7aa0fddb4c0f6ffb31b124f9d08d957182 100644 (file)
@@ -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().
index f521940a739724de35df9737333597af45ae49fd..bcce7e6fc272ad73639cd67c4f19e15025d1c6d2 100644 (file)
@@ -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;
index 5120074e839a6caaa24ca5a9c7aca8a6baa0437a..ae694d57693a0c6e061c2ea7eb4527a75b6f8882 100644 (file)
@@ -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 */
index 3c966243d165b70485e0824f3f6f9b39fa999f45..fcf6e8dfd7e03a9f941f31258d1fd7dd4fcb4b13 100644 (file)
@@ -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, 
index 8076874ae75207c7caab11621d97c71d851b104c..2df5ad62f716575b51a9dc9aef77da460b2ed405 100644 (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);
                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;
-
 }
 
 /**
index c786bded39083cf7461f448456324827bf38b4b4..d50f8b03cc2e9b551c5f7d211c91fcba86f2b777 100644 (file)
@@ -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);
index 43464e9bbe0c9289e7bc4905fb1cb901be4ed9f0..034bb24bd6e4fcdeb616a0fe13f077d480b4eb31 100644 (file)
@@ -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);
 }
 
index 108c9dacb39bc5b2973fc2034d1abab62eb05242..1d565c1ea280d818f7e194f5f276647617292211 100644 (file)
@@ -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;
 }
index 44497c8ca381b3f1bbbe9787b7b3033ae5a9d9fb..cae988ff9950f6326a907f56e8de50b9553923bb 100644 (file)
@@ -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.
index 06593ffe1b27af71e2b5864f0eb20bf08573a39f..4cfc37d7142c07ba88c3e87838bc5fdc32ca3256 100644 (file)
@@ -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;
+}
index 708897950089cd28001e8c4ecbaf3205313fb2b9..32a822706a7b686df2aa22d640e4178d3aad96fb 100644 (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, 
        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 */
index f8c9c8c63f9416382791b38158c517d72a40e214..232397c31069f821f4158af98aa1249a653e4b38 100644 (file)
@@ -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;