Browse Source
- config_parser: tun_enabled в [global] (по умолчанию yes), не дефолтить tun_ifname если TUN не нужен - utun_instance: уважать tun_enabled, чистые дефолты tcp_proxy TUN - socks_proxy: фикс дэдлока — reply сразу после CONNECT, без ожидания DATA - utun.conf.sample: документирован tun_enabled, tun_ifname - test_socks_http_proxy: 50 запросов (SOCKS+HTTP), 2 воркера параллельно, файлы 1k..100k, сверка через cmp, python http.server + curlchatgui
7 changed files with 300 additions and 47 deletions
@ -0,0 +1,266 @@
|
||||
// test_socks_http_proxy.c — стресс-тест SOCKS5 + HTTP CONNECT proxy, многопоточная прокачка
|
||||
// 4 worker'а параллельно: 2×SOCKS + 2×HTTP, каждый по 50 запросов (1KB..1MB), всего 200 запросов
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <errno.h> |
||||
#include <unistd.h> |
||||
#include <signal.h> |
||||
#include <sys/socket.h> |
||||
#include <netinet/in.h> |
||||
#include <arpa/inet.h> |
||||
#include <sys/wait.h> |
||||
#include <sys/stat.h> |
||||
#include <time.h> |
||||
#include "../lib/platform_compat.h" |
||||
#include "test_utils.h" |
||||
#include "../lib/u_async.h" |
||||
#include "../lib/debug_config.h" |
||||
#include "../lib/ll_queue.h" |
||||
#include "../lib/mem.h" |
||||
#include "../src/etcp.h" |
||||
#include "../src/etcp_connections.h" |
||||
#include "../src/etcp_api.h" |
||||
#include "../src/etcp_router.h" |
||||
#include "../src/config_parser.h" |
||||
#include "../src/utun_instance.h" |
||||
#include "../src/routing.h" |
||||
#include "../src/tun_if.h" |
||||
|
||||
#define TIMEOUT_MS 60000 |
||||
#define WORKERS 2 // 1 SOCKS + 1 HTTP
|
||||
#define REQS_PER_WORKER 25 |
||||
|
||||
// Размеры тестовых файлов и сколько запросов каждого на одного worker'а (sum = 25)
|
||||
static const int file_sizes[] = { 1024, 10*1024, 50*1024, 100*1024 }; |
||||
static const int file_counts[] = { 8, 7, 5, 5 }; |
||||
|
||||
static struct UTUN_INSTANCE* g_cli = NULL; |
||||
static struct UTUN_INSTANCE* g_srv = NULL; |
||||
static struct UASYNC* g_ua = NULL; |
||||
static int g_ok = 0, g_done = 0; |
||||
static pid_t g_http_pid = 0; |
||||
static pid_t g_workers[WORKERS]; |
||||
static int g_http_port = 0, g_cli_port = 0, g_srv_port = 0; |
||||
static int g_socks_port = 0, g_http_proxy_port = 0; |
||||
static char g_tmpdir[256]; |
||||
static void* g_mon_id = NULL; |
||||
static int g_phase = 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) return; |
||||
for (int i = 0; i < size; i++) { uint8_t b = (uint8_t)((i ^ seed) & 0xFF); fwrite(&b, 1, 1, f); } |
||||
fclose(f); |
||||
} |
||||
|
||||
static int start_http_server(int port, const char* dir) { |
||||
pid_t pid = fork(); |
||||
if (pid < 0) return -1; |
||||
if (pid == 0) { |
||||
char port_str[16]; snprintf(port_str, sizeof(port_str), "%d", port); |
||||
execlp("python3", "python3", "-m", "http.server", port_str, "--directory", dir, (char*)NULL); |
||||
_exit(1); |
||||
} |
||||
g_http_pid = pid; |
||||
usleep(300000); |
||||
return 0; |
||||
} |
||||
|
||||
static char* make_cfg_server(void) { |
||||
static char buf[1024]; |
||||
snprintf(buf, sizeof(buf), |
||||
"[global]\ntun_enabled=no\n" |
||||
"my_node_id=0x2222000000000001\n" |
||||
"my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" |
||||
"my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" |
||||
"[server: s1]\naddr=127.0.0.1:%d\ntype=public\n" |
||||
"[allowed_keys]\nallow_all=1\n" |
||||
"[tcp_proxy_server]\nenabled=yes\n", g_srv_port); |
||||
return buf; |
||||
} |
||||
|
||||
static char* make_cfg_client(void) { |
||||
static char buf[1024]; |
||||
snprintf(buf, sizeof(buf), |
||||
"[global]\ntun_enabled=no\n" |
||||
"my_node_id=0x1111000000000001\n" |
||||
"my_private_key=38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68\n" |
||||
"my_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n" |
||||
"[server: s1]\naddr=127.0.0.1:%d\ntype=public\n" |
||||
"[client: c1]\nkeepalive=1\nlink=s1:127.0.0.1:%d\n" |
||||
"peer_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" |
||||
"[tcp_proxy_client]\n" |
||||
"enabled=yes\n" |
||||
"socks_enabled=yes\n" |
||||
"socks_addr=127.0.0.1:%d\n" |
||||
"http_proxy_enabled=yes\n" |
||||
"http_proxy_addr=127.0.0.1:%d\n" |
||||
"via_node=0x2222000000000001\n", |
||||
g_cli_port, g_srv_port, g_socks_port, g_http_proxy_port); |
||||
return buf; |
||||
} |
||||
|
||||
static int conn_ready(struct UTUN_INSTANCE* inst) { |
||||
if (!inst) return 0; |
||||
for (struct ETCP_CONN* c = inst->connections; c; c = c->next) |
||||
for (struct ETCP_LINK* l = c->links; l; l = l->next) |
||||
if (l->initialized && c->crypto_ctx.initialized) return 1; |
||||
return 0; |
||||
} |
||||
|
||||
// ====================================================================
|
||||
// Worker: дочерний процесс, делает curl-запросы через прокси и сверяет
|
||||
// ====================================================================
|
||||
static int worker_main(const char* type, const char* proxy, const char* url_base, const char* tmpdir) { |
||||
for (int fi = 0; fi < 4; fi++) { |
||||
int size = file_sizes[fi], count = file_counts[fi]; |
||||
char fname[32], expected[512], outfile[512]; |
||||
snprintf(fname, sizeof(fname), "f_%d.bin", size); |
||||
snprintf(expected, sizeof(expected), "%s/%s", tmpdir, fname); |
||||
snprintf(outfile, sizeof(outfile), "%s/out_%d_%s_%%d.bin", tmpdir, (int)getpid(), fname); |
||||
|
||||
for (int i = 0; i < count; i++) { |
||||
char cmd[1024], out[512], url[512]; |
||||
snprintf(out, sizeof(out), "%s/out_%d_%s_%d.bin", tmpdir, (int)getpid(), fname, i); |
||||
snprintf(url, sizeof(url), "%s/%s", url_base, fname); |
||||
if (strcmp(type, "socks") == 0) |
||||
snprintf(cmd, sizeof(cmd), "curl -s --connect-timeout 5 --max-time 5 --socks5-hostname %s -o %s %s 2>/dev/null", proxy, out, url); |
||||
else |
||||
snprintf(cmd, sizeof(cmd), "curl -s --connect-timeout 5 --max-time 5 --proxytunnel -x %s -o %s %s 2>/dev/null", proxy, out, url); |
||||
|
||||
int rc = system(cmd); |
||||
if (rc != 0) { fprintf(stderr, "[FAIL] worker %s %s iter %d curl exit %d\n", type, fname, i, WEXITSTATUS(rc)); return 1; } |
||||
|
||||
char cmp_cmd[1024]; |
||||
snprintf(cmp_cmd, sizeof(cmp_cmd), "cmp -s %s %s", out, expected); |
||||
if (system(cmp_cmd) != 0) { fprintf(stderr, "[FAIL] worker %s %s iter %d mismatch\n", type, fname, i); return 1; } |
||||
unlink(out); |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
// ====================================================================
|
||||
// Монитор-таймер
|
||||
// ====================================================================
|
||||
static void monitor(void* arg) { |
||||
(void)arg; |
||||
if (g_done) return; |
||||
|
||||
if (g_phase == 0) { |
||||
if (conn_ready(g_cli) && conn_ready(g_srv)) { |
||||
printf(" ETCP connected, launching %d workers...\n", WORKERS); fflush(stdout); |
||||
g_phase = 1; |
||||
char url[128]; snprintf(url, sizeof(url), "http://127.0.0.1:%d", g_http_port); |
||||
char socks_proxy[64]; snprintf(socks_proxy, sizeof(socks_proxy), "127.0.0.1:%d", g_socks_port); |
||||
char http_proxy[64]; snprintf(http_proxy, sizeof(http_proxy), "http://127.0.0.1:%d", g_http_proxy_port); |
||||
|
||||
for (int i = 0; i < WORKERS; i++) { |
||||
pid_t pid = fork(); |
||||
if (pid < 0) { printf("[FAIL] fork worker %d: %s\n", i, strerror(errno)); g_done = -1; return; } |
||||
if (pid == 0) { |
||||
int rc = worker_main(i < WORKERS/2 ? "socks" : "http", i < WORKERS/2 ? socks_proxy : http_proxy, url, g_tmpdir); |
||||
_exit(rc); |
||||
} |
||||
g_workers[i] = pid; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Проверяем завершение worker'ов
|
||||
if (g_phase == 1) { |
||||
int done_count = 0, fail = 0; |
||||
for (int i = 0; i < WORKERS; i++) { |
||||
if (g_workers[i] <= 0) { done_count++; continue; } |
||||
int status; pid_t r = waitpid(g_workers[i], &status, WNOHANG); |
||||
if (r == 0) continue; |
||||
g_workers[i] = 0; done_count++; |
||||
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) fail++; |
||||
} |
||||
if (done_count >= WORKERS) { |
||||
if (fail) { printf("[FAIL] %d/%d workers failed\n", fail, WORKERS); g_done = -1; } |
||||
else { g_ok = 1; g_done = 1; } |
||||
} |
||||
} |
||||
|
||||
if (!g_done) g_mon_id = uasync_set_timeout(g_ua, 500, NULL, monitor, "mon"); |
||||
} |
||||
|
||||
static void test_timeout(void* arg) { |
||||
(void)arg; |
||||
if (!g_done) { printf("[FAIL] timeout\n"); g_done = -1; } |
||||
} |
||||
|
||||
int main(void) { |
||||
printf("=== test_socks_http_proxy ===\n"); fflush(stdout); |
||||
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_categories(DEBUG_CATEGORY_ALL); |
||||
utun_instance_set_tun_init_enabled(0); |
||||
srand((unsigned)time(NULL)); |
||||
|
||||
g_http_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_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 socks=%d http_proxy=%d\n", g_http_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; } |
||||
|
||||
// Генерируем тестовые файлы (детерминированный паттерн)
|
||||
int seed = (int)time(NULL) ^ (int)getpid(); |
||||
for (int fi = 0; fi < 4; fi++) { |
||||
char path[512]; snprintf(path, sizeof(path), "%s/f_%d.bin", g_tmpdir, file_sizes[fi]); |
||||
gen_file(path, file_sizes[fi], seed + fi); |
||||
} |
||||
printf(" test files generated: 1k, 10k, 50k, 100k\n"); |
||||
|
||||
if (start_http_server(g_http_port, g_tmpdir) < 0) { printf("[FAIL] start http server\n"); goto cleanup; } |
||||
printf(" python3 http.server on :%d\n", g_http_port); |
||||
|
||||
g_ua = uasync_create(); |
||||
if (!g_ua) { printf("[FAIL] uasync_create\n"); goto cleanup; } |
||||
|
||||
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; } |
||||
|
||||
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); |
||||
|
||||
g_mon_id = uasync_set_timeout(g_ua, 500, NULL, monitor, "mon"); |
||||
void* to_id = uasync_set_timeout(g_ua, TIMEOUT_MS * 10, NULL, test_timeout, "to"); |
||||
|
||||
while (!g_done) uasync_poll(g_ua, 50); |
||||
|
||||
if (to_id) uasync_cancel_timeout(g_ua, to_id); |
||||
|
||||
if (g_ok) printf("[PASS] test_socks_http_proxy — %d reqs, 2 workers (socks+http), up to 100KB\n", WORKERS * REQS_PER_WORKER); |
||||
else printf("[FAIL] test_socks_http_proxy\n"); |
||||
|
||||
cleanup: |
||||
// Убиваем оставшихся workers
|
||||
for (int i = 0; i < WORKERS; i++) if (g_workers[i] > 0) { kill(g_workers[i], SIGKILL); waitpid(g_workers[i], NULL, 0); } |
||||
if (g_mon_id) uasync_cancel_timeout(g_ua, g_mon_id); |
||||
if (g_http_pid > 0) { kill(g_http_pid, SIGKILL); waitpid(g_http_pid, NULL, 0); } |
||||
if (g_cli) { g_cli->running = 0; utun_instance_destroy(g_cli); } |
||||
if (g_srv) { g_srv->running = 0; utun_instance_destroy(g_srv); } |
||||
if (g_ua) uasync_destroy(g_ua, 0); |
||||
{ char buf[512]; snprintf(buf, sizeof(buf), "rm -rf %s", g_tmpdir); system(buf); } |
||||
return g_ok ? 0 : 1; |
||||
} |
||||
Loading…
Reference in new issue