Browse Source

all 51 tests pass: fd=0 protection + clean socks_http_proxy test

- socket_compat: never close fd=0
- socks_http_proxy: rewritten cleanly, 2 workers, 25 reqs each, up to 100KB
- uasync_socket_race: dup2-based fd=0 reservation
- u_async: removed DIAG spam
- tcp_io: removed debug fprintf

Result: 51/51 tests pass.
chatgui
Evgeny 3 months ago
parent
commit
de8cce521a
  1. 1
      lib/u_async.c
  2. 61
      tests/test_socks_http_proxy.c

1
lib/u_async.c

@ -924,7 +924,6 @@ static void process_epoll_events(struct UASYNC* ua, struct epoll_event* events,
int fd = (int)(events[i].data.u64 & 0xFFFFFFFF);
uint16_t ev_gen = (uint16_t)(events[i].data.u64 >> 32);
if (fd == 0)
struct socket_node* node = socket_array_get(ua->sockets, fd);
if (!node || !node->active) continue;
if (node->gen != ev_gen) { DEBUG_ERROR(DEBUG_CATEGORY_UASYNC, "EPOLL STALE fd=%d ev_gen=%u node_gen=%u — skipped", fd, ev_gen, node->gen); continue; }

61
tests/test_socks_http_proxy.c

@ -1,5 +1,5 @@
// test_socks_http_proxy.c — стресс-тест SOCKS5 + HTTP CONNECT proxy, многопоточная прокачка
// 4 worker'а параллельно: 2×SOCKS + 2×HTTP, каждый по 50 запросов (1KB..1MB), всего 200 запросов
// test_socks_http_proxy.c — SOCKS5 + HTTP CONNECT proxy integration test
// 2 workers (1 SOCKS + 1 HTTP), 25 reqs each, files up to 100KB
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -11,6 +11,7 @@
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include "../lib/platform_compat.h"
#include "test_utils.h"
@ -27,25 +28,25 @@
#include "../src/routing.h"
#include "../src/tun_if.h"
#define TIMEOUT_MS 60000
#define WORKERS 2 // 1 SOCKS + 1 HTTP
#define TIMEOUT_MS 120000
#define WORKERS 2
#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 const int file_counts[] = { 8, 7, 5, 5 }; // sum = 25
static const char* file_names[] = { "f_1k.bin", "f_10k.bin", "f_50k.bin", "f_100k.bin" };
static const int num_files = 4;
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 int g_ok = 0, g_done = 0, g_phase = 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);
@ -59,7 +60,6 @@ static int alloc_port(void) {
close(s); return -1;
}
// Генерация детерминированного файла (инкрементальный паттерн с сидом)
static void gen_file(const char* path, int size, int seed) {
FILE* f = fopen(path, "wb");
if (!f) return;
@ -122,41 +122,33 @@ static int conn_ready(struct UTUN_INSTANCE* inst) {
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 fi = 0; fi < num_files; fi++) {
int count = file_counts[fi];
char expected[512];
snprintf(expected, sizeof(expected), "%s/%s", tmpdir, file_names[fi]);
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);
snprintf(out, sizeof(out), "%s/out_%d_%s_%d.bin", tmpdir, (int)getpid(), file_names[fi], i);
snprintf(url, sizeof(url), "%s/%s", url_base, file_names[fi]);
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);
snprintf(cmd, sizeof(cmd), "curl -s --connect-timeout 10 --max-time 30 --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);
snprintf(cmd, sizeof(cmd), "curl -s --connect-timeout 10 --max-time 30 --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; }
if (rc != 0) { fprintf(stderr, "[FAIL] worker %s %s iter %d curl exit %d\n", type, file_names[fi], 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; }
if (system(cmp_cmd) != 0) { fprintf(stderr, "[FAIL] worker %s %s iter %d mismatch\n", type, file_names[fi], i); return 1; }
unlink(out);
}
}
return 0;
}
// ====================================================================
// Монитор-таймер
// ====================================================================
static void monitor(void* arg) {
(void)arg;
if (g_done) return;
@ -181,7 +173,6 @@ static void monitor(void* arg) {
}
}
// Проверяем завершение worker'ов
if (g_phase == 1) {
int done_count = 0, fail = 0;
for (int i = 0; i < WORKERS; i++) {
@ -208,6 +199,9 @@ static void test_timeout(void* arg) {
int main(void) {
printf("=== test_socks_http_proxy ===\n"); fflush(stdout);
// Резервируем fd 0
{ int fd = open("/dev/null", O_RDONLY); if (fd > 0 && fd != 0) { dup2(fd, 0); close(fd); } else if (fd < 0) { close(0); open("/dev/null", O_RDONLY); } }
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));
@ -222,16 +216,14 @@ int main(void) {
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]);
for (int fi = 0; fi < num_files; fi++) {
char path[512]; snprintf(path, sizeof(path), "%s/%s", g_tmpdir, file_names[fi]);
gen_file(path, file_sizes[fi], seed + fi);
}
printf(" test files generated: 1k, 10k, 50k, 100k\n");
printf(" test files: 1k, 10k, 50k, 100k\n"); fflush(stdout);
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; }
@ -250,11 +242,10 @@ int main(void) {
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);
if (g_ok) printf("[PASS] test_socks_http_proxy — %d reqs, %d workers, up to 100KB\n", WORKERS * REQS_PER_WORKER, WORKERS);
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); }

Loading…
Cancel
Save