From 1ce285c09b50d1e3a830d1f768ded0fc409c0bd7 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 13:38:45 +0200 Subject: [PATCH] Preserve datagram bindings and verify optional ICMP behavior --- src/proxy/icmp_proxy.c | 3 +++ src/proxy/udp_proxy.c | 3 +++ tests/test_proxy_lifecycle.c | 26 +++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/proxy/icmp_proxy.c b/src/proxy/icmp_proxy.c index 2a2e7ee2..729fda7a 100644 --- a/src/proxy/icmp_proxy.c +++ b/src/proxy/icmp_proxy.c @@ -379,6 +379,9 @@ int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { if (inst->icmp_proxy->ua == ua) return 0; DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: init attempted with another event loop"); return -EINVAL; } + if (inst->router_bindings.callbacks[ETCP_RT_ID_ICMP_PROXY]) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: service already belongs to another owner"); return -EINVAL; + } struct icmp_proxy_ctx* ctx = u_calloc(1, sizeof(struct icmp_proxy_ctx)); if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: context allocation failed"); return -ENOMEM; } ctx->inst = inst; ctx->ua = ua; ctx->raw_sock = SOCKET_INVALID; diff --git a/src/proxy/udp_proxy.c b/src/proxy/udp_proxy.c index 2247166b..00b7a1c3 100644 --- a/src/proxy/udp_proxy.c +++ b/src/proxy/udp_proxy.c @@ -305,6 +305,9 @@ int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { if (inst->udp_proxy->ua == ua) return 0; DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "udp_proxy: init attempted with another event loop"); return -1; } + if (inst->router_bindings.callbacks[ETCP_RT_ID_UDP_PROXY]) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "udp_proxy: service already belongs to another owner"); return -1; + } struct udp_proxy_ctx* ctx = u_calloc(1, sizeof(struct udp_proxy_ctx)); if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "udp_proxy: context allocation failed"); return -1; } ctx->inst = inst; ctx->ua = ua; diff --git a/tests/test_proxy_lifecycle.c b/tests/test_proxy_lifecycle.c index 7c19dd87..5f84b6c3 100644 --- a/tests/test_proxy_lifecycle.c +++ b/tests/test_proxy_lifecycle.c @@ -1,6 +1,8 @@ /* Отказы реальных UDP-сокетов, атомарный запуск и независимое владение TCP/UDP/ICMP. */ #include "async_regression.h" #include "../src/utun_instance.h" +#include "../src/transport_layer/etcp.h" +#include "../src/transport_layer/etcp_connections.h" #include "../src/proxy/udp_proxy.h" #include "../src/proxy/icmp_proxy.h" #include "../src/proxy/tcp_proxy_client.h" @@ -134,6 +136,14 @@ static int icmp_failures(int kind) { CHECK(f.inst->icmp_proxy == ctx && f.inst->router_bindings.callbacks[ETCP_RT_ID_ICMP_PROXY] == icmp_proxy_recv_cb); if (kind < 3) { CHECK(ret < 0); CHECK_EQ(ctx->raw_sock, SOCKET_INVALID); CHECK(!ctx->raw_read_id); + if (kind == 0) { + struct tun_if tun = {0}; tun.input_queue = queue_new(f.ua, 0, 0, 0, "icmp_reply"); CHECK(tun.input_queue); + struct tcp_proxy_client client = {.tun = &tun, .via_node_id = 42}; f.inst->tcp_proxy_client = &client; + struct ll_entry* e = request(1, 0, 4); CHECK(e); e->dgram[ROUTER_SVC_PAYLOAD_OFF] = ICMP_PROXY_SUBCMD_REPLY; + struct ETCP_CONN conn = {.instance = f.inst}; icmp_proxy_recv_cb(&conn, e); + e = queue_data_get(tun.input_queue); CHECK(e && e->len == 33 && e->dgram[0] == 4); + queue_dgram_free(e); queue_entry_free(e); queue_free(tun.input_queue); f.inst->tcp_proxy_client = NULL; + } deny_raw = 0; mock_raw = 1; CHECK_EQ(icmp_proxy_enable_exit(f.inst), 0); } else CHECK_EQ(ret, 0); CHECK_EQ(icmp_proxy_enable_exit(f.inst), 0); @@ -172,6 +182,12 @@ static int ownership(int unused) { CHECK(!tcp_proxy_client_create(f.inst, f.ua, NULL, NULL, 1500, 1, NULL, 0, 42, 1, "127.0.0.1:0", 1, "bad")); CHECK(!f.inst->router_bindings.callbacks[ETCP_RT_ID_TCP_PROXY_CLIENT]); CHECK(f.inst->udp_proxy == udp && f.inst->icmp_proxy == icmp); + struct tcp_proxy_client_mapping_config maps[2] = { + {.local_port = 80, .remote_port = 80, .remote_ip = "127.0.0.1"}, + {.local_port = 80, .remote_port = 81, .remote_ip = "127.0.0.1"} + }; + CHECK(!tcp_proxy_client_create(f.inst, f.ua, "proxy_test", "10.0.0.1", 1500, 1, maps, 2, 42, 0, NULL, 0, NULL)); + CHECK(!f.inst->router_bindings.callbacks[ETCP_RT_ID_TCP_PROXY_CLIENT]); CHECK(!tcp_proxy_client_create(f.inst, f.ua, NULL, NULL, 1500, 1, NULL, 0, 42, 1, NULL, 0, NULL)); struct tcp_proxy_client* p = tcp_proxy_client_create(f.inst, f.ua, NULL, NULL, 1500, 1, NULL, 0, 42, 1, "127.0.0.1:0", 0, NULL); CHECK(p); f.inst->tcp_proxy_client = p; CHECK(socks_proxy_get_port(p->socks_listen)); @@ -194,11 +210,18 @@ static int instance_rollback(int kind) { 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 if (kind == 3) deny_raw = 1; + else if (kind == 4) { mock_raw = 1; fail_read = 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 == 3) { + CHECK(inst && inst->tcp_proxy_server.enabled && inst->udp_proxy && inst->icmp_proxy); + CHECK_EQ(inst->icmp_proxy->raw_sock, SOCKET_INVALID); + utun_instance_destroy(inst); uasync_destroy(ua, 0); return 0; + } 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; @@ -216,7 +239,8 @@ int main(int argc, char** argv) { {"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}, {"Instance UDP rollback", instance_rollback, 0}, {"Instance ICMP rollback", instance_rollback, 1}, - {"Instance listener rollback", instance_rollback, 2} + {"Instance listener rollback", instance_rollback, 2}, {"Instance optional ICMP", instance_rollback, 3}, + {"Instance raw registration rollback", instance_rollback, 4} }; return regression_run(argc, argv, cases, sizeof(cases) / sizeof(cases[0])); }