From bb2ef05157e1c0e10e2d5dc7c93000b2a85e75d5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 13:36:22 +0200 Subject: [PATCH] Roll back required TUN mappings and verify instance startup failures --- src/proxy/tcp_proxy_client.c | 19 ++++++++++++++++--- tests/test_proxy_lifecycle.c | 24 +++++++++++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/proxy/tcp_proxy_client.c b/src/proxy/tcp_proxy_client.c index 917f129f..775dca24 100644 --- a/src/proxy/tcp_proxy_client.c +++ b/src/proxy/tcp_proxy_client.c @@ -752,6 +752,9 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str } int need_tun = tun_name && tun_name[0] && tun_ip && tun_ip[0]; + if ((tun_name && tun_name[0]) != (tun_ip && tun_ip[0])) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: TUN requires both name and IP"); return NULL; + } if (!need_tun && !socks_enabled && !http_proxy_enabled) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "no proxy mode enabled"); return NULL; } @@ -784,10 +787,20 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str int j; for (j = 0; j < mapping_count; j++) { uint16_t port_net = htons(mappings[j].local_port); struct tcp_pcb *lpcb = tcp_new(p->lwip); - if (!lpcb) continue; - tcp_bind(lpcb, INADDR_ANY, port_net); + if (!lpcb) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: mapping PCB allocation failed port=%u", mappings[j].local_port); + tcp_proxy_client_destroy(p); return NULL; + } + if (tcp_bind(lpcb, INADDR_ANY, port_net) != LERR_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: mapping bind failed port=%u", mappings[j].local_port); + tcp_abort(lpcb); tcp_proxy_client_destroy(p); return NULL; + } struct tcp_pcb *listen_pcb = tcp_listen(lpcb); - if (listen_pcb) { tcp_arg(listen_pcb, p); tcp_accept(listen_pcb, tcp_proxy_client_accept_cb); } + if (!listen_pcb) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: mapping listen failed port=%u", mappings[j].local_port); + tcp_abort(lpcb); tcp_proxy_client_destroy(p); return NULL; + } + tcp_arg(listen_pcb, p); tcp_accept(listen_pcb, tcp_proxy_client_accept_cb); } } } diff --git a/tests/test_proxy_lifecycle.c b/tests/test_proxy_lifecycle.c index 8cd5c258..7c19dd87 100644 --- a/tests/test_proxy_lifecycle.c +++ b/tests/test_proxy_lifecycle.c @@ -184,6 +184,26 @@ static int ownership(int unused) { CHECK_EQ(tcp_proxy_server_init(f.inst), 0); cleanup(&f); return 0; } +// Конструктор instance сам откатывает proxy-ресурсы, сохраняя заимствованный config при отказе. +static int instance_rollback(int kind) { + struct UASYNC* ua = uasync_create(); CHECK(ua); + const char* text = "[global]\ntun_enabled=no\n" + "my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" + "my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" + "[server: s1]\naddr=127.0.0.1:0\ntype=public\n[tcp_proxy_server]\nconnect_timeout=2000\n"; + struct utun_config* cfg = parse_config_from_buf(text, strlen(text), "proxy-failure"); CHECK(cfg); + if (kind == 0) regression_fault_arm(FAULT_CALLOC, "udp_proxy.c", 1); + else if (kind == 1) regression_fault_arm(FAULT_CALLOC, "icmp_proxy.c", 1); + else { + cfg->global.tcp_proxy_client_socks_enabled = 1; + strcpy(cfg->global.tcp_proxy_client_socks_addr, "invalid"); + } + struct UTUN_INSTANCE* inst = utun_instance_create_from_config(ua, cfg); + if (kind < 2) CHECK(regression_fault_hit()); + regression_fault_reset(); CHECK(!inst); CHECK_EQ(cfg->global.tcp_proxy_server_enabled, 1); + free_config(cfg); uasync_destroy(ua, 0); return 0; +} + int main(int argc, char** argv) { const struct regression_case cases[] = { {"UDP flow allocation", udp_failures, 0}, {"UDP socket", udp_failures, 1}, @@ -194,7 +214,9 @@ int main(int argc, char** argv) { {"UDP empty datagram", udp_failures, 10}, {"UDP context OOM", context_oom, 0}, {"ICMP context OOM", context_oom, 1}, {"ICMP unavailable raw", icmp_failures, 0}, {"ICMP nonblocking", icmp_failures, 1}, {"ICMP read registration", icmp_failures, 2}, - {"ICMP expiry and renewal", icmp_failures, 3}, {"Shared contexts and listener rollback", ownership, 0} + {"ICMP expiry and renewal", icmp_failures, 3}, {"Shared contexts and listener rollback", ownership, 0}, + {"Instance UDP rollback", instance_rollback, 0}, {"Instance ICMP rollback", instance_rollback, 1}, + {"Instance listener rollback", instance_rollback, 2} }; return regression_run(argc, argv, cases, sizeof(cases) / sizeof(cases[0])); }