From 4e2cc0e35a2d8bb65b04463114037cfc15005e6e Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 10 Oct 2026 16:04:19 +0200 Subject: [PATCH] tests: hold OS-assigned HTTP and SOCKS listener ports --- src/proxy/socks_proxy.c | 17 ++++++-- src/proxy/socks_proxy.h | 2 + tests/test_proxy_regressions.c | 14 +++---- tests/test_socks_http_proxy.c | 71 +++++++++++++++------------------- 4 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/proxy/socks_proxy.c b/src/proxy/socks_proxy.c index 6f1df5fb..44d8dd8a 100644 --- a/src/proxy/socks_proxy.c +++ b/src/proxy/socks_proxy.c @@ -909,8 +909,12 @@ struct listen_ctx* socks_proxy_init_listen(struct UASYNC* ua, const char* addr_s size_t ip_len = (size_t)(colon - addr_str); if (ip_len > sizeof(ip) - 1) ip_len = sizeof(ip) - 1; memcpy(ip, addr_str, ip_len); ip[ip_len] = '\0'; - int port = atoi(colon + 1); - if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: bad port %d", port); return NULL; } + char* end; + long parsed = strtol(colon + 1, &end, 10); + if (end == colon + 1 || *end || parsed < 0 || parsed > 65535) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: bad port '%s'", colon + 1); return NULL; + } + int port = (int)parsed; struct listen_ctx* ctx = u_calloc(1, sizeof(struct listen_ctx)); if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: u_calloc failed"); return NULL; } @@ -928,16 +932,23 @@ struct listen_ctx* socks_proxy_init_listen(struct UASYNC* ua, const char* addr_s if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: inet_pton(%s) failed", ip); socket_close_wrapper(sock); u_free(ctx); return NULL; } if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: bind(%s:%d) failed errno=%d", ip, port, errno); socket_close_wrapper(sock); u_free(ctx); return NULL; } if (listen(sock, 32) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: listen() failed errno=%d", errno); socket_close_wrapper(sock); u_free(ctx); return NULL; } + uint16_t bound_port = socket_get_bound_port(sock); + if (!bound_port) { socket_close_wrapper(sock); u_free(ctx); return NULL; } ctx->listen_sock = sock; ctx->socket_id = uasync_add_socket_t(ua, sock, on_accept_cb, NULL, NULL, "socks_proxy_listen", ctx); if (!ctx->socket_id) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: uasync_add_socket_t failed"); socket_close_wrapper(sock); u_free(ctx); return NULL; } DEBUG_INFO(DEBUG_CATEGORY_PROXY, "socks_proxy: %s listening on %s:%d sock=%d", - is_http ? "HTTP" : "SOCKS", ip, port, (int)sock); + is_http ? "HTTP" : "SOCKS", ip, bound_port, (int)sock); return ctx; } +uint16_t socks_proxy_get_port(const struct listen_ctx* ctx) { + if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: missing listener"); return 0; } + return socket_get_bound_port(ctx->listen_sock); +} + void socks_proxy_close_listen(struct UASYNC* ua, struct listen_ctx* ctx, socket_t* sock_out) { if (!ctx) return; if (ctx->socket_id) uasync_remove_socket_t(ua, ctx->listen_sock); diff --git a/src/proxy/socks_proxy.h b/src/proxy/socks_proxy.h index 359c5873..0f08c539 100644 --- a/src/proxy/socks_proxy.h +++ b/src/proxy/socks_proxy.h @@ -83,6 +83,8 @@ struct listen_ctx* socks_proxy_init_listen(struct UASYNC* ua, const char* addr_s // Закрыть слушающий сокет и освободить listen_ctx. // Если sock_out задан, в него записывается SOCKET_INVALID: сокет уже закрыт. void socks_proxy_close_listen(struct UASYNC* ua, struct listen_ctx* ctx, socket_t* sock_out); +// Фактический порт активного listener (IP:0 выбирает ОС); 0 при ошибке. +uint16_t socks_proxy_get_port(const struct listen_ctx* ctx); // Найти соединение по stream_id struct socks_proxy_conn* socks_proxy_find_conn(struct socks_proxy_conn* head, uint32_t stream_id); diff --git a/tests/test_proxy_regressions.c b/tests/test_proxy_regressions.c index 5bd40d83..f2bd3c5d 100644 --- a/tests/test_proxy_regressions.c +++ b/tests/test_proxy_regressions.c @@ -115,13 +115,10 @@ static void deliver(struct UTUN_INSTANCE* inst, uint64_t peer, uint8_t svc, uint } static void parser_tests(struct UTUN_INSTANCE* inst) { - uint16_t sp, hp; - socket_t reserved = listen_local(&sp); socket_close_wrapper(reserved); - reserved = listen_local(&hp); socket_close_wrapper(reserved); - char socks[64], http[64]; - snprintf(socks, sizeof(socks), "127.0.0.1:%u", sp); snprintf(http, sizeof(http), "127.0.0.1:%u", hp); - struct tcp_proxy_client* p = tcp_proxy_client_create(inst, ua, NULL, NULL, 1500, 1, NULL, 0, 42, 1, socks, 1, http); + struct tcp_proxy_client* p = tcp_proxy_client_create(inst, ua, NULL, NULL, 1500, 1, NULL, 0, 42, + 1, "127.0.0.1:0", 1, "127.0.0.1:0"); CHECK(p); inst->tcp_proxy_client = p; + uint16_t sp = socks_proxy_get_port(p->socks_listen), hp = socks_proxy_get_port(p->http_listen); CHECK(sp && hp); socket_t fd = connect_local(sp); // Greeting, CONNECT и ранние данные намеренно одним write. const uint8_t request[] = {5,1,0, 5,1,0,1,127,0,0,1,0,80, 'a','b','c'}; @@ -238,10 +235,9 @@ static void upstream(struct UTUN_INSTANCE* inst, uint32_t sid, const char* text) } static void http_tests(struct UTUN_INSTANCE* inst) { - uint16_t port; socket_t reserved = listen_local(&port); socket_close_wrapper(reserved); - char addr[64]; snprintf(addr, sizeof(addr), "127.0.0.1:%u", port); - struct tcp_proxy_client* p = tcp_proxy_client_create(inst, ua, NULL, NULL, 1500, 1, NULL, 0, 42, 0, NULL, 1, addr); + struct tcp_proxy_client* p = tcp_proxy_client_create(inst, ua, NULL, NULL, 1500, 1, NULL, 0, 42, 0, NULL, 1, "127.0.0.1:0"); CHECK(p); inst->tcp_proxy_client = p; + uint16_t port = socks_proxy_get_port(p->http_listen); CHECK(port); char out[64000]; socket_t fd = connect_local(port); message_count = 0; const char pipeline[] = "GET http://127.0.0.1:8080?x=1 HTTP/1.1\r\nHost: wrong\r\n" "Proxy-Authorization: secret\r\nConnection: X-Hop\r\nX-Hop: secret\r\n\r\n" diff --git a/tests/test_socks_http_proxy.c b/tests/test_socks_http_proxy.c index 85cf8b36..740b137b 100644 --- a/tests/test_socks_http_proxy.c +++ b/tests/test_socks_http_proxy.c @@ -20,6 +20,9 @@ #include #include "../lib/platform_compat.h" #include "test_utils.h" +#include "test_net.h" +#include "../src/proxy/socks_proxy.h" +#include "../src/proxy/tcp_proxy_client.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" #include "../lib/ll_queue.h" @@ -58,18 +61,6 @@ static char g_cert[512], g_key[512]; static void* g_mon_id = NULL; static int g_file_seed = 0; -static int alloc_port(void) { - int s = socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) return -1; - struct sockaddr_in a; memset(&a, 0, sizeof(a)); - a.sin_family = AF_INET; a.sin_addr.s_addr = inet_addr("127.0.0.1"); - for (int i = 0; i < 200; i++) { - a.sin_port = htons((uint16_t)(20000 + (rand() % 40000))); - if (bind(s, (struct sockaddr*)&a, sizeof(a)) == 0) { close(s); return ntohs(a.sin_port); } - } - close(s); return -1; -} - static void gen_file(const char* path, int size, int seed) { FILE* f = fopen(path, "wb"); if (!f) { fprintf(stderr, "gen_file: fopen(%s) failed\n", path); return; } @@ -83,7 +74,7 @@ static void gen_file(const char* path, int size, int seed) { } // Сервер сообщает готовность после listen и настройки TLS; EOF обнаруживает ошибку запуска без паузы. -static int wait_server_ready(int fd, int port, const char* name) { +static int wait_server_ready(int fd, int* port, const char* name) { uint64_t start = get_time_us(), deadline = start + 5000000; struct pollfd pfd = {.fd = fd, .events = POLLIN}; int result; @@ -91,21 +82,23 @@ static int wait_server_ready(int fd, int port, const char* name) { uint64_t now = get_time_us(); result = poll(&pfd, 1, now < deadline ? (int)((deadline - now + 999) / 1000) : 0); } while (result < 0 && errno == EINTR); - char marker = 0; - ssize_t bytes = result > 0 ? read(fd, &marker, 1) : -1; + uint8_t assigned[2] = {0}; + ssize_t bytes = result > 0 ? read(fd, assigned, sizeof(assigned)) : -1; int error = result < 0 || (result > 0 && bytes < 0) ? errno : 0; close(fd); - if (result <= 0 || bytes != 1 || marker != 'R') { - DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test %s server startup failed: port=%d poll=%d events=%x bytes=%ld marker=%d errno=%d", - name, port, result, pfd.revents, (long)bytes, marker, error); + int actual = ((int)assigned[0] << 8) | assigned[1]; + if (result <= 0 || bytes != sizeof(assigned) || !actual) { + DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test %s server startup failed: poll=%d events=%x bytes=%ld port=%d errno=%d", + name, result, pfd.revents, (long)bytes, actual, error); return -1; } - DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "test %s server ready: port=%d elapsed=%lluus", name, port, + *port = actual; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "test %s server ready: port=%d elapsed=%lluus", name, *port, (unsigned long long)(get_time_us() - start)); return 0; } -static int start_http_server(int port, const char* dir) { +static int start_http_server(int* port, const char* dir) { char script[512]; snprintf(script, sizeof(script), "%s/_srv.py", dir); FILE* sf = fopen(script, "w"); if (!sf) return -1; @@ -130,7 +123,7 @@ static int start_http_server(int port, const char* dir) { "port=int(sys.argv[1])\n" "os.chdir(sys.argv[2])\n" "httpd=HTTPServer(('127.0.0.1',port),H)\n" - "ready=int(sys.argv[3]);os.write(ready,b'R');os.close(ready)\n" + "ready=int(sys.argv[3]);os.write(ready,httpd.server_port.to_bytes(2,'big'));os.close(ready)\n" "httpd.serve_forever()\n" ); fclose(sf); @@ -144,9 +137,8 @@ static int start_http_server(int port, const char* dir) { } if (pid == 0) { close(ready[0]); - char port_str[16], ready_str[16]; - snprintf(port_str, sizeof(port_str), "%d", port); snprintf(ready_str, sizeof(ready_str), "%d", ready[1]); - execlp("python3", "python3", script, port_str, dir, ready_str, (char*)NULL); + char ready_str[16]; snprintf(ready_str, sizeof(ready_str), "%d", ready[1]); + execlp("python3", "python3", script, "0", dir, ready_str, (char*)NULL); DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTP python exec: %s", strerror(errno)); _exit(1); } @@ -155,7 +147,7 @@ static int start_http_server(int port, const char* dir) { return wait_server_ready(ready[0], port, "HTTP"); } -static int start_https_server(int port, const char* dir, const char* cert, const char* key) { +static int start_https_server(int* port, const char* dir, const char* cert, const char* key) { char script[512]; snprintf(script, sizeof(script), "%s/_srv_https.py", dir); FILE* sf = fopen(script, "w"); if (!sf) return -1; @@ -187,7 +179,7 @@ static int start_https_server(int port, const char* dir, const char* cert, const "httpd=HTTPServer(('127.0.0.1',int(sys.argv[4])),H)\n" "httpd.socket=ctx.wrap_socket(httpd.socket,server_side=True)\n" "httpd.timeout=None\n" - "ready=int(sys.argv[5]);os.write(ready,b'R');os.close(ready)\n" + "ready=int(sys.argv[5]);os.write(ready,httpd.server_port.to_bytes(2,'big'));os.close(ready)\n" "httpd.serve_forever()\n" ); fclose(sf); @@ -201,9 +193,8 @@ static int start_https_server(int port, const char* dir, const char* cert, const } if (pid == 0) { close(ready[0]); - char port_str[16], ready_str[16]; - snprintf(port_str, sizeof(port_str), "%d", port); snprintf(ready_str, sizeof(ready_str), "%d", ready[1]); - execlp("python3", "python3", script, cert, key, dir, port_str, ready_str, (char*)NULL); + char ready_str[16]; snprintf(ready_str, sizeof(ready_str), "%d", ready[1]); + execlp("python3", "python3", script, cert, key, dir, "0", ready_str, (char*)NULL); DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTPS python exec: %s", strerror(errno)); _exit(1); } @@ -487,14 +478,6 @@ int main(void) { utun_instance_set_tun_init_enabled(0); srand((unsigned)time(NULL)); - g_http_port = alloc_port(); g_https_port = alloc_port(); - g_cli_port = alloc_port(); g_srv_port = alloc_port(); - g_socks_port = alloc_port(); g_http_proxy_port = alloc_port(); - if (g_http_port < 0 || g_https_port < 0 || g_cli_port < 0 || g_srv_port < 0 || g_socks_port < 0 || g_http_proxy_port < 0) { - printf("[FAIL] port allocation\n"); return 1; - } - printf(" ports: http=%d https=%d socks=%d http_proxy=%d\n", g_http_port, g_https_port, g_socks_port, g_http_proxy_port); - strcpy(g_tmpdir, "/tmp/utun_test_XXXXXX"); if (test_mkdtemp(g_tmpdir) < 0) { printf("[FAIL] mkdtemp: %s\n", strerror(errno)); return 1; } @@ -509,8 +492,8 @@ int main(void) { } printf(" test files: 1KB, 100KB, 1MB, 10MB (total %.1fMB)\n", total_mb / (1024.0*1024.0)); fflush(stdout); - if (start_http_server(g_http_port, g_tmpdir) < 0) { printf("[FAIL] start http server\n"); goto cleanup; } - if (start_https_server(g_https_port, g_tmpdir, g_cert, g_key) < 0) { printf("[FAIL] start https server\n"); goto cleanup; } + if (start_http_server(&g_http_port, g_tmpdir) < 0) { printf("[FAIL] start http server\n"); goto cleanup; } + if (start_https_server(&g_https_port, g_tmpdir, g_cert, g_key) < 0) { printf("[FAIL] start https server\n"); goto cleanup; } printf(" HTTP/HTTPS servers started\n"); g_ua = uasync_create(); @@ -519,6 +502,16 @@ int main(void) { g_srv = utun_instance_create_from_str(g_ua, make_cfg_server()); g_cli = utun_instance_create_from_str(g_ua, make_cfg_client()); if (!g_srv || !g_cli) { printf("[FAIL] instance create\n"); goto cleanup; } + test_etcp_set_peer(g_cli, "c1", g_srv, "s1"); + g_srv_port = ss_get_port(&test_etcp_bound_socket(g_srv, "s1")->local_addr); + g_cli_port = ss_get_port(&test_etcp_bound_socket(g_cli, "s1")->local_addr); + struct tcp_proxy_client* proxy = g_cli->tcp_proxy_client; + if (!proxy) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test: proxy client missing"); goto cleanup; } + g_socks_port = socks_proxy_get_port(proxy->socks_listen); + g_http_proxy_port = socks_proxy_get_port(proxy->http_listen); + if (!g_socks_port || !g_http_proxy_port) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test: proxy listeners missing"); goto cleanup; } + printf(" ports: http=%d https=%d socks=%d http_proxy=%d udp_client=%d udp_server=%d\n", + g_http_port, g_https_port, g_socks_port, g_http_proxy_port, g_cli_port, g_srv_port); if (utun_instance_init(g_srv) < 0 || utun_instance_init(g_cli) < 0) { printf("[FAIL] instance init\n"); goto cleanup; } printf(" waiting for ETCP...\n"); fflush(stdout);