Initialize proxy timers before notifying connected owners

This commit is contained in:
evgeny committed 2026-10-21 16:48:25 +02:00
1 parent 0e15234ce1
commit ba7c2873c5
3 files changed
+64 -4

No files matched your search

+18 -3
View File
@@ -118,7 +118,12 @@ void tcp_conn_destroy(struct tcp_conn* tc) {
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_conn_destroy: fd=%d connected=%d error=%d fin_remote=%d fin_pending=%d fin_local=%d closed=%d",
(int)tc->sock, tc->connected, tc->error, tc->fin_remote, tc->fin_pending, tc->fin_local, tc->closed);
if (tc->connect_timer) { uasync_cancel_timeout(tc->ua, tc->connect_timer); tc->connect_timer = NULL; }
if (tc->connect_timer) {
if (uasync_cancel_timeout(tc->ua, tc->connect_timer) != ERR_OK)
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_io: destroy could not cancel connect timer fd=%d timer=%p",
(int)tc->sock, tc->connect_timer);
tc->connect_timer = NULL;
}
if (tc->socket_id) {
uasync_remove_socket_t(tc->ua, tc->sock);
@@ -149,7 +154,12 @@ void tcp_conn_destroy(struct tcp_conn* tc) {
static void tcp_conn_handle_error(struct tcp_conn* tc, int err)
{
tc->error = 1;
if (tc->connect_timer) { uasync_cancel_timeout(tc->ua, tc->connect_timer); tc->connect_timer = NULL; }
if (tc->connect_timer) {
if (uasync_cancel_timeout(tc->ua, tc->connect_timer) != ERR_OK)
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_io: error cleanup could not cancel connect timer fd=%d timer=%p err=%d",
(int)tc->sock, tc->connect_timer, err);
tc->connect_timer = NULL;
}
queue_waiter_cancel(tc->read_queue, &tc->read_waiter);
queue_set_empty_callback(tc->read_queue, NULL, NULL);
@@ -398,7 +408,12 @@ static void write_cb(socket_t sock, void* arg) {
socklen_t len = sizeof(err);
if (getsockopt(tc->sock, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0 && err == 0) {
tc->connected = 1;
if (tc->connect_timer) { uasync_cancel_timeout(tc->ua, tc->connect_timer); tc->connect_timer = NULL; }
if (tc->connect_timer) {
if (uasync_cancel_timeout(tc->ua, tc->connect_timer) != ERR_OK)
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_io: connected socket could not cancel connect timer fd=%d timer=%p",
(int)tc->sock, tc->connect_timer);
tc->connect_timer = NULL;
}
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_io: connect ok fd=%d", (int)tc->sock);
if (tc->on_connected) tc->on_connected(tc, tc->arg);
if (tc->destroyed) return;
+3 -1
View File
@@ -329,7 +329,7 @@ int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry*
}
rc->next = ctx->conns; ctx->conns = rc;
if (ret == 0) { rc->tc->connected = 1; on_connected_cb(rc->tc, rc); }
if (ret == 0) rc->tc->connected = 1;
int timeout_rc = tcp_conn_set_connect_timeout(rc->tc, g->tcp_proxy_server_connect_timeout_ms);
if (timeout_rc < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy server: cannot set connect timeout sid=%08x timeout=%d rc=%d",
@@ -339,6 +339,8 @@ int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry*
return -1;
}
rc->diag_timer = uasync_set_timeout(rc->ua, 10000, rc, diag_timer_cb, "tps_diag");
/* CONNECTED может синхронно закрыть exit через loopback; все ресурсы уже должны принадлежать rc. */
if (ret == 0) on_connected_cb(rc->tc, rc);
queue_dgram_free(entry); queue_entry_free(entry);
return 0;
+43
View File
@@ -57,6 +57,8 @@ static int tests_failed = 0;
static int g_fin_count = 0;
static int g_error_count = 0;
static int g_last_error = 0;
static int g_error_had_timer = 0;
static int g_destroy_on_error = 0;
static struct tcp_conn* g_last_fin_tc = NULL;
static int g_fin_sent_count = 0;
static int g_closed_count = 0;
@@ -72,6 +74,8 @@ static void on_error(struct tcp_conn* tc, int err, void* arg) {
(void)arg;
g_error_count++;
g_last_error = err;
g_error_had_timer = tc->connect_timer != NULL;
if (g_destroy_on_error) tcp_conn_destroy(tc);
}
static void on_fin_sent_cb(struct tcp_conn* tc, void* arg) {
@@ -88,6 +92,8 @@ static void reset_counters(void) {
g_fin_count = 0;
g_error_count = 0;
g_last_error = 0;
g_error_had_timer = 0;
g_destroy_on_error = 0;
g_last_fin_tc = NULL;
g_fin_sent_count = 0;
g_closed_count = 0;
@@ -513,6 +519,42 @@ static void test_connect_timer_destroy(void) {
TEST_PASS();
}
/* Порт зарезервирован, но не слушает: реальный ECONNREFUSED до истечения connect-таймера. */
static void test_connect_error_cancels_timer(void) {
TEST_START("Connect error cancels timer before owner destroys connection");
size_t allocated = u_get_allocated_count();
uasync_t* ua = uasync_create();
ASSERT_TRUE(ua != NULL, "uasync_create failed");
reset_counters();
int reserved = socket(AF_INET, SOCK_STREAM, 0);
ASSERT_TRUE(reserved >= 0, "reserved socket failed");
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
ASSERT_EQ(bind(reserved, (struct sockaddr*)&addr, sizeof(addr)), 0, "reserve port failed");
socklen_t len = sizeof(addr);
ASSERT_EQ(getsockname(reserved, (struct sockaddr*)&addr, &len), 0, "getsockname failed");
int fd = socket(AF_INET, SOCK_STREAM, 0);
ASSERT_TRUE(fd >= 0, "socket failed");
ASSERT_EQ(socket_set_nonblocking(fd), 0, "nonblocking failed");
struct tcp_conn* tc = tcp_conn_create(ua, fd, 1500, 8192, 32, 8, 0, on_fin, on_error, NULL);
ASSERT_TRUE(tc != NULL, "tcp_conn_create failed");
ASSERT_EQ(tcp_conn_set_connect_timeout(tc, 60000), 0, "arm connect timer");
g_destroy_on_error = 1;
int ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
ASSERT_TRUE(ret < 0 && (errno == EINPROGRESS || errno == ECONNREFUSED), "expected refused connect");
uint64_t deadline = get_time_tb() + 5000;
while (!g_error_count && get_time_tb() < deadline) uasync_poll(ua, 10);
ASSERT_EQ(g_error_count, 1, "connect error did not call on_error");
ASSERT_EQ(g_last_error, ECONNREFUSED, "wrong connect error");
ASSERT_EQ(g_error_had_timer, 0, "connect timer still owned when on_error ran");
ASSERT_EQ(active_timers(ua), 0, "connect error left active timers");
close(reserved);
uasync_drain_immediate(ua);
ASSERT_EQ(ua->timer_alloc_count, ua->timer_free_count, "timer allocations leaked");
uasync_destroy(ua, 0);
ASSERT_EQ(u_get_allocated_count(), allocated, "on_error destroy leaked memory");
TEST_PASS();
}
// ====================================================================
// FIN / Close через очередь — новые тесты
// ====================================================================
@@ -814,6 +856,7 @@ int main(void) {
test_error_callback();
test_connect_timeout();
test_connect_timer_destroy();
test_connect_error_cancels_timer();
test_push_fin();
test_push_close();
test_fin_data_ordering();