Browse Source

update: utils refactor

master
dnomd343 2 years ago
parent
commit
e89161b52b
  1. 2
      include/common.h
  2. 1
      include/sip003.h
  3. 2
      include/utils/dns.h
  4. 1
      include/utils/logger.h
  5. 2
      include/utils/network.h
  6. 10
      include/utils/process.h
  7. 5
      src/local.c
  8. 1
      src/sip003.c
  9. 2
      src/utils/CMakeLists.txt
  10. 16
      src/utils/dns.c
  11. 12
      src/utils/logger.c
  12. 21
      src/utils/network.c
  13. 219
      src/utils/process.c

2
include/common.h

@ -1,7 +1,7 @@
#ifndef _COMMON_H_
#define _COMMON_H_
#define VERSION "0.9.2"
#define VERSION "0.9.3"
#define RANDOM_PORT_START 41952
#define RANDOM_PORT_END 65535

1
include/sip003.h

@ -11,6 +11,7 @@ typedef struct {
char *SS_PLUGIN_OPTIONS;
char *plugin_file;
char **shadowsocks_cmd;
int is_udp_proxy;
} sip003;
sip003* load_sip003(char *ss_default, bootstrap *info);

2
include/utils/dns.h

@ -1,6 +1,6 @@
#ifndef _DNS_H_
#define _DNS_H_
char* dns_resolve(char *domain);
char* dns_resolve(const char *domain);
#endif

1
include/utils/logger.h

