diff options
author | Erik de Castro Lopo <erikd@mega-nerd.com> | 2017-06-16 20:16:05 +1000 |
---|---|---|
committer | Erik de Castro Lopo <erikd@mega-nerd.com> | 2017-06-17 23:04:00 +1000 |
commit | a85b5759f34c0c4110a479a8b5fa606f15ed9b23 (patch) | |
tree | 518cb8346249a42fd2aa8a78c09c3631e14db6aa /external/unbound/services/mesh.c | |
parent | Merge pull request #2059 (diff) | |
download | monero-a85b5759f34c0c4110a479a8b5fa606f15ed9b23.tar.xz |
Upgrade unbound library
These files were pulled from the 1.6.3 release tarball.
This new version builds against OpenSSL version 1.1 which will be
the default in the new Debian Stable which is due to be released
RealSoonNow (tm).
Diffstat (limited to '')
-rw-r--r-- | external/unbound/services/mesh.c | 364 |
1 files changed, 325 insertions, 39 deletions
diff --git a/external/unbound/services/mesh.c b/external/unbound/services/mesh.c index 8076874ae..0cb134ade 100644 --- a/external/unbound/services/mesh.c +++ b/external/unbound/services/mesh.c @@ -56,6 +56,10 @@ #include "util/alloc.h" #include "util/config_file.h" #include "sldns/sbuffer.h" +#include "sldns/wire2str.h" +#include "services/localzone.h" +#include "util/data/dname.h" +#include "respip/respip.h" /** subtract timers and the values do not overflow or become negative */ static void @@ -121,11 +125,69 @@ timeval_smaller(const struct timeval* x, const struct timeval* y) #endif } +/* + * Compare two response-ip client info entries for the purpose of mesh state + * compare. It returns 0 if ci_a and ci_b are considered equal; otherwise + * 1 or -1 (they mean 'ci_a is larger/smaller than ci_b', respectively, but + * in practice it should be only used to mean they are different). + * We cannot share the mesh state for two queries if different response-ip + * actions can apply in the end, even if those queries are otherwise identical. + * For this purpose we compare tag lists and tag action lists; they should be + * identical to share the same state. + * For tag data, we don't look into the data content, as it can be + * expensive; unless tag data are not defined for both or they point to the + * exact same data in memory (i.e., they come from the same ACL entry), we + * consider these data different. + * Likewise, if the client info is associated with views, we don't look into + * the views. They are considered different unless they are exactly the same + * even if the views only differ in the names. + */ +static int +client_info_compare(const struct respip_client_info* ci_a, + const struct respip_client_info* ci_b) +{ + int cmp; + + if(!ci_a && !ci_b) + return 0; + if(ci_a && !ci_b) + return -1; + if(!ci_a && ci_b) + return 1; + if(ci_a->taglen != ci_b->taglen) + return (ci_a->taglen < ci_b->taglen) ? -1 : 1; + cmp = memcmp(ci_a->taglist, ci_b->taglist, ci_a->taglen); + if(cmp != 0) + return cmp; + if(ci_a->tag_actions_size != ci_b->tag_actions_size) + return (ci_a->tag_actions_size < ci_b->tag_actions_size) ? + -1 : 1; + cmp = memcmp(ci_a->tag_actions, ci_b->tag_actions, + ci_a->tag_actions_size); + if(cmp != 0) + return cmp; + if(ci_a->tag_datas != ci_b->tag_datas) + return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1; + if(ci_a->view != ci_b->view) + return ci_a->view < ci_b->view ? -1 : 1; + /* For the unbound daemon these should be non-NULL and identical, + * but we check that just in case. */ + if(ci_a->respip_set != ci_b->respip_set) + return ci_a->respip_set < ci_b->respip_set ? -1 : 1; + return 0; +} + int mesh_state_compare(const void* ap, const void* bp) { struct mesh_state* a = (struct mesh_state*)ap; struct mesh_state* b = (struct mesh_state*)bp; + int cmp; + + if(a->unique < b->unique) + return -1; + if(a->unique > b->unique) + return 1; if(a->s.is_priming && !b->s.is_priming) return -1; @@ -147,7 +209,10 @@ mesh_state_compare(const void* ap, const void* bp) if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD)) return 1; - return query_info_compare(&a->s.qinfo, &b->s.qinfo); + cmp = query_info_compare(&a->s.qinfo, &b->s.qinfo); + if(cmp != 0) + return cmp; + return client_info_compare(a->s.client_info, b->s.client_info); } int @@ -195,7 +260,7 @@ mesh_create(struct module_stack* stack, struct module_env* env) /** help mesh delete delete mesh states */ static void -mesh_delete_helper(rbnode_t* n) +mesh_delete_helper(rbnode_type* n) { struct mesh_state* mstate = (struct mesh_state*)n->key; /* perform a full delete, not only 'cleanup' routine, @@ -279,13 +344,16 @@ int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf) } void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, struct edns_data* edns, struct comm_reply* rep, - uint16_t qid) + struct respip_client_info* cinfo, uint16_t qflags, + struct edns_data* edns, struct comm_reply* rep, uint16_t qid) { - struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + struct mesh_state* s = NULL; + int unique = unique_mesh_state(edns->opt_list, mesh->env); int was_detached = 0; int was_noreply = 0; int added = 0; + if(!unique) + s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); /* does this create a new reply state? */ if(!s || s->list_select == mesh_no_list) { if(!mesh_make_new_space(mesh, rep->c->buffer)) { @@ -310,16 +378,38 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, /* see if it already exists, if not, create one */ if(!s) { #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + s = mesh_state_create(mesh->env, qinfo, cinfo, + qflags&(BIT_RD|BIT_CD), 0, 0); if(!s) { log_err("mesh_state_create: out of memory; SERVFAIL"); + if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL, + LDNS_RCODE_SERVFAIL, 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); return; } + if(unique) + mesh_state_make_unique(s); + /* copy the edns options we got from the front */ + if(edns->opt_list) { + s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list, + s->s.region); + if(!s->s.edns_opts_front_in) { + log_err("mesh_state_create: out of memory; SERVFAIL"); + if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, + NULL, LDNS_RCODE_SERVFAIL, 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); + return; + } + } + #ifdef UNBOUND_DEBUG n = #else @@ -336,8 +426,11 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, if(!s->reply_list && !s->cb_list) was_noreply = 1; /* 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)) { log_err("mesh_new_client: out of memory; SERVFAIL"); + if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s, + NULL, LDNS_RCODE_SERVFAIL, 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); @@ -374,23 +467,37 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, struct edns_data* edns, sldns_buffer* buf, - uint16_t qid, mesh_cb_func_t cb, void* cb_arg) + uint16_t qid, mesh_cb_func_type cb, void* cb_arg) { - struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + struct mesh_state* s = NULL; + int unique = unique_mesh_state(edns->opt_list, mesh->env); int was_detached = 0; int was_noreply = 0; int added = 0; + if(!unique) + s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + /* there are no limits on the number of callbacks */ /* see if it already exists, if not, create one */ if(!s) { #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + s = mesh_state_create(mesh->env, qinfo, NULL, + qflags&(BIT_RD|BIT_CD), 0, 0); if(!s) { return 0; } + if(unique) + mesh_state_make_unique(s); + if(edns->opt_list) { + s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list, + s->s.region); + if(!s->s.edns_opts_front_in) { + return 0; + } + } #ifdef UNBOUND_DEBUG n = #else @@ -429,9 +536,10 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, time_t leeway) { - struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo, + qflags&(BIT_RD|BIT_CD), 0, 0); #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif /* already exists, and for a different purpose perhaps. * if mesh_no_list, keep it that way. */ @@ -448,7 +556,9 @@ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, mesh->stats_dropped ++; return; } - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + + s = mesh_state_create(mesh->env, qinfo, NULL, + qflags&(BIT_RD|BIT_CD), 0, 0); if(!s) { log_err("prefetch mesh_state_create: out of memory"); return; @@ -497,7 +607,8 @@ void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, struct mesh_state* mesh_state_create(struct module_env* env, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec) + struct respip_client_info* cinfo, uint16_t qflags, int prime, + int valrec) { struct regional* region = alloc_reg_obtain(env->alloc); struct mesh_state* mstate; @@ -521,9 +632,11 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo, rbtree_init(&mstate->super_set, &mesh_state_ref_compare); rbtree_init(&mstate->sub_set, &mesh_state_ref_compare); mstate->num_activated = 0; + mstate->unique = NULL; /* init module qstate */ mstate->s.qinfo.qtype = qinfo->qtype; mstate->s.qinfo.qclass = qinfo->qclass; + mstate->s.qinfo.local_alias = NULL; mstate->s.qinfo.qname_len = qinfo->qname_len; mstate->s.qinfo.qname = regional_alloc_init(region, qinfo->qname, qinfo->qname_len); @@ -531,6 +644,14 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo, alloc_reg_release(env->alloc, region); return NULL; } + if(cinfo) { + mstate->s.client_info = regional_alloc_init(region, cinfo, + sizeof(*cinfo)); + if(!mstate->s.client_info) { + alloc_reg_release(env->alloc, region); + return NULL; + } + } /* remove all weird bits from qflags */ mstate->s.query_flags = (qflags & (BIT_RD|BIT_CD)); mstate->s.is_priming = prime; @@ -543,14 +664,34 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo, mstate->s.env = env; mstate->s.mesh_info = mstate; mstate->s.prefetch_leeway = 0; + mstate->s.no_cache_lookup = 0; + mstate->s.no_cache_store = 0; /* init modules */ for(i=0; i<env->mesh->mods.num; i++) { mstate->s.minfo[i] = NULL; mstate->s.ext_state[i] = module_state_initial; } + /* init edns option lists */ + mstate->s.edns_opts_front_in = NULL; + mstate->s.edns_opts_back_out = NULL; + mstate->s.edns_opts_back_in = NULL; + mstate->s.edns_opts_front_out = NULL; + return mstate; } +int +mesh_state_is_unique(struct mesh_state* mstate) +{ + return mstate->unique != NULL; +} + +void +mesh_state_make_unique(struct mesh_state* mstate) +{ + mstate->unique = mstate; +} + void mesh_state_cleanup(struct mesh_state* mstate) { @@ -658,7 +799,7 @@ void mesh_detach_subs(struct module_qstate* qstate) struct mesh_area* mesh = qstate->env->mesh; struct mesh_state_ref* ref, lookup; #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif lookup.node.key = &lookup; lookup.s = qstate->mesh_info; @@ -685,8 +826,8 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, { /* find it, if not, create it */ struct mesh_area* mesh = qstate->env->mesh; - struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime, - valrec); + struct mesh_state* sub = mesh_area_find(mesh, NULL, qinfo, qflags, + prime, valrec); int was_detached; if(mesh_detect_cycle_found(qstate, sub)) { verbose(VERB_ALGO, "attach failed, cycle detected"); @@ -694,10 +835,10 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, } if(!sub) { #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif /* create a new one */ - sub = mesh_state_create(qstate->env, qinfo, qflags, prime, + sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime, valrec); if(!sub) { log_err("mesh_attach_sub: out of memory"); @@ -740,7 +881,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub) { #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif struct mesh_state_ref* subref; /* points to sub, inserted in super */ struct mesh_state_ref* superref; /* points to super, inserted in sub */ @@ -800,6 +941,15 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, } /* send the reply */ if(rcode) { + if(rcode == LDNS_RCODE_SERVFAIL) { + if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, + rep, rcode, &r->edns, m->s.region)) + r->edns.opt_list = NULL; + } else { + if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, + &r->edns, m->s.region)) + r->edns.opt_list = NULL; + } fptr_ok(fptr_whitelist_mesh_cb(r->cb)); (*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL); } else { @@ -809,7 +959,10 @@ 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(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, + LDNS_RCODE_NOERROR, &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)) @@ -842,6 +995,9 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, struct timeval end_time; struct timeval duration; int secure; + /* Copy the client's EDNS for later restore, to make sure the edns + * compare is with the correct edns options. */ + struct edns_data edns_bak = r->edns; /* examine security status */ if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) || m->s.env->cfg->ignore_cd) && rep && @@ -856,10 +1012,18 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, if(!rep && rcode == LDNS_RCODE_NOERROR) rcode = LDNS_RCODE_SERVFAIL; /* send the reply */ + /* We don't reuse the encoded answer if either the previous or current + * response has a local alias. We could compare the alias records + * and still reuse the previous answer if they are the same, but that + * would be complicated and error prone for the relatively minor case. + * So we err on the side of safety. */ if(prev && prev->qflags == r->qflags && + !prev->local_alias && !r->local_alias && 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_compare(prev->edns.opt_list, r->edns.opt_list) + == 0) { /* 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, @@ -871,6 +1035,16 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, comm_point_send_reply(&r->query_reply); } else if(rcode) { m->s.qinfo.qname = r->qname; + m->s.qinfo.local_alias = r->local_alias; + if(rcode == LDNS_RCODE_SERVFAIL) { + if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, + rep, rcode, &r->edns, m->s.region)) + r->edns.opt_list = NULL; + } else { + if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, + &r->edns, m->s.region)) + r->edns.opt_list = NULL; + } error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo, r->qid, r->qflags, &r->edns); comm_point_send_reply(&r->query_reply); @@ -881,15 +1055,22 @@ 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, + m->s.qinfo.local_alias = r->local_alias; + if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, + LDNS_RCODE_NOERROR, &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)) { + if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, + rep, LDNS_RCODE_SERVFAIL, &r->edns, m->s.region)) + r->edns.opt_list = NULL; error_encode(r->query_reply.c->buffer, LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, r->qflags, &r->edns); } + r->edns = edns_bak; comm_point_send_reply(&r->query_reply); } /* account */ @@ -910,6 +1091,12 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, query_reply.c->buffer)) == 0) m->s.env->mesh->ans_nodata++; } + /* Log reply sent */ + if(m->s.env->cfg->log_replies) { + log_reply_info(0, &m->s.qinfo, &r->query_reply.addr, + r->query_reply.addrlen, duration, 0, + r->query_reply.c->buffer); + } } void mesh_query_done(struct mesh_state* mstate) @@ -920,8 +1107,25 @@ void mesh_query_done(struct mesh_state* mstate) struct reply_info* rep = (mstate->s.return_msg? mstate->s.return_msg->rep:NULL); for(r = mstate->reply_list; r; r = r->next) { - mesh_send_reply(mstate, mstate->s.return_rcode, rep, r, prev); - prev = r; + /* if a response-ip address block has been stored the + * information should be logged for each client. */ + if(mstate->s.respip_action_info && + mstate->s.respip_action_info->addrinfo) { + respip_inform_print(mstate->s.respip_action_info->addrinfo, + r->qname, mstate->s.qinfo.qtype, + mstate->s.qinfo.qclass, r->local_alias, + &r->query_reply); + } + + /* if this query is determined to be dropped during the + * mesh processing, this is the point to take that action. */ + if(mstate->s.is_drop) + comm_point_drop_reply(&r->query_reply); + else { + mesh_send_reply(mstate, mstate->s.return_rcode, rep, + r, prev); + prev = r; + } } mstate->replies_sent = 1; for(c = mstate->cb_list; c; c = c->next) { @@ -945,7 +1149,8 @@ void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate) } struct mesh_state* mesh_area_find(struct mesh_area* mesh, - struct query_info* qinfo, uint16_t qflags, int prime, int valrec) + struct respip_client_info* cinfo, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec) { struct mesh_state key; struct mesh_state* result; @@ -955,13 +1160,18 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh, key.s.is_valrec = valrec; key.s.qinfo = *qinfo; key.s.query_flags = qflags; + /* We are searching for a similar mesh state when we DO want to + * aggregate the state. Thus unique is set to NULL. (default when we + * desire aggregation).*/ + key.unique = NULL; + key.s.client_info = cinfo; result = (struct mesh_state*)rbtree_search(&mesh->all, &key); return result; } int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, - sldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg, + sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg, uint16_t qid, uint16_t qflags) { struct mesh_cb* r = regional_alloc(s->s.region, @@ -973,6 +1183,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; @@ -982,7 +1198,8 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, } int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, - struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname) + struct comm_reply* rep, uint16_t qid, uint16_t qflags, + const struct query_info* qinfo) { struct mesh_reply* r = regional_alloc(s->s.region, sizeof(struct mesh_reply)); @@ -990,17 +1207,74 @@ 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; r->next = s->reply_list; - r->qname = regional_alloc_init(s->s.region, qname, + r->qname = regional_alloc_init(s->s.region, qinfo->qname, s->s.qinfo.qname_len); if(!r->qname) return 0; + + /* Data related to local alias stored in 'qinfo' (if any) is ephemeral + * and can be different for different original queries (even if the + * replaced query name is the same). So we need to make a deep copy + * and store the copy for each reply info. */ + if(qinfo->local_alias) { + struct packed_rrset_data* d; + struct packed_rrset_data* dsrc; + r->local_alias = regional_alloc_zero(s->s.region, + sizeof(*qinfo->local_alias)); + if(!r->local_alias) + return 0; + r->local_alias->rrset = regional_alloc_init(s->s.region, + qinfo->local_alias->rrset, + sizeof(*qinfo->local_alias->rrset)); + if(!r->local_alias->rrset) + return 0; + dsrc = qinfo->local_alias->rrset->entry.data; + + /* In the current implementation, a local alias must be + * a single CNAME RR (see worker_handle_request()). */ + log_assert(!qinfo->local_alias->next && dsrc->count == 1 && + qinfo->local_alias->rrset->rk.type == + htons(LDNS_RR_TYPE_CNAME)); + /* Technically, we should make a local copy for the owner + * name of the RRset, but in the case of the first (and + * currently only) local alias RRset, the owner name should + * point to the qname of the corresponding query, which should + * be valid throughout the lifetime of this mesh_reply. So + * we can skip copying. */ + log_assert(qinfo->local_alias->rrset->rk.dname == + sldns_buffer_at(rep->c->buffer, LDNS_HEADER_SIZE)); + + d = regional_alloc_init(s->s.region, dsrc, + sizeof(struct packed_rrset_data) + + sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t)); + if(!d) + return 0; + r->local_alias->rrset->entry.data = d; + d->rr_len = (size_t*)((uint8_t*)d + + sizeof(struct packed_rrset_data)); + d->rr_data = (uint8_t**)&(d->rr_len[1]); + d->rr_ttl = (time_t*)&(d->rr_data[1]); + d->rr_len[0] = dsrc->rr_len[0]; + d->rr_ttl[0] = dsrc->rr_ttl[0]; + d->rr_data[0] = regional_alloc_init(s->s.region, + dsrc->rr_data[0], d->rr_len[0]); + if(!d->rr_data[0]) + return 0; + } else + r->local_alias = NULL; + s->reply_list = r; return 1; - } /** @@ -1041,15 +1315,26 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, return mesh_continue(mesh, mstate, module_error, ev); } if(s == module_restart_next) { - fptr_ok(fptr_whitelist_mod_clear( - mesh->mods.mod[mstate->s.curmod]->clear)); - (*mesh->mods.mod[mstate->s.curmod]->clear) - (&mstate->s, mstate->s.curmod); - mstate->s.minfo[mstate->s.curmod] = NULL; + int curmod = mstate->s.curmod; + for(; mstate->s.curmod < mesh->mods.num; + mstate->s.curmod++) { + fptr_ok(fptr_whitelist_mod_clear( + mesh->mods.mod[mstate->s.curmod]->clear)); + (*mesh->mods.mod[mstate->s.curmod]->clear) + (&mstate->s, mstate->s.curmod); + mstate->s.minfo[mstate->s.curmod] = NULL; + } + mstate->s.curmod = curmod; } *ev = module_event_pass; return 1; } + if(s == module_wait_subquery && mstate->sub_set.count == 0) { + log_err("module cannot wait for subquery, subquery list empty"); + log_query_info(VERB_QUERY, "pass error for qstate", + &mstate->s.qinfo); + s = module_error; + } if(s == module_error && mstate->s.return_rcode == LDNS_RCODE_NOERROR) { /* error is bad, handle pass back up below */ mstate->s.return_rcode = LDNS_RCODE_SERVFAIL; @@ -1187,8 +1472,9 @@ mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo, uint16_t flags, int prime, int valrec) { struct mesh_area* mesh = qstate->env->mesh; - struct mesh_state* dep_m = mesh_area_find(mesh, qinfo, flags, prime, - valrec); + struct mesh_state* dep_m = NULL; + if(!mesh_state_is_unique(qstate->mesh_info)) + dep_m = mesh_area_find(mesh, NULL, qinfo, flags, prime, valrec); return mesh_detect_cycle_found(qstate, dep_m); } |