aboutsummaryrefslogtreecommitdiff
path: root/services/mesh.c
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2022-06-08 14:43:13 +0000
committerCy Schubert <cy@FreeBSD.org>2022-06-08 14:43:13 +0000
commit5f9f82264b91e041df7cba2406625146e7268ce4 (patch)
treeba7309ee547bf22115420277f45a3478aafb6397 /services/mesh.c
parent3574dc0bd83e731bba79edc130c0569bf05f7af5 (diff)
downloadsrc-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.c181
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;