diff --git a/services/mesh.c b/services/mesh.c index add773b88..6c52759ae 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1097,6 +1097,18 @@ mesh_state_make_unique(struct mesh_state* mstate) mstate->unique = mstate; } +/** pop a reply from the reply list, if there are any. */ +static struct mesh_reply* +mesh_reply_list_pop_first(struct mesh_state* mstate) +{ + if(mstate->reply_list) { + struct mesh_reply* r = mstate->reply_list; + mstate->reply_list = r->next; + return r; + } + return NULL; +} + void mesh_state_cleanup(struct mesh_state* mstate) { @@ -1112,15 +1124,30 @@ mesh_state_cleanup(struct mesh_state* mstate) } /* drop unsent replies */ if(!mstate->replies_sent) { - struct mesh_reply* rep = mstate->reply_list; + struct mesh_reply* rep; struct mesh_cb* cb; - /* in tcp_req_info, the mstates linked are removed, but - * the reply_list is now NULL, so the remove-from-empty-list - * takes no time and also it does not do the mesh accounting */ - mstate->reply_list = NULL; - for(; rep; rep=rep->next) { + /* Pop items from the list, that means there is no iterator. + * And then items can be removed from the reply list, from + * like comm_point_drop_reply and comm_point_close calls. + * As the tcp_req_info and http2 code drops the entire + * connection. That could delete mesh_reply items previous and + * after the current state. The previous items are already + * popped. And the next items can be altered, like to when a + * connection has more replies on the reply list. + * The current item is also popped so the code needs to + * remove its references. */ + while((rep = mesh_reply_list_pop_first(mstate)) != NULL) { infra_wait_limit_dec(mesh->env->infra_cache, &rep->query_reply, mesh->env->cfg); + if(rep->query_reply.c->tcp_req_info) + tcp_req_info_remove_mesh_state( + rep->query_reply.c->tcp_req_info, + mstate); + else if(rep->query_reply.c->use_h2) + http2_stream_remove_mesh_state(rep->h2_stream); + else if(rep->query_reply.doq_stream) + doq_stream_remove_mesh_state( + rep->query_reply.doq_stream); comm_point_drop_reply(&rep->query_reply); log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; @@ -1484,12 +1511,6 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, struct timeval end_time; struct timeval duration; int secure; - /* briefly set the replylist to null in case the - * meshsendreply calls tcpreqinfo sendreply that - * comm_point_drops because of size, and then the - * null stops the mesh state remove and thus - * reply_list modification and accounting */ - struct mesh_reply* rlist = m->reply_list; /* rpz: apply actions */ rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m) @@ -1546,9 +1567,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, sldns_buffer_write_at(r_buffer, 0, &r->qid, sizeof(uint16_t)); sldns_buffer_write_at(r_buffer, 12, r->qname, m->s.qinfo.qname_len); - m->reply_list = NULL; comm_point_send_reply(&r->query_reply); - m->reply_list = rlist; } else if(rcode) { m->s.qinfo.qname = r->qname; m->s.qinfo.local_alias = r->local_alias; @@ -1570,9 +1589,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, } error_encode(r_buffer, rcode, &m->s.qinfo, r->qid, r->qflags, &r->edns); - m->reply_list = NULL; comm_point_send_reply(&r->query_reply); - m->reply_list = rlist; } else { size_t udp_size = r->edns.udp_size; r->edns.edns_version = EDNS_ADVERTISED_VERSION; @@ -1608,9 +1625,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, error_encode(r_buffer, LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, r->qflags, &r->edns); } - m->reply_list = NULL; comm_point_send_reply(&r->query_reply); - m->reply_list = rlist; } infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply, m->s.env->cfg); @@ -1762,6 +1777,7 @@ void mesh_query_done(struct mesh_state* mstate) struct reply_info* rep = (mstate->s.return_msg? mstate->s.return_msg->rep:NULL); struct timeval tv = {0, 0}; + struct mesh_area* mesh = mstate->s.env->mesh; int i = 0; /* No need for the serve expired timer anymore; we are going to reply. */ if(mstate->s.serve_expired_data) { @@ -1786,7 +1802,18 @@ void mesh_query_done(struct mesh_state* mstate) && (!rep || rep->security != sec_status_secure)) dns_error_reporting(&mstate->s, rep); - for(r = mstate->reply_list; r; r = r->next) { + while((r = mesh_reply_list_pop_first(mstate)) != NULL) { + + /* it was not detached (because it had a reply list), could be now */ + if(!mstate->reply_list && !mstate->cb_list + && mstate->super_set.count == 0) { + mesh->num_detached_states++; + } + /* if not replies any more in mstate, it is no longer a reply_state */ + if(!mstate->reply_list && !mstate->cb_list) { + log_assert(mesh->num_reply_states > 0); + mesh->num_reply_states--; + } if(mesh_is_udp(r)) { /* For UDP queries, the old replies are discarded. * This stops a large volume of old replies from @@ -1801,22 +1828,18 @@ void mesh_query_done(struct mesh_state* mstate) ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > mstate->s.env->cfg->discard_timeout) { /* Drop the reply, it is too old */ - /* briefly set the reply_list to NULL, so that the - * tcp req info cleanup routine that calls the mesh - * to deregister the meshstate for it is not done - * because the list is NULL and also accounting is not - * done there, but instead we do that here. */ - struct mesh_reply* reply_list = mstate->reply_list; verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); infra_wait_limit_dec(mstate->s.env->infra_cache, &r->query_reply, mstate->s.env->cfg); - mstate->reply_list = NULL; - if(r->query_reply.c->use_h2) + if(r->query_reply.c->tcp_req_info) + tcp_req_info_remove_mesh_state( + r->query_reply.c->tcp_req_info, + mstate); + else if(r->query_reply.c->use_h2) http2_stream_remove_mesh_state(r->h2_stream); else if(r->query_reply.doq_stream) doq_stream_remove_mesh_state(r->query_reply.doq_stream); comm_point_drop_reply(&r->query_reply); - mstate->reply_list = reply_list; log_assert(mstate->s.env->mesh->num_reply_addrs > 0); mstate->s.env->mesh->num_reply_addrs--; mstate->s.env->mesh->num_queries_discard_timeout++; @@ -1841,22 +1864,17 @@ void mesh_query_done(struct mesh_state* mstate) /* 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) { - /* briefly set the reply_list to NULL, so that the - * tcp req info cleanup routine that calls the mesh - * to deregister the meshstate for it is not done - * because the list is NULL and also accounting is not - * done there, but instead we do that here. */ - struct mesh_reply* reply_list = mstate->reply_list; infra_wait_limit_dec(mstate->s.env->infra_cache, &r->query_reply, mstate->s.env->cfg); - mstate->reply_list = NULL; - if(r->query_reply.c->use_h2) { + if(r->query_reply.c->tcp_req_info) { + tcp_req_info_remove_mesh_state( + r->query_reply.c->tcp_req_info, mstate); + } else if(r->query_reply.c->use_h2) { http2_stream_remove_mesh_state(r->h2_stream); } else if(r->query_reply.doq_stream) { doq_stream_remove_mesh_state(r->query_reply.doq_stream); } comm_point_drop_reply(&r->query_reply); - mstate->reply_list = reply_list; log_assert(mstate->s.env->mesh->num_reply_addrs > 0); mstate->s.env->mesh->num_reply_addrs--; } else { @@ -1897,18 +1915,6 @@ void mesh_query_done(struct mesh_state* mstate) } } - /* Mesh area accounting */ - if(mstate->reply_list) { - mstate->reply_list = NULL; - if(!mstate->reply_list && !mstate->cb_list) { - /* was a reply state, not anymore */ - log_assert(mstate->s.env->mesh->num_reply_states > 0); - mstate->s.env->mesh->num_reply_states--; - } - if(!mstate->reply_list && !mstate->cb_list && - mstate->super_set.count == 0) - mstate->s.env->mesh->num_detached_states++; - } mstate->replies_sent = 1; while((c = mstate->cb_list) != NULL) { @@ -2498,7 +2504,6 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m, } } - static int apply_respip_action(struct module_qstate* qstate, const struct query_info* qinfo, struct respip_client_info* cinfo, @@ -2631,7 +2636,18 @@ mesh_serve_expired_callback(void* arg) if(verbosity >= VERB_ALGO) log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep); - for(r = mstate->reply_list; r; r = r->next) { + while((r = mesh_reply_list_pop_first(mstate)) != NULL) { + + /* it was not detached (because it had a reply list), could be now */ + if(!mstate->reply_list && !mstate->cb_list + && mstate->super_set.count == 0) { + mesh->num_detached_states++; + } + /* if not replies any more in mstate, it is no longer a reply_state */ + if(!mstate->reply_list && !mstate->cb_list) { + log_assert(mesh->num_reply_states > 0); + mesh->num_reply_states--; + } if(mesh_is_udp(r)) { struct timeval old; timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); @@ -2639,22 +2655,17 @@ mesh_serve_expired_callback(void* arg) ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > mstate->s.env->cfg->discard_timeout) { /* Drop the reply, it is too old */ - /* briefly set the reply_list to NULL, so that the - * tcp req info cleanup routine that calls the mesh - * to deregister the meshstate for it is not done - * because the list is NULL and also accounting is not - * done there, but instead we do that here. */ - struct mesh_reply* reply_list = mstate->reply_list; verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); infra_wait_limit_dec(mstate->s.env->infra_cache, &r->query_reply, mstate->s.env->cfg); - mstate->reply_list = NULL; - if(r->query_reply.c->use_h2) + if(r->query_reply.c->tcp_req_info) + tcp_req_info_remove_mesh_state( + r->query_reply.c->tcp_req_info, mstate); + else if(r->query_reply.c->use_h2) http2_stream_remove_mesh_state(r->h2_stream); else if(r->query_reply.doq_stream) doq_stream_remove_mesh_state(r->query_reply.doq_stream); comm_point_drop_reply(&r->query_reply); - mstate->reply_list = reply_list; log_assert(mstate->s.env->mesh->num_reply_addrs > 0); mstate->s.env->mesh->num_reply_addrs--; mstate->s.env->mesh->num_queries_discard_timeout++; @@ -2712,18 +2723,6 @@ mesh_serve_expired_callback(void* arg) } } - /* Mesh area accounting */ - if(mstate->reply_list) { - mstate->reply_list = NULL; - if(!mstate->reply_list && !mstate->cb_list) { - log_assert(mesh->num_reply_states > 0); - mesh->num_reply_states--; - if(mstate->super_set.count == 0) { - mesh->num_detached_states++; - } - } - } - while((c = mstate->cb_list) != NULL) { /* take this cb off the list; so that the list can be * changed, eg. by adds from the callback routine */ diff --git a/util/netevent.c b/util/netevent.c index 432fead1b..33516f246 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -3226,6 +3226,26 @@ static int http2_submit_settings(struct http2_session* h2_session) } #endif /* HAVE_NGHTTP2 */ +/** Clear http2 stream mesh states */ +static void http2_session_clear_meshstate(struct http2_session* h2_session) +{ +#ifdef HAVE_NGHTTP2 + /* Since the session gets closed, remove the mesh state references. */ + struct http2_stream* h2_stream; + for(h2_stream = h2_session->first_stream; h2_stream; + h2_stream = h2_stream->next) { + if(h2_stream->mesh_state) { + mesh_state_remove_reply(h2_stream->mesh, + h2_stream->mesh_state, h2_session->c, + h2_stream, NULL); + h2_stream->mesh_state = NULL; + } + } +#else + (void)h2_session; +#endif /* HAVE_NGHTTP2 */ +} + #ifdef HAVE_NGHTTP2 /** Delete http2 stream. After session delete or stream close callback */ static void http2_stream_delete(struct http2_session* h2_session, @@ -6872,6 +6892,7 @@ comm_point_drop_reply(struct comm_reply* repinfo) if(repinfo->c->type == comm_http) { if(repinfo->c->h2_session) { repinfo->c->h2_session->is_drop = 1; + http2_session_clear_meshstate(repinfo->c->h2_session); if(!repinfo->c->h2_session->postpone_drop) reclaim_http_handler(repinfo->c); return;