diff options
author | Cy Schubert <cy@FreeBSD.org> | 2022-06-08 14:43:13 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2022-06-08 14:43:13 +0000 |
commit | 5f9f82264b91e041df7cba2406625146e7268ce4 (patch) | |
tree | ba7309ee547bf22115420277f45a3478aafb6397 /services/mesh.c | |
parent | 3574dc0bd83e731bba79edc130c0569bf05f7af5 (diff) | |
download | src-5f9f82264b91e041df7cba2406625146e7268ce4.tar.gz src-5f9f82264b91e041df7cba2406625146e7268ce4.zip |
unbound: Vendor import 1.16.0vendor/unbound/1.16.0
Diffstat (limited to 'services/mesh.c')
-rw-r--r-- | services/mesh.c | 181 |
1 files changed, 169 insertions, 12 deletions
diff --git a/services/mesh.c b/services/mesh.c index cdcfedda270c..fbaa966bdd05 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -64,6 +64,11 @@ #include "respip/respip.h" #include "services/listen_dnsport.h" +#ifdef CLIENT_SUBNET +#include "edns-subnet/subnetmod.h" +#include "edns-subnet/edns-subnet.h" +#endif + /** subtract timers and the values do not overflow or become negative */ static void timeval_subtract(struct timeval* d, const struct timeval* end, const struct timeval* start) @@ -458,7 +463,8 @@ mesh_serve_expired_init(struct mesh_state* mstate, int timeout) void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, struct respip_client_info* cinfo, uint16_t qflags, - struct edns_data* edns, struct comm_reply* rep, uint16_t qid) + struct edns_data* edns, struct comm_reply* rep, uint16_t qid, + int rpz_passthru) { struct mesh_state* s = NULL; int unique = unique_mesh_state(edns->opt_list_in, mesh->env); @@ -513,6 +519,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, } if(unique) mesh_state_make_unique(s); + s->s.rpz_passthru = rpz_passthru; /* copy the edns options we got from the front */ if(edns->opt_list_in) { s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in, @@ -606,7 +613,7 @@ servfail_mem: 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_type cb, void* cb_arg) + uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru) { struct mesh_state* s = NULL; int unique = unique_mesh_state(edns->opt_list_in, mesh->env); @@ -632,6 +639,7 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, } if(unique) mesh_state_make_unique(s); + s->s.rpz_passthru = rpz_passthru; if(edns->opt_list_in) { s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in, s->s.region); @@ -686,7 +694,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, * 0 (false), in which case the new state is only made runnable so it * will not be run recursively on top of the current state. */ static void mesh_schedule_prefetch(struct mesh_area* mesh, - struct query_info* qinfo, uint16_t qflags, time_t leeway, int run) + struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, + int rpz_passthru) { struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); @@ -732,15 +741,109 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh, /* move to either the forever or the jostle_list */ if(mesh->num_forever_states < mesh->max_forever_states) { mesh->num_forever_states ++; - mesh_list_insert(s, &mesh->forever_first, + mesh_list_insert(s, &mesh->forever_first, &mesh->forever_last); s->list_select = mesh_forever_list; } else { - mesh_list_insert(s, &mesh->jostle_first, + mesh_list_insert(s, &mesh->jostle_first, + &mesh->jostle_last); + s->list_select = mesh_jostle_list; + } + } + s->s.rpz_passthru = rpz_passthru; + + if(!run) { +#ifdef UNBOUND_DEBUG + n = +#else + (void) +#endif + rbtree_insert(&mesh->run, &s->run_node); + log_assert(n != NULL); + return; + } + + mesh_run(mesh, s, module_event_new, NULL); +} + +#ifdef CLIENT_SUBNET +/* Same logic as mesh_schedule_prefetch but tailored to the subnet module logic + * like passing along the comm_reply info. This will be faked into an EDNS + * option for processing by the subnet module if the client has not already + * attached its own ECS data. */ +static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, + struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, + int rpz_passthru, struct comm_reply* rep, struct edns_option* edns_list) +{ + struct mesh_state* s = NULL; + struct edns_option* opt = NULL; +#ifdef UNBOUND_DEBUG + struct rbnode_type* n; +#endif + if(!mesh_make_new_space(mesh, NULL)) { + verbose(VERB_ALGO, "Too many queries. dropped prefetch."); + mesh->stats_dropped ++; + return; + } + + s = mesh_state_create(mesh->env, qinfo, NULL, + qflags&(BIT_RD|BIT_CD), 0, 0); + if(!s) { + log_err("prefetch_subnet mesh_state_create: out of memory"); + return; + } + mesh_state_make_unique(s); + + opt = edns_opt_list_find(edns_list, mesh->env->cfg->client_subnet_opcode); + if(opt) { + /* Use the client's ECS data */ + if(!edns_opt_list_append(&s->s.edns_opts_front_in, opt->opt_code, + opt->opt_len, opt->opt_data, s->s.region)) { + log_err("prefetch_subnet edns_opt_list_append: out of memory"); + return; + } + } else { + /* Fake the ECS data from the client's IP */ + struct ecs_data ecs; + memset(&ecs, 0, sizeof(ecs)); + subnet_option_from_ss(&rep->addr, &ecs, mesh->env->cfg); + if(ecs.subnet_validdata == 0) { + log_err("prefetch_subnet subnet_option_from_ss: invalid data"); + return; + } + subnet_ecs_opt_list_append(&ecs, &s->s.edns_opts_front_in, &s->s); + if(!s->s.edns_opts_front_in) { + log_err("prefetch_subnet subnet_ecs_opt_list_append: out of memory"); + return; + } + } +#ifdef UNBOUND_DEBUG + n = +#else + (void) +#endif + rbtree_insert(&mesh->all, &s->node); + log_assert(n != NULL); + /* set detached (it is now) */ + mesh->num_detached_states++; + /* make it ignore the cache */ + sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); + s->s.prefetch_leeway = leeway; + + if(s->list_select == mesh_no_list) { + /* move to either the forever or the jostle_list */ + if(mesh->num_forever_states < mesh->max_forever_states) { + mesh->num_forever_states ++; + mesh_list_insert(s, &mesh->forever_first, + &mesh->forever_last); + s->list_select = mesh_forever_list; + } else { + mesh_list_insert(s, &mesh->jostle_first, &mesh->jostle_last); s->list_select = mesh_jostle_list; } } + s->s.rpz_passthru = rpz_passthru; if(!run) { #ifdef UNBOUND_DEBUG @@ -755,11 +858,22 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh, mesh_run(mesh, s, module_event_new, NULL); } +#endif /* CLIENT_SUBNET */ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, time_t leeway) + uint16_t qflags, time_t leeway, int rpz_passthru, + struct comm_reply* rep, struct edns_option* opt_list) { - mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1); + (void)opt_list; + (void)rep; +#ifdef CLIENT_SUBNET + if(rep) + mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1, + rpz_passthru, rep, opt_list); + else +#endif + mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1, + rpz_passthru); } void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, @@ -1234,7 +1348,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, (rep->security <= sec_status_bogus || rep->security == sec_status_secure_sentinel_fail)) { rcode = LDNS_RCODE_SERVFAIL; - if(m->s.env->cfg->stat_extended) + if(m->s.env->cfg->stat_extended) m->s.env->mesh->ans_bogus++; } if(rep && rep->security == sec_status_secure) @@ -1290,6 +1404,36 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, &r->edns, &r->query_reply, m->s.region, &r->start_time)) r->edns.opt_list_inplace_cb_out = NULL; } + /* Send along EDE BOGUS EDNS0 option when answer is bogus */ + if(m->s.env->cfg->ede && rcode == LDNS_RCODE_SERVFAIL && + m->s.env->need_to_validate && (!(r->qflags&BIT_CD) || + m->s.env->cfg->ignore_cd) && rep && + (rep->security <= sec_status_bogus || + rep->security == sec_status_secure_sentinel_fail)) { + char *reason = m->s.env->cfg->val_log_level >= 2 + ? errinf_to_str_bogus(&m->s) : NULL; + + /* During validation the EDE code can be received via two + * code paths. One code path fills the reply_info EDE, and + * the other fills it in the errinf_strlist. These paths + * intersect at some points, but where is opaque due to + * the complexity of the validator. At the time of writing + * we make the choice to prefer the EDE from errinf_strlist + * but a compelling reason to do otherwise is just as valid + */ + sldns_ede_code reason_bogus = errinf_to_reason_bogus(&m->s); + if ((reason_bogus == LDNS_EDE_DNSSEC_BOGUS && + rep->reason_bogus != LDNS_EDE_NONE) || + reason_bogus == LDNS_EDE_NONE) { + reason_bogus = rep->reason_bogus; + } + + if(reason_bogus != LDNS_EDE_NONE) { + edns_opt_list_append_ede(&r->edns.opt_list_out, + m->s.region, reason_bogus, reason); + } + free(reason); + } error_encode(r_buffer, rcode, &m->s.qinfo, r->qid, r->qflags, &r->edns); m->reply_list = NULL; @@ -1313,6 +1457,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time)) r->edns.opt_list_inplace_cb_out = NULL; + /* internal server error (probably malloc failure) so no + * EDE (RFC8914) needed */ error_encode(r_buffer, LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, r->qflags, &r->edns); } @@ -1524,7 +1670,7 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, struct comm_reply* rep, uint16_t qid, uint16_t qflags, const struct query_info* qinfo) { - struct mesh_reply* r = regional_alloc(s->s.region, + struct mesh_reply* r = regional_alloc(s->s.region, sizeof(struct mesh_reply)); if(!r) return 0; @@ -1693,6 +1839,7 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, if(mstate->s.curmod == 0) { struct query_info* qinfo = NULL; uint16_t qflags; + int rpz_p = 0; mesh_query_done(mstate); mesh_walk_supers(mesh, mstate); @@ -1701,13 +1848,15 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, * from an external DNS server, we'll need to schedule * a prefetch after removing the current state, so * we need to make a copy of the query info here. */ - if(mstate->s.need_refetch) + if(mstate->s.need_refetch) { mesh_copy_qinfo(mstate, &qinfo, &qflags); + rpz_p = mstate->s.rpz_passthru; + } mesh_state_delete(&mstate->s); if(qinfo) { mesh_schedule_prefetch(mesh, qinfo, qflags, - 0, 1); + 0, 1, rpz_p); } return 0; } @@ -1917,7 +2066,7 @@ apply_respip_action(struct module_qstate* qstate, return 1; if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo, - alias_rrset, 0, qstate->region, az)) + alias_rrset, 0, qstate->region, az, NULL)) return 0; /* xxx_deny actions mean dropping the reply, unless the original reply @@ -2042,6 +2191,14 @@ mesh_serve_expired_callback(void* arg) } } + /* Add EDE Stale Answer (RCF8914). Ignore global ede as this is + * warning instead of an error */ + if (r->edns.edns_present && qstate->env->cfg->ede_serve_expired && + qstate->env->cfg->ede) { + edns_opt_list_append_ede(&r->edns.opt_list_out, + mstate->s.region, LDNS_EDE_STALE_ANSWER, NULL); + } + r_buffer = r->query_reply.c->buffer; if(r->query_reply.c->tcp_req_info) r_buffer = r->query_reply.c->tcp_req_info->spool_buffer; |