|
|
@ -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(); |
|
|
|
} |
|
|
|