diff --git a/src/proxy/tcp_proxy_server.c b/src/proxy/tcp_proxy_server.c index e07106b4..08a399a6 100644 --- a/src/proxy/tcp_proxy_server.c +++ b/src/proxy/tcp_proxy_server.c @@ -55,6 +55,18 @@ static int send_msg(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst, return proxy_send(inst, dst, ETCP_RT_ID_TCP_PROXY_CLIENT, subcmd, sid, data, len, force); } +// CLOSE может синхронно освободить rc через on_closed: резервируем состояние до push. +static void server_queue_close(struct tcp_proxy_server_conn* rc) { + if (rc->close_queued) return; + rc->close_queued = 1; + DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "proxy exit: queue CLOSE peer=%016llx sid=%08x fd=%d", + (unsigned long long)rc->peer_node_id, rc->stream_id, (int)rc->tc->sock); + if (tcp_conn_push_close(rc->tc) == 0) return; + rc->close_queued = 0; + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "proxy exit: queue CLOSE failed peer=%016llx sid=%08x", + (unsigned long long)rc->peer_node_id, rc->stream_id); +} + // FIN относится только к прочитанному направлению: сначала передаём весь read_queue. static void server_maybe_finish(struct tcp_proxy_server_conn* rc) { struct tcp_conn* tc = rc->tc; @@ -64,7 +76,7 @@ static void server_maybe_finish(struct tcp_proxy_server_conn* rc) { proxy_flow_flush(&rc->flow); } if (rc->flow.fin_sent && rc->flow.fin_received && tc->fin_local) { - if (tcp_conn_push_close(tc) == 0) rc->close_queued = 1; + server_queue_close(rc); } } @@ -377,7 +389,7 @@ void tcp_proxy_server_handle_close(struct UTUN_INSTANCE* inst, uint64_t peer, ui } } if (rc->tc->connected) { - if (!rc->close_queued && tcp_conn_push_close(rc->tc) == 0) rc->close_queued = 1; + server_queue_close(rc); } else tcp_proxy_server_conn_free(rc); } diff --git a/tests/test_proxy_regressions.c b/tests/test_proxy_regressions.c index bdc64d49..f1c895a3 100644 --- a/tests/test_proxy_regressions.c +++ b/tests/test_proxy_regressions.c @@ -204,6 +204,15 @@ static void server_tests(struct UTUN_INSTANCE* inst) { CHECK(inst->tcp_proxy_server.conn_count == 1); deliver(inst, 22, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_CLOSE, 2, NULL, 0); CHECK(inst->tcp_proxy_server.conn_count == 0); + // Сначала FIN от ETCP, затем FIN из OS: on_fin закрывает и освобождает exit-поток. + deliver(inst, 11, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_CONNECT, 3, connect_data, 6); + socket_t c = accept(listener, NULL, NULL); CHECK(c != SOCKET_INVALID); socket_set_nonblocking(c); + deliver(inst, 11, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_FIN, 3, NULL, 0); + CHECK(inst->tcp_proxy_server.conn_count == 1 && recv(c, buf, sizeof(buf), 0) == 0); + message_count = 0; + CHECK(shutdown(c, SHUT_WR) == 0); pump(); + CHECK(inst->tcp_proxy_server.conn_count == 0 && count_cmd(TCP_PROXY_SUBCMD_FIN) == 1); + socket_close_wrapper(c); socket_close_wrapper(a); socket_close_wrapper(b); socket_close_wrapper(listener); inst->tcp_proxy_server.enabled = 0; pump(); puts("[PASS] exit streams isolated by node + stream ID, duplicate CONNECT rejected");