|
|
|
|
@ -13,6 +13,7 @@
|
|
|
|
|
#include <netinet/in.h> |
|
|
|
|
#include <arpa/inet.h> |
|
|
|
|
#include <sys/wait.h> |
|
|
|
|
#include <poll.h> |
|
|
|
|
#endif |
|
|
|
|
#include <sys/stat.h> |
|
|
|
|
#include <fcntl.h> |
|
|
|
|
@ -46,6 +47,7 @@ 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, g_phase = 0; |
|
|
|
|
static int g_worker_failures; |
|
|
|
|
static pid_t g_http_pid = 0, g_https_pid = 0; |
|
|
|
|
static pid_t g_workers[WORKERS]; |
|
|
|
|
static pid_t g_workers_orig[WORKERS]; |
|
|
|
|
@ -80,6 +82,29 @@ static void gen_file(const char* path, int size, int seed) {
|
|
|
|
|
fclose(f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Сервер сообщает готовность после listen и настройки TLS; EOF обнаруживает ошибку запуска без паузы.
|
|
|
|
|
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; |
|
|
|
|
do { |
|
|
|
|
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; |
|
|
|
|
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); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
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) { |
|
|
|
|
char script[512]; snprintf(script, sizeof(script), "%s/_srv.py", dir); |
|
|
|
|
FILE* sf = fopen(script, "w"); |
|
|
|
|
@ -104,20 +129,30 @@ static int start_http_server(int port, const char* dir) {
|
|
|
|
|
" self.end_headers()\n" |
|
|
|
|
"port=int(sys.argv[1])\n" |
|
|
|
|
"os.chdir(sys.argv[2])\n" |
|
|
|
|
"HTTPServer(('127.0.0.1',port),H).serve_forever()\n" |
|
|
|
|
"httpd=HTTPServer(('127.0.0.1',port),H)\n" |
|
|
|
|
"ready=int(sys.argv[3]);os.write(ready,b'R');os.close(ready)\n" |
|
|
|
|
"httpd.serve_forever()\n" |
|
|
|
|
); |
|
|
|
|
fclose(sf); |
|
|
|
|
|
|
|
|
|
int ready[2]; |
|
|
|
|
if (pipe(ready) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTP ready pipe: %s", strerror(errno)); return -1; } |
|
|
|
|
pid_t pid = fork(); |
|
|
|
|
if (pid < 0) return -1; |
|
|
|
|
if (pid < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTP server fork: %s", strerror(errno)); |
|
|
|
|
close(ready[0]); close(ready[1]); return -1; |
|
|
|
|
} |
|
|
|
|
if (pid == 0) { |
|
|
|
|
char port_str[16]; snprintf(port_str, sizeof(port_str), "%d", port); |
|
|
|
|
execlp("python3", "python3", script, port_str, dir, (char*)NULL); |
|
|
|
|
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); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTP python exec: %s", strerror(errno)); |
|
|
|
|
_exit(1); |
|
|
|
|
} |
|
|
|
|
close(ready[1]); |
|
|
|
|
g_http_pid = pid; |
|
|
|
|
usleep(300000); |
|
|
|
|
return 0; |
|
|
|
|
return wait_server_ready(ready[0], port, "HTTP"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int start_https_server(int port, const char* dir, const char* cert, const char* key) { |
|
|
|
|
@ -152,20 +187,29 @@ 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" |
|
|
|
|
"httpd.serve_forever()\n" |
|
|
|
|
); |
|
|
|
|
fclose(sf); |
|
|
|
|
|
|
|
|
|
int ready[2]; |
|
|
|
|
if (pipe(ready) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTPS ready pipe: %s", strerror(errno)); return -1; } |
|
|
|
|
pid_t pid = fork(); |
|
|
|
|
if (pid < 0) return -1; |
|
|
|
|
if (pid < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTPS server fork: %s", strerror(errno)); |
|
|
|
|
close(ready[0]); close(ready[1]); return -1; |
|
|
|
|
} |
|
|
|
|
if (pid == 0) { |
|
|
|
|
char port_str[16]; snprintf(port_str, sizeof(port_str), "%d", port); |
|
|
|
|
execlp("python3", "python3", script, cert, key, dir, port_str, (char*)NULL); |
|
|
|
|
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); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTPS python exec: %s", strerror(errno)); |
|
|
|
|
_exit(1); |
|
|
|
|
} |
|
|
|
|
close(ready[1]); |
|
|
|
|
g_https_pid = pid; |
|
|
|
|
usleep(300000); |
|
|
|
|
return 0; |
|
|
|
|
return wait_server_ready(ready[0], port, "HTTPS"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int gen_self_signed_cert(const char* tmpdir, char* cert_out, size_t cert_sz, char* key_out, size_t key_sz) { |
|
|
|
|
@ -329,6 +373,7 @@ static int worker_main(int wtype, const char* socks_proxy, const char* http_prox
|
|
|
|
|
|
|
|
|
|
static void monitor(void* arg) { |
|
|
|
|
(void)arg; |
|
|
|
|
g_mon_id = NULL; |
|
|
|
|
if (g_done) return; |
|
|
|
|
|
|
|
|
|
if (g_phase == 0) { |
|
|
|
|
@ -373,16 +418,21 @@ static void monitor(void* arg) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (g_phase == 1) { |
|
|
|
|
int done_count = 0, fail = 0; |
|
|
|
|
int done_count = 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; |
|
|
|
|
if (r < 0 && errno == EINTR) continue; |
|
|
|
|
if (r < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test worker=%d pid=%d failed: waitpid=%d status=%d errno=%d", |
|
|
|
|
i, (int)g_workers[i], (int)r, r > 0 ? status : -1, r < 0 ? errno : 0); |
|
|
|
|
g_worker_failures++; |
|
|
|
|
} |
|
|
|
|
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; } |
|
|
|
|
if (g_worker_failures) { printf("[FAIL] %d/%d workers failed\n", g_worker_failures, WORKERS); g_done = -1; } |
|
|
|
|
else { g_ok = 1; g_done = 1; } |
|
|
|
|
|
|
|
|
|
printf("\n=== PROFILING ===\n"); |
|
|
|
|
@ -433,6 +483,7 @@ int main(void) {
|
|
|
|
|
{ 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); |
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO); |
|
|
|
|
utun_instance_set_tun_init_enabled(0); |
|
|
|
|
srand((unsigned)time(NULL)); |
|
|
|
|
|
|
|
|
|
|