@ -20,6 +20,7 @@ enum {
#define log_fatal(...) log_printf(LOG_FATAL, __VA_ARGS__)
extern int LOG_LEVEL;
void log_perror(char *prefix);
void log_printf(int level, const char *fmt, ...);
#endif

2
include/utils/network.h

@ -1,7 +1,7 @@
#ifndef _NETWORK_H_
#define _NETWORK_H_
extern int proxy_exit;
extern int PROXY_EXIT;
int is_ip_addr(char *address);
int get_available_port(unsigned short range_start, unsigned short range_end);

10
include/utils/process.h

@ -1,14 +1,8 @@
#ifndef _PROCESS_H_
#define _PROCESS_H_
extern char **shadowsocks_args;
extern char *plugin_file;
extern char *SS_REMOTE_HOST;
extern char *SS_REMOTE_PORT;
extern char *SS_LOCAL_HOST;
extern char *SS_LOCAL_PORT;
extern char *SS_PLUGIN_OPTIONS;
#include "sip003.h"
void start_bootstrap(char *ss_type, int is_udp_proxy);
void start_bootstrap(int local_mode, sip003 *service);
#endif

5
src/local.c

@ -32,9 +32,6 @@ int main(int argc, char *argv[]) {
init(argc, argv, help_msg);
log_info("Shadowsocks bootstrap local (%s)", VERSION);
bootstrap *info = load_info(argc, argv);
load_sip003("sslocal", info);
// start_bootstrap("sslocal", is_udp_proxy); // local or server mode
start_bootstrap(1, load_sip003("sslocal", info));
return 0;
}

1
src/sip003.c

@ -38,6 +38,7 @@ sip003* load_sip003(char *ss_default, bootstrap *info) { // load shadowsocks and
info->shadowsocks_opts[0] = info->shadowsocks; // fill with file name
service->plugin_file = NULL;
service->is_udp_proxy = info->is_udp_proxy;
if (info->plugin != NULL) { // with sip003 plugin
char *rand_port = int_to_string(get_available_port(RANDOM_PORT_START, RANDOM_PORT_END));
service->SS_REMOTE_HOST = info->server_addr;

2
src/utils/CMakeLists.txt

@ -1,2 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
aux_source_directory(. utils_src)
add_library(utils ${utils_src})

16
src/utils/dns.c

@ -1,17 +1,17 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "logger.h"
#include "dns.h"
char** init_dns_result();
char** add_dns_result(char **dns_result, char *str);
void free_dns_result(char **dns_result);
char** ipv4_dns_resolve(char *domain);
char** ipv6_dns_resolve(char *domain);
char** ipv4_dns_resolve(const char *domain);
char** ipv6_dns_resolve(const char *domain);
char** init_dns_result() { // 初始化DNS解析存储结构
char **dns_result = (char**)malloc(sizeof(char*));
@ -35,7 +35,7 @@ void free_dns_result(char **dns_result) { // 释放DNS解析结果
}
}
char** ipv4_dns_resolve(char *domain) { // DNS解析IPv4地址
char** ipv4_dns_resolve(const char *domain) { // DNS解析IPv4地址
char **result = init_dns_result();
char ip_str[16]; // IPv4地址字符串 (3 * 4 + 1 * 3 + 1)
struct sockaddr_in *ipv4_addr;
@ -45,7 +45,7 @@ char** ipv4_dns_resolve(char *domain) { // DNS解析IPv4地址
hint.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(domain, NULL, &hint, &answer); // 发起解析
if (ret != 0) { // 解析失败
printf("[Shadowsocks Bootstrap] IPv4 DNS resolve `%s`: %s\n", domain, gai_strerror(ret));
log_debug("IPv4 DNS resolve `%s`: %s", domain, gai_strerror(ret));
return result; // 返回空数据
}
for (p = answer; p != NULL; p = p->ai_next) { // 遍历解析结果
@ -57,7 +57,7 @@ char** ipv4_dns_resolve(char *domain) { // DNS解析IPv4地址
return result;
}
char** ipv6_dns_resolve(char *domain) { // DNS解析IPv6地址
char** ipv6_dns_resolve(const char *domain) { // DNS解析IPv6地址
char **result = init_dns_result();
char ip_str[40]; // IPv6地址字符串 (4 * 8 + 1 * 7 + 1)
struct sockaddr_in6 *ipv6_addr;
@ -67,7 +67,7 @@ char** ipv6_dns_resolve(char *domain) { // DNS解析IPv6地址
hint.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(domain, NULL, &hint, &answer); // 发起解析
if (ret != 0) { // 解析失败
printf("[Shadowsocks Bootstrap] IPv6 DNS resolve `%s`: %s\n", domain, gai_strerror(ret));
log_debug("IPv6 DNS resolve `%s`: %s", domain, gai_strerror(ret));
return result; // 返回空数据
}
for (p = answer; p != NULL; p = p->ai_next) { // 遍历解析结果
@ -79,7 +79,7 @@ char** ipv6_dns_resolve(char *domain) { // DNS解析IPv6地址
return result;
}
char* dns_resolve(char *domain) { // DNS解析 返回首个IP地址 IPv4优先
char* dns_resolve(const char *domain) { // DNS解析 返回首个IP地址 IPv4优先
int num = 0;
char **result = ipv4_dns_resolve(domain); // IPv4解析
while(result[num++] != NULL); // num - 1 为IPv4地址数

12
src/utils/logger.c

@ -40,3 +40,15 @@ void log_printf(int level, const char *fmt, ...) {
exit(1);
}
}
void log_perror(char *prefix) {
time_t t;
time(&t);
struct tm *lt = localtime(&t);
fprintf(stderr, "\x1b[36m[Bootstrap]\x1b[0m ");
fprintf(stderr, "\x1b[90m%04d-%02d-%02d %02d:%02d:%02d\x1b[0m",
lt->tm_year + 1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);
fprintf(stderr, " %s%s\x1b[0m ", log_color[3], log_string[3]); // error level
fflush(stderr);
perror(prefix);
}

21
src/utils/network.c

@ -7,11 +7,12 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include "network.h"
#include "logger.h"
#define TIMEOUT 15
#define BUFFER_SIZE 4096
int proxy_exit;
int PROXY_EXIT;
int get_random_num(int range_start, int range_end);
int check_port_available(unsigned int port, int is_udp, int is_ipv6);
@ -256,12 +257,12 @@ void ipv4_proxy(void *ipv4_info) { // 代理IPv4客户端
char *recv_buffer = (char*)malloc(BUFFER_SIZE); // 申请接收缓冲区内存
long recv_len = send_and_receive(info->server_ip, info->server_port, info->buffer, info->len, recv_buffer); // 服务端交互
if (recv_len < 0) { // 服务端超时
printf("[Shadowsocks Bootstrap] UDP Proxy: server return timeout\n");
log_warn("UDP Proxy: server return timeout");
} else {
if (sendto(info->ipv4_client_fd, recv_buffer, recv_len, 0, (struct sockaddr*)&(info->ipv4_client_addr), sizeof(info->ipv4_client_addr)) < 0) { // 服务端数据返回给客户端
perror("[Shadowsocks Bootstrap] IPv4 UDP return failed");
} else {
printf("[Shadowsocks Bootstrap] UDP Proxy: ↑ %ld bytes ↓ %ld bytes\n", info->len, recv_len);
log_info("UDP Proxy: ↑ %ld bytes ↓ %ld bytes", info->len, recv_len);
}
}
free(recv_buffer); // 释放接收缓冲区内存
@ -274,12 +275,12 @@ void ipv6_proxy(void *ipv6_info) { // 代理IPv6客户端
char *recv_buffer = (char*)malloc(BUFFER_SIZE); // 申请接收缓冲区内存
long recv_len = send_and_receive(info->server_ip, info->server_port, info->buffer, info->len, recv_buffer); // 服务端交互
if (recv_len < 0) { // 服务端超时
printf("[Shadowsocks Bootstrap] Server return timeout\n");
log_warn("UDP Proxy: Server return timeout");
} else {
if (sendto(info->ipv6_client_fd, recv_buffer, recv_len, 0, (struct sockaddr*)&(info->ipv6_client_addr), sizeof(info->ipv6_client_addr)) < 0) { // 服务端数据返回给客户端
perror("[Shadowsocks Bootstrap] IPv6 UDP return failed");
} else {
printf("[Shadowsocks Bootstrap] UDP Proxy: ↑ %ld bytes ↓ %ld bytes\n", info->len, recv_len);
log_info("UDP Proxy: ↑ %ld bytes ↓ %ld bytes", info->len, recv_len);
}
}
free(recv_buffer); // 释放接收缓冲区内存
@ -310,12 +311,12 @@ void proxy(char *server_ip, int server_port, char *listen_ip, int listen_port) {
}
}
if (bind_error_flag) { // 端口被占用
printf("[Shadowsocks Bootstrap] The UDP port seems to be occupied by the SIP003 plugin\n");
printf("[Shadowsocks Bootstrap] WARNING: UDP communication of the agent will not work properly\n");
log_warn("The UDP port seems to be occupied by the SIP003 plugin");
log_warn("UDP communication of the agent will not work properly");
return;
}
proxy_exit = 0; // 重置退出标识
printf("[Shadowsocks Bootstrap] UDP Proxy: %s:%d -> %s:%d\n", listen_ip, listen_port, server_ip, server_port);
PROXY_EXIT = 0; // 重置退出标识
log_info("UDP Proxy: %s:%d -> %s:%d", listen_ip, listen_port, server_ip, server_port);
for (;;) {
if (!is_listen_ipv6) { // IPv4客户端
recv_len = recvfrom(ipv4_client_fd, recv_buffer, BUFFER_SIZE, 0, (struct sockaddr*)&ipv4_client_addr, &ipv4_client_addr_len);
@ -342,7 +343,7 @@ void proxy(char *server_ip, int server_port, char *listen_ip, int listen_port) {
info->len = recv_len;
pthread_create(&tid, NULL, (void*)ipv6_proxy, (void*)info); // 新线程代理请求
}
if (proxy_exit) {
if (PROXY_EXIT) {
break; // 退出代理
}
}

219
src/utils/process.c

@ -1,52 +1,56 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include "network.h"
#include "process.h"
#include "logger.h"
#include "dns.h"
char **shadowsocks_args;
char *plugin_file;
char *SS_REMOTE_HOST;
char *SS_REMOTE_PORT;
char *SS_LOCAL_HOST;
char *SS_LOCAL_PORT;
char *SS_PLUGIN_OPTIONS;
char **plugin_env;
char *plugin_arg[] = { NULL, NULL };
int exiting = 0;
int exit_complete = 0;
typedef struct exit_info {
typedef struct {
int pid;
int exit_code;
int exit_signal;
} exit_info;
//GMainLoop* main_loop;
char *plugin;
pid_t ss_pid = 0, plugin_pid = 0;
int exiting = 0; // sub process exiting
int exited = 0; // all sub process exited
//void process_exec();
//void get_sub_exit();
//void exit_with_child();
//void plugin_env_load();
//void show_exit_info(exit_info info);
//exit_info get_exit_info(int status, pid_t pid);
void error_exit();
void normal_exit();
void kill_sub_process();
void normal_exit() { // exit normally
kill_sub_process();
log_info("Exit complete");
exit(0);
}
void show_params();
void process_exec();
void get_sub_exit();
void exit_with_child();
void plugin_env_load();
void show_exit_info(exit_info info);
exit_info get_exit_info(int status, pid_t pid);
void error_exit() { // exit with error
kill_sub_process();
log_info("Exit with error");
exit(1);
}
void show_exit_info(exit_info info) { // show info of child process death
printf("(PID = %d) -> ", info.pid);
void show_exit_info(exit_info info, char *prefix) { // show info of child process death
if (info.exit_code != -1) { // exit normally
printf("exit code %d.\n", info.exit_code);
log_warn("%s (PID = %d) -> exit code %d", prefix, info.pid, info.exit_code);
} else if (info.exit_signal != -1) { // abnormal exit
printf("killed by signal %d.\n", info.exit_signal);
log_warn("%s (PID = %d) -> killed by signal %d", prefix, info.pid, info.exit_signal);
} else {
printf("unknown reason.\n");
log_warn("%s (PID = %d) -> unknown reason", prefix, info.pid);
}
}
@ -63,6 +67,25 @@ exit_info get_exit_info(int status, pid_t pid) { // get why the child process de
return temp;
}
void kill_sub_process() { // kill child process
while (exiting) {
sleep(1); // exit process already working -> block
}
exiting = 1; // exit process flag
PROXY_EXIT = 1; // udp proxy cancel
if (ss_pid != 0) {
kill(ss_pid, SIGKILL);
log_info("Kill shadowsocks process");
}
if (plugin != NULL && plugin_pid != 0) {
kill(plugin_pid, SIGKILL);
log_info("Kill plugin process");
}
int status;
log_info("Wait child process exit");
waitpid(0, &status, 0); // block wait
}
void get_sub_exit() { // catch child process die
exit_info sub_exit_info;
int ss_ret, plugin_ret, ss_status, plugin_status;
@ -75,67 +98,68 @@ void get_sub_exit() { // catch child process die
ss_ret = waitpid(ss_pid, &ss_status, WNOHANG); // non-blocking
if (ss_ret == -1) {
perror("[Shadowsocks Bootstrap] shadowsocks waitpid error");
exit_with_child();
error_exit();
} else if (ss_ret) { // ss exit
sub_exit_info = get_exit_info(ss_status, ss_pid);
printf("[Shadowsocks Bootstrap] shadowsocks exit ");
show_exit_info(sub_exit_info);
exit_with_child();
show_exit_info(sub_exit_info, "Shadowsocks exit");
error_exit();
}
}
if (plugin_file != NULL && plugin_pid != 0) { // with plugin
if (plugin != NULL && plugin_pid != 0) { // with plugin
plugin_ret = waitpid(plugin_pid, &plugin_status, WNOHANG); // non-blocking
if (plugin_ret == -1) {
perror("[Shadowsocks Bootstrap] plugin waitpid error");
exit_with_child();
error_exit();
} else if (plugin_ret) { // plugin exit
sub_exit_info = get_exit_info(plugin_status, plugin_pid);
printf("[Shadowsocks Bootstrap] plugin exit ");
show_exit_info(sub_exit_info);
exit_with_child();
show_exit_info(sub_exit_info, "Plugin exit");
error_exit();
}
}
// g_main_loop_quit(main_loop); // exit main loop
exit_complete = 1;
exited = 1;
}
void plugin_env_load() { // load plugin's environment variable
char** plugin_env_load(sip003 *service) { // load plugin's environment variable
char *remote_host = "SS_REMOTE_HOST=";
char *remote_port = "SS_REMOTE_PORT=";
char *local_host = "SS_LOCAL_HOST=";
char *local_port = "SS_LOCAL_PORT=";
char *plugin_options = "SS_PLUGIN_OPTIONS=";
plugin_env = (char**)malloc(sizeof(char*) * 6);
plugin_env[0] = (char*)malloc(strlen(remote_host) + strlen(SS_REMOTE_HOST) + 1);
plugin_env[1] = (char*)malloc(strlen(remote_port) + strlen(SS_REMOTE_PORT) + 1);
plugin_env[2] = (char*)malloc(strlen(local_host) + strlen(SS_LOCAL_HOST) + 1);
plugin_env[3] = (char*)malloc(strlen(local_port) + strlen(SS_LOCAL_PORT) + 1);
strcat(strcpy(plugin_env[0], remote_host), SS_REMOTE_HOST);
strcat(strcpy(plugin_env[1], remote_port), SS_REMOTE_PORT);
strcat(strcpy(plugin_env[2], local_host), SS_LOCAL_HOST);
strcat(strcpy(plugin_env[3], local_port), SS_LOCAL_PORT);
if (SS_PLUGIN_OPTIONS == NULL) {
char **plugin_env = (char**)malloc(sizeof(char*) * 6);
plugin_env[0] = (char*)malloc(strlen(remote_host) + strlen(service->SS_REMOTE_HOST) + 1);
plugin_env[1] = (char*)malloc(strlen(remote_port) + strlen(service->SS_REMOTE_PORT) + 1);
plugin_env[2] = (char*)malloc(strlen(local_host) + strlen(service->SS_LOCAL_HOST) + 1);
plugin_env[3] = (char*)malloc(strlen(local_port) + strlen(service->SS_LOCAL_PORT) + 1);
strcat(strcpy(plugin_env[0], remote_host), service->SS_REMOTE_HOST);
strcat(strcpy(plugin_env[1], remote_port), service->SS_REMOTE_PORT);
strcat(strcpy(plugin_env[2], local_host), service->SS_LOCAL_HOST);
strcat(strcpy(plugin_env[3], local_port), service->SS_LOCAL_PORT);
if (service->SS_PLUGIN_OPTIONS == NULL) {
plugin_env[4] = NULL;
} else {
plugin_env[4] = (char*)malloc(strlen(plugin_options) + strlen(SS_PLUGIN_OPTIONS) + 1);
strcat(strcpy(plugin_env[4], plugin_options), SS_PLUGIN_OPTIONS);
plugin_env[4] = (char*)malloc(strlen(plugin_options) + strlen(service->SS_PLUGIN_OPTIONS) + 1);
strcat(strcpy(plugin_env[4], plugin_options), service->SS_PLUGIN_OPTIONS);
}
plugin_env[5] = NULL;
plugin_arg[0] = plugin_file;
return plugin_env;
}
void process_exec() { // run shadowsocks main process and plugin (as child process)
void process_exec(sip003 *service) { // run shadowsocks main process and plugin
if ((ss_pid = fork()) < 0) {
perror("[Shadowsocks Bootstrap] fork error");
exit_with_child();
error_exit();
} else if (ss_pid == 0) { // child process
prctl(PR_SET_PDEATHSIG, SIGKILL); // child die with his father
if (execvp(shadowsocks_args[0], shadowsocks_args) < 0) {
perror("[Shadowsocks Bootstrap] shadowsocks exec error");
if (execvp(service->shadowsocks_cmd[0], service->shadowsocks_cmd) < 0) {
log_perror("Shadowsocks exec error");
// perror("[Shadowsocks Bootstrap] shadowsocks exec error");
exit(2);
}
}
if (plugin_file == NULL) { // plugin no need
plugin = service->plugin_file;
if (plugin == NULL) { // plugin no need
return;
}
usleep(100 * 1000); // sleep 100ms (python always a little slower)
@ -144,77 +168,52 @@ void process_exec() { // run shadowsocks main process and plugin (as child proce
}
if ((plugin_pid = fork()) < 0) {
perror("[Shadowsocks Bootstrap] fork error");
exit_with_child();
error_exit();
} else if (plugin_pid == 0) { // child process
prctl(PR_SET_PDEATHSIG, SIGKILL); // child die with his father
plugin_env_load();
if (execvpe(plugin_file, plugin_arg, plugin_env) < 0) {
char **plugin_env = plugin_env_load(service);
char *plugin_arg[] = { plugin, NULL };
if (execvpe(plugin, plugin_arg, plugin_env) < 0) {
perror("[Shadowsocks Bootstrap] plugin exec error");
exit(2);
}
}
}
void exit_with_child() { // exit and kill his child process
while (exiting) {
sleep(1); // block
}
exiting = 1;
proxy_exit = 1;
if (ss_pid != 0) {
kill(ss_pid, SIGKILL);
printf("[Shadowsocks Bootstrap] kill shadowsocks process.\n");
}
if (plugin_file != NULL && plugin_pid != 0) {
kill(plugin_pid, SIGKILL);
printf("[Shadowsocks Bootstrap] kill plugin process.\n");
}
int status;
printf("[Shadowsocks Bootstrap] wait for child process.\n");
waitpid(0, &status, 0); // block
printf("[Shadowsocks Bootstrap] exit.\n");
exit(1);
}
void start_bootstrap(char *ss_type, int is_udp_proxy) { // start shadowsocks and plugin (optional)
show_params();
// main_loop = g_main_loop_new(NULL, FALSE);
signal(SIGINT, exit_with_child); // catch Ctrl + C (2)
signal(SIGTERM, exit_with_child); // catch exit signal (15)
void start_bootstrap(int local_mode, sip003 *service) { // start shadowsocks (and plugin)
log_info("Start shadowsocks bootstrap");
signal(SIGINT, normal_exit); // catch Ctrl + C (2)
signal(SIGTERM, normal_exit); // catch exit signal (15)
signal(SIGCHLD, get_sub_exit); // callback when child process die
process_exec(); // exec child process
if (plugin_file != NULL && is_udp_proxy) { // start udp proxy when using plugin
process_exec(service); // exec child process
if (service->plugin_file == NULL || !service->is_udp_proxy) { // start udp proxy when using plugin
log_info("UDP Proxy no need");
} else {
char *remote_ip;
if (is_ip_addr(SS_REMOTE_HOST)) { // remote_host -> ip address
remote_ip = SS_REMOTE_HOST;
if (is_ip_addr(service->SS_REMOTE_HOST)) { // remote_host -> ip address
remote_ip = service->SS_REMOTE_HOST;
} else { // remote_host -> domain
printf("[Shadowsocks Bootstrap] DNS Resolve: %s\n", SS_REMOTE_HOST);
remote_ip = dns_resolve(SS_REMOTE_HOST); // dns resolve
log_info("DNS Resolve: %s", service->SS_REMOTE_HOST);
remote_ip = dns_resolve(service->SS_REMOTE_HOST); // dns resolve
if (remote_ip == NULL) { // no result
printf("[Shadowsocks Bootstrap] DNS record not found.\n");
log_warn("DNS record not found");
} else { // dns resolve success
printf("[Shadowsocks Bootstrap] %s => %s\n", SS_REMOTE_HOST, remote_ip);
log_info("%s => %s", service->SS_REMOTE_HOST, remote_ip);
}
}
if (remote_ip == NULL) { // resolve error
printf("[Shadowsocks Bootstrap] Skip UDP Proxy.\n");
log_warn("Skip UDP Proxy");
} else { // udp proxy
if (!strcmp(ss_type, "sslocal")) { // local mode
proxy(remote_ip, atoi(SS_REMOTE_PORT), SS_LOCAL_HOST, atoi(SS_LOCAL_PORT)); // NOLINT
if (local_mode) { // local mode
proxy(remote_ip, atoi(service->SS_REMOTE_PORT), service->SS_LOCAL_HOST, atoi(service->SS_LOCAL_PORT)); // NOLINT
} else { // server mode
proxy(SS_LOCAL_HOST, atoi(SS_LOCAL_PORT), remote_ip, atoi(SS_REMOTE_PORT)); // NOLINT
proxy(service->SS_LOCAL_HOST, atoi(service->SS_LOCAL_PORT), remote_ip, atoi(service->SS_REMOTE_PORT)); // NOLINT
}
}
} else {
printf("[Shadowsocks Bootstrap] UDP Proxy no need.\n");
}
while (!exit_complete) {
while (!exited) {
pause();
printf("!!! get signal !!!\n");
}
exit_with_child();
normal_exit();
}

Loading…
Cancel
Save