From 50da7fe4578d0faf99b637ab85e7e0d989ea8f84 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 13:24:30 +0200 Subject: [PATCH] Make TCP proxy construction atomic and retain instance datagrams --- src/proxy/tcp_proxy_client.c | 29 +++++++++++++++++++++-------- src/proxy/tcp_proxy_client.h | 10 ++++++---- src/proxy/tcp_proxy_server.c | 27 ++++++++++++++------------- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/proxy/tcp_proxy_client.c b/src/proxy/tcp_proxy_client.c index caef252c..917f129f 100644 --- a/src/proxy/tcp_proxy_client.c +++ b/src/proxy/tcp_proxy_client.c @@ -723,7 +723,8 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en if (subcmd == TCP_PROXY_SUBCMD_ERROR) { tcp_proxy_client_handle_error(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } if (subcmd == TCP_PROXY_SUBCMD_FIN) { tcp_proxy_client_handle_fin(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } } - if (subcmd == TCP_PROXY_SUBCMD_DATA || subcmd == TCP_PROXY_SUBCMD_CLOSE || subcmd == TCP_PROXY_SUBCMD_FIN || subcmd == TCP_PROXY_SUBCMD_ERROR) { + if (subcmd == TCP_PROXY_SUBCMD_DATA || subcmd == TCP_PROXY_SUBCMD_CLOSE || subcmd == TCP_PROXY_SUBCMD_FIN || + subcmd == TCP_PROXY_SUBCMD_ERROR || subcmd == TCP_PROXY_SUBCMD_WINDOW) { DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY subcmd=%02x sid=%08x — client conn not found, silent drop", subcmd, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } @@ -742,6 +743,13 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str int http_proxy_enabled, const char* http_proxy_addr) { if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "ua is NULL"); return NULL; } + if (inst && (inst->tcp_proxy_client || inst->router_bindings.callbacks[ETCP_RT_ID_TCP_PROXY_CLIENT])) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: duplicate instance binding"); return NULL; + } + if ((socks_enabled && (!socks_addr || !socks_addr[0])) || + (http_proxy_enabled && (!http_proxy_addr || !http_proxy_addr[0])) || mapping_count < 0 || (mapping_count && !mappings)) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: missing listener address or invalid mappings"); return NULL; + } int need_tun = tun_name && tun_name[0] && tun_ip && tun_ip[0]; if (!need_tun && !socks_enabled && !http_proxy_enabled) { @@ -787,21 +795,28 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str if (socks_enabled && socks_addr && socks_addr[0]) { p->socks_listen = socks_proxy_init_listen(ua, socks_addr, &p->socks_conns, &p->socks_conn_count, &p->next_stream_id, inst, via_node_id, 0); - if (!p->socks_listen) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client: SOCKS init failed on %s", socks_addr); } + if (!p->socks_listen) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client: SOCKS init failed on %s", socks_addr); + tcp_proxy_client_destroy(p); return NULL; + } } if (http_proxy_enabled && http_proxy_addr && http_proxy_addr[0]) { p->http_listen = socks_proxy_init_listen(ua, http_proxy_addr, &p->http_conns, &p->http_conn_count, &p->next_stream_id, inst, via_node_id, 1); - if (!p->http_listen) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client: HTTP proxy init failed on %s", http_proxy_addr); } + if (!p->http_listen) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client: HTTP proxy init failed on %s", http_proxy_addr); + tcp_proxy_client_destroy(p); return NULL; + } } if (inst) { if (etcp_router_bind(inst, ETCP_RT_ID_TCP_PROXY_CLIENT, tcp_proxy_client_router_recv_cb) != 0) { - DEBUG_WARN(DEBUG_CATEGORY_PROXY, "TCP proxy client: etcp_router_bind failed"); + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: etcp_router_bind failed"); + tcp_proxy_client_destroy(p); return NULL; } else { DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client: etcp_router bind registered for ID=0x%02x", ETCP_RT_ID_TCP_PROXY_CLIENT); - if (need_tun) { udp_proxy_init(inst, ua); icmp_proxy_init(inst, ua); } + p->router_bound = 1; } } @@ -813,11 +828,9 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str void tcp_proxy_client_destroy(struct tcp_proxy_client* p) { if (!p) return; - int total = p->conn_count + p->socks_conn_count + p->http_conn_count; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client destroying: lwip=%d socks=%d http=%d", p->conn_count, p->socks_conn_count, p->http_conn_count); - if (p->inst) etcp_router_unbind(p->inst, ETCP_RT_ID_TCP_PROXY_CLIENT); - if (p->inst && !p->inst->tcp_proxy_server.enabled) { udp_proxy_destroy(p->inst); icmp_proxy_destroy(p->inst); } + if (p->router_bound) { etcp_router_unbind(p->inst, ETCP_RT_ID_TCP_PROXY_CLIENT); p->router_bound = 0; } if (p->socks_listen) socks_proxy_close_listen(p->ua, p->socks_listen, NULL); socks_proxy_conn_free_all(&p->socks_conns, &p->socks_conn_count); diff --git a/src/proxy/tcp_proxy_client.h b/src/proxy/tcp_proxy_client.h index 2a145c59..174dbfac 100644 --- a/src/proxy/tcp_proxy_client.h +++ b/src/proxy/tcp_proxy_client.h @@ -65,6 +65,7 @@ struct tcp_proxy_client_conn { struct tcp_proxy_client { struct UTUN_INSTANCE* inst; // заимствован; NULL допустим для автономного TUN smoke test + uint8_t router_bound; // собственный CLIENT binding установлен; rollback не снимает чужую регистрацию struct UASYNC* ua; // заимствованный event loop struct tun_if* tun; // собственный TUN или NULL для SOCKS/HTTP без TUN struct tcp_proxy_client_conn* conns; // список собственных lwIP-потоков @@ -78,13 +79,13 @@ struct tcp_proxy_client { int mapping_count; // число записей mappings, порты конфига в host byte order // SOCKS proxy - int socks_enabled; // запрошен SOCKS5; не гарантирует успешный listener + int socks_enabled; // SOCKS5 listener обязателен при создании struct socks_proxy_conn* socks_conns; // собственные SOCKS5-клиенты int socks_conn_count; // число SOCKS5-клиентов struct listen_ctx* socks_listen; // собственный listener или NULL при отключении/ошибке // HTTP forward proxy и CONNECT - int http_proxy_enabled; // запрошен HTTP; не гарантирует успешный listener + int http_proxy_enabled; // HTTP listener обязателен при создании struct socks_proxy_conn* http_conns; // собственные HTTP-клиенты int http_conn_count; // число HTTP-клиентов struct listen_ctx* http_listen; // собственный listener или NULL при отключении/ошибке @@ -94,7 +95,8 @@ struct tcp_proxy_client { // test_mode передаётся tun_init_nat: ненулевой использует тестовый TUN; mappings заимствуются до destroy. // SOCKS/HTTP создаются при enabled и непустом IPv4:port; via_node_id — общий exit. // Результат: собственный p или NULL при критической ошибке/отсутствии режима. -// Ошибки listener/router bind/UDP/ICMP не делают create атомарным: проверять listener и диагностику отдельно. +// Все выбранные входы и CLIENT binding обязательны; отказ полностью освобождает новые ресурсы. +// Общие UDP/ICMP создаёт и уничтожает instance отдельно; create их не затрагивает. struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua, const char* tun_name, const char* tun_ip, int mtu, int test_mode, struct tcp_proxy_client_mapping_config* mappings, int mapping_count, @@ -102,7 +104,7 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str int socks_enabled, const char* socks_addr, int http_proxy_enabled, const char* http_proxy_addr); // Остановить listeners, освободить потоки/TUN/lwIP и снять router binding; NULL допустим. -// UDP/ICMP сохраняются, пока включён сервер того же inst. Не обнуляет inst->tcp_proxy_client. +// Не затрагивает общие UDP/ICMP и не обнуляет inst->tcp_proxy_client. void tcp_proxy_client_destroy(struct tcp_proxy_client* p); // Router callback: conn->instance задаёт владельца, entry содержит метаданные router и передаёт владение. // Освобождает entry/dgram при любом исходе; CLOSE_ALL = пустой payload router, а не TCP-подкоманда. diff --git a/src/proxy/tcp_proxy_server.c b/src/proxy/tcp_proxy_server.c index 47e69d1c..a2ba0341 100644 --- a/src/proxy/tcp_proxy_server.c +++ b/src/proxy/tcp_proxy_server.c @@ -1,7 +1,5 @@ // tcp_proxy_server.c — TCP прокси-сервер (exit node) #include "tcp_proxy_server.h" -#include "udp_proxy.h" -#include "icmp_proxy.h" #include "etcp.h" #include "etcp_api.h" #include "etcp_router.h" @@ -494,7 +492,8 @@ void tcp_proxy_server_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { if (subcmd == TCP_PROXY_SUBCMD_FIN) { tcp_proxy_server_handle_fin(inst, peer, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } } } - if (subcmd == TCP_PROXY_SUBCMD_DATA || subcmd == TCP_PROXY_SUBCMD_CLOSE || subcmd == TCP_PROXY_SUBCMD_FIN || subcmd == TCP_PROXY_SUBCMD_ERROR) { + if (subcmd == TCP_PROXY_SUBCMD_DATA || subcmd == TCP_PROXY_SUBCMD_CLOSE || subcmd == TCP_PROXY_SUBCMD_FIN || + subcmd == TCP_PROXY_SUBCMD_ERROR || subcmd == TCP_PROXY_SUBCMD_WINDOW) { DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY subcmd=%02x sid=%08x — server conn not found, silent drop", subcmd, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } @@ -507,16 +506,18 @@ void tcp_proxy_server_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { // ==================================================================== int tcp_proxy_server_init(struct UTUN_INSTANCE* inst) { - if (!inst) return -1; + if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy server: init requires instance"); return -1; } struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + if (ctx->inst || ctx->conns) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy server: duplicate initialization"); return -1; } memset(ctx, 0, sizeof(*ctx)); if (!inst->config) return 0; - ctx->enabled = inst->config->global.tcp_proxy_server_enabled; ctx->inst = inst; - if (!ctx->enabled) return 0; - etcp_router_bind(inst, ETCP_RT_ID_TCP_PROXY_SERVER, tcp_proxy_server_recv_cb); - udp_proxy_init(inst, inst->ua); - icmp_proxy_init(inst, inst->ua); + if (!inst->config->global.tcp_proxy_server_enabled) return 0; + if (inst->router_bindings.callbacks[ETCP_RT_ID_TCP_PROXY_SERVER] || + etcp_router_bind(inst, ETCP_RT_ID_TCP_PROXY_SERVER, tcp_proxy_server_recv_cb) < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy server: service binding unavailable"); ctx->inst = NULL; return -1; + } + ctx->enabled = 1; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy server initialized node=%016llx", (unsigned long long)inst->node_id); return 0; } @@ -524,12 +525,12 @@ int tcp_proxy_server_init(struct UTUN_INSTANCE* inst) { void tcp_proxy_server_destroy(struct UTUN_INSTANCE* inst) { if (!inst) return; struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + if (!ctx->inst) return; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy server destroying: conn_count=%d", ctx->conn_count); struct tcp_proxy_server_conn* rc = ctx->conns; while (rc) { struct tcp_proxy_server_conn* next = rc->next; tcp_proxy_server_conn_free(rc); rc = next; } - ctx->conns = NULL; ctx->enabled = 0; - etcp_router_unbind(inst, ETCP_RT_ID_TCP_PROXY_SERVER); - udp_proxy_destroy(inst); - icmp_proxy_destroy(inst); + ctx->conns = NULL; + if (ctx->enabled) etcp_router_unbind(inst, ETCP_RT_ID_TCP_PROXY_SERVER); + ctx->enabled = 0; ctx->inst = NULL; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy server destroyed"); }