diff --git a/src/local.c b/src/local.c index 03a154ff..684230e1 100644 --- a/src/local.c +++ b/src/local.c @@ -109,6 +109,13 @@ static struct ev_signal sigterm_watcher; #ifndef __MINGW32__ static struct ev_signal sigchld_watcher; static struct ev_signal sigusr1_watcher; +#else +static struct plugin_watcher_t { + ev_io io; + SOCKET fd; + uint16_t port; + int valid; +} plugin_watcher; #endif #ifdef HAVE_SETRLIMIT @@ -123,6 +130,9 @@ static void remote_recv_cb(EV_P_ ev_io *w, int revents); static void remote_send_cb(EV_P_ ev_io *w, int revents); static void accept_cb(EV_P_ ev_io *w, int revents); static void signal_cb(EV_P_ ev_signal *w, int revents); +#if defined(__MINGW32__) && !defined(LIB_ONLY) +static void plugin_watcher_cb(EV_P_ ev_io *w, int revents); +#endif static int create_and_bind(const char *addr, const char *port); #ifdef HAVE_LAUNCHD @@ -1312,6 +1322,8 @@ signal_cb(EV_P_ ev_signal *w, int revents) #ifndef __MINGW32__ ev_signal_stop(EV_DEFAULT, &sigchld_watcher); ev_signal_stop(EV_DEFAULT, &sigusr1_watcher); +#else + ev_io_stop(EV_DEFAULT, &plugin_watcher.io); #endif keep_resolving = 0; ev_unloop(EV_A_ EVUNLOOP_ALL); @@ -1319,6 +1331,27 @@ signal_cb(EV_P_ ev_signal *w, int revents) } } +#if defined(__MINGW32__) && !defined(LIB_ONLY) +static void +plugin_watcher_cb(EV_P_ ev_io *w, int revents) +{ + char buf[1]; + SOCKET fd = accept(plugin_watcher.fd, NULL, NULL); + if (fd == INVALID_SOCKET) { + return; + } + recv(fd, buf, 1, 0); + closesocket(fd); + LOGE("plugin service exit unexpectedly"); + ret_val = -1; + ev_signal_stop(EV_DEFAULT, &sigint_watcher); + ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + ev_io_stop(EV_DEFAULT, &plugin_watcher.io); + keep_resolving = 0; + ev_unloop(EV_A_ EVUNLOOP_ALL); +} +#endif + void accept_cb(EV_P_ ev_io *w, int revents) { @@ -1364,9 +1397,7 @@ main(int argc, char **argv) char *plugin_opts = NULL; char *plugin_host = NULL; char *plugin_port = NULL; -#ifndef __MINGW32__ char tmp_port[8]; -#endif srand(time(NULL)); @@ -1596,7 +1627,6 @@ main(int argc, char **argv) #endif if (plugin != NULL) { -#ifndef __MINGW32__ uint16_t port = get_local_port(); if (port == 0) { FATAL("failed to find a free port"); @@ -1605,10 +1635,15 @@ main(int argc, char **argv) plugin_host = "127.0.0.1"; plugin_port = tmp_port; - LOGI("plugin \"%s\" enabled", plugin); -#else - FATAL("plugins not implemented in MinGW port"); +#ifdef __MINGW32__ + memset(&plugin_watcher, 0, sizeof(plugin_watcher)); + plugin_watcher.port = get_local_port(); + if (plugin_watcher.port == 0) { + LOGE("failed to assign a control port for plugin"); + } #endif + + LOGI("plugin \"%s\" enabled", plugin); } if (method == NULL) { @@ -1658,7 +1693,40 @@ main(int argc, char **argv) LOGI("resolving hostname to IPv6 address first"); } -#ifndef __MINGW32__ +#ifdef __MINGW32__ + // Listen on plugin control port + if (plugin != NULL && plugin_watcher.port != 0) { + SOCKET fd; + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd != INVALID_SOCKET) { + plugin_watcher.valid = 0; + do { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(plugin_watcher.port); + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) { + LOGE("failed to bind plugin control port"); + break; + } + if (listen(fd, 1)) { + LOGE("failed to listen on plugin control port"); + break; + } + plugin_watcher.fd = fd; + ev_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, EV_READ); + ev_io_start(EV_DEFAULT, &plugin_watcher.io); + plugin_watcher.valid = 1; + } while (0); + if (!plugin_watcher.valid) { + closesocket(fd); + plugin_watcher.port = 0; + } + } + } +#endif + if (plugin != NULL) { int len = 0; size_t buf_size = 256 * remote_num; @@ -1671,12 +1739,16 @@ main(int argc, char **argv) len = strlen(remote_str); } int err = start_plugin(plugin, plugin_opts, remote_str, - remote_port, plugin_host, plugin_port, MODE_CLIENT); + remote_port, plugin_host, plugin_port, +#ifdef __MINGW32__ + plugin_watcher.port, +#endif + MODE_CLIENT); if (err) { + ERROR("start_plugin"); FATAL("failed to start the plugin"); } } -#endif #ifndef __MINGW32__ // ignore SIGPIPE @@ -1800,11 +1872,9 @@ main(int argc, char **argv) } // Clean up -#ifndef __MINGW32__ if (plugin != NULL) { stop_plugin(); } -#endif if (mode != UDP_ONLY) { ev_io_stop(loop, &listen_ctx.io); @@ -1969,6 +2039,10 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat } #ifdef __MINGW32__ + if (plugin_watcher.valid) { + closesocket(plugin_watcher.fd); + } + winsock_cleanup(); #endif diff --git a/src/plugin.c b/src/plugin.c index f20c49ed..a33e08f3 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -24,26 +24,38 @@ #include "config.h" #endif -#ifndef __MINGW32__ #include +#ifndef __MINGW32__ #include #include #include #include +#endif #include #include #include "utils.h" #include "plugin.h" +#include "winsock.h" #define CMD_RESRV_LEN 128 +#ifndef __MINGW32__ +#define TEMPDIR "/tmp/" +#else +#define TEMPDIR +#endif + static int exit_code; static struct cork_env *env = NULL; static struct cork_exec *exec = NULL; static struct cork_subprocess *sub = NULL; +#ifdef __MINGW32__ +static uint16_t sub_control_port = 0; +void cork_subprocess_set_control(struct cork_subprocess *self, uint16_t port); +#endif static int plugin_log__data(struct cork_stream_consumer *vself, @@ -103,6 +115,9 @@ start_ss_plugin(const char *plugin, cork_exec_set_env(exec, env); sub = cork_subprocess_new_exec(exec, NULL, NULL, &exit_code); +#ifdef __MINGW32__ + cork_subprocess_set_control(sub, sub_control_port); +#endif return cork_subprocess_start(sub); } @@ -144,14 +159,19 @@ start_obfsproxy(const char *plugin, enum plugin_mode mode) { char *pch; - char *opts_dump; + char *opts_dump = NULL; char *buf = NULL; int ret, buf_size = 0; - opts_dump = strndup(plugin_opts, OBFSPROXY_OPTS_MAX); - if (!opts_dump) { - ERROR("start_obfsproxy strndup failed"); - return -ENOMEM; + if (plugin_opts != NULL) { + opts_dump = strndup(plugin_opts, OBFSPROXY_OPTS_MAX); + if (!opts_dump) { + ERROR("start_obfsproxy strndup failed"); + if (env != NULL) { + cork_env_free(env); + } + return -ENOMEM; + } } exec = cork_exec_new(plugin); @@ -162,17 +182,19 @@ start_obfsproxy(const char *plugin, buf_size = 20 + strlen(plugin) + strlen(remote_host) + strlen(remote_port) + strlen(local_host) + strlen(local_port); buf = ss_malloc(buf_size); - snprintf(buf, buf_size, "/tmp/%s_%s:%s_%s:%s", plugin, + snprintf(buf, buf_size, TEMPDIR "%s_%s:%s_%s:%s", plugin, remote_host, remote_port, local_host, local_port); cork_exec_add_param(exec, buf); /* * Iterate @plugin_opts by space */ - pch = strtok(opts_dump, " "); - while (pch) { - cork_exec_add_param(exec, pch); - pch = strtok(NULL, " "); + if (opts_dump != NULL) { + pch = strtok(opts_dump, " "); + while (pch) { + cork_exec_add_param(exec, pch); + pch = strtok(NULL, " "); + } } /* The rest options */ @@ -193,8 +215,12 @@ start_obfsproxy(const char *plugin, snprintf(buf, buf_size, "%s:%s", remote_host, remote_port); cork_exec_add_param(exec, buf); } + cork_exec_set_env(exec, env); sub = cork_subprocess_new_exec(exec, NULL, NULL, &exit_code); +#ifdef __MINGW32__ + cork_subprocess_set_control(sub, sub_control_port); +#endif ret = cork_subprocess_start(sub); ss_free(opts_dump); free(buf); @@ -208,11 +234,16 @@ start_plugin(const char *plugin, const char *remote_port, const char *local_host, const char *local_port, +#ifdef __MINGW32__ + uint16_t control_port, +#endif enum plugin_mode mode) { +#ifndef __MINGW32__ char *new_path = NULL; const char *current_path; size_t new_path_len; +#endif int ret; if (plugin == NULL) @@ -221,6 +252,7 @@ start_plugin(const char *plugin, if (strlen(plugin) == 0) return 0; +#ifndef __MINGW32__ /* * Add current dir to PATH, so we can search plugin in current dir */ @@ -244,6 +276,9 @@ start_plugin(const char *plugin, } if (new_path != NULL) cork_env_add(env, "PATH", new_path); +#else + sub_control_port = control_port; +#endif if (!strncmp(plugin, "obfsproxy", strlen("obfsproxy"))) ret = start_obfsproxy(plugin, plugin_opts, remote_host, remote_port, @@ -251,7 +286,9 @@ start_plugin(const char *plugin, else ret = start_ss_plugin(plugin, plugin_opts, remote_host, remote_port, local_host, local_port, mode); +#ifndef __MINGW32__ ss_free(new_path); +#endif env = NULL; return ret; } @@ -302,4 +339,3 @@ is_plugin_running() return 0; } -#endif diff --git a/src/plugin.h b/src/plugin.h index 090ad974..ec1340bb 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -69,6 +69,9 @@ int start_plugin(const char *plugin, const char *remote_port, const char *local_host, const char *local_port, +#ifdef __MINGW32__ + uint16_t control_port, +#endif enum plugin_mode mode); uint16_t get_local_port(); void stop_plugin(); diff --git a/src/server.c b/src/server.c index ff46fdd9..03dbab8e 100644 --- a/src/server.c +++ b/src/server.c @@ -145,6 +145,13 @@ static struct ev_signal sigint_watcher; static struct ev_signal sigterm_watcher; #ifndef __MINGW32__ static struct ev_signal sigchld_watcher; +#else +static struct plugin_watcher_t { + ev_io io; + SOCKET fd; + uint16_t port; + int valid; +} plugin_watcher; #endif static struct cork_dllist connections; @@ -1509,12 +1516,34 @@ signal_cb(EV_P_ ev_signal *w, int revents) ev_signal_stop(EV_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ ev_signal_stop(EV_DEFAULT, &sigchld_watcher); +#else + ev_io_stop(EV_DEFAULT, &plugin_watcher.io); #endif ev_unloop(EV_A_ EVUNLOOP_ALL); } } } +#ifdef __MINGW32__ +static void +plugin_watcher_cb(EV_P_ ev_io *w, int revents) +{ + char buf[1]; + SOCKET fd = accept(plugin_watcher.fd, NULL, NULL); + if (fd == INVALID_SOCKET) { + return; + } + recv(fd, buf, 1, 0); + closesocket(fd); + LOGE("plugin service exit unexpectedly"); + ret_val = -1; + ev_signal_stop(EV_DEFAULT, &sigint_watcher); + ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + ev_io_stop(EV_DEFAULT, &plugin_watcher.io); + ev_unloop(EV_A_ EVUNLOOP_ALL); +} +#endif + static void accept_cb(EV_P_ ev_io *w, int revents) { @@ -1584,9 +1613,7 @@ main(int argc, char **argv) char *server_port = NULL; char *plugin_opts = NULL; char *plugin_port = NULL; -#ifndef __MINGW32__ char tmp_port[8]; -#endif int server_num = 0; const char *server_host[MAX_REMOTE_NUM]; @@ -1815,7 +1842,6 @@ main(int argc, char **argv) #endif if (plugin != NULL) { -#ifndef __MINGW32__ uint16_t port = get_local_port(); if (port == 0) { FATAL("failed to find a free port"); @@ -1823,8 +1849,13 @@ main(int argc, char **argv) snprintf(tmp_port, 8, "%d", port); plugin_port = server_port; server_port = tmp_port; -#else - FATAL("plugins not implemented in MinGW port"); + +#ifdef __MINGW32__ + memset(&plugin_watcher, 0, sizeof(plugin_watcher)); + plugin_watcher.port = get_local_port(); + if (plugin_watcher.port == 0) { + LOGE("failed to assign a control port for plugin"); + } #endif } @@ -1913,7 +1944,40 @@ main(int argc, char **argv) if (nameservers != NULL) LOGI("using nameserver: %s", nameservers); -#ifndef __MINGW32__ +#ifdef __MINGW32__ + // Listen on plugin control port + if (plugin != NULL && plugin_watcher.port != 0) { + SOCKET fd; + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd != INVALID_SOCKET) { + plugin_watcher.valid = 0; + do { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(plugin_watcher.port); + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) { + LOGE("failed to bind plugin control port"); + break; + } + if (listen(fd, 1)) { + LOGE("failed to listen on plugin control port"); + break; + } + plugin_watcher.fd = fd; + ev_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, EV_READ); + ev_io_start(EV_DEFAULT, &plugin_watcher.io); + plugin_watcher.valid = 1; + } while (0); + if (!plugin_watcher.valid) { + closesocket(fd); + plugin_watcher.port = 0; + } + } + } +#endif + // Start plugin server if (plugin != NULL) { int len = 0; @@ -1928,12 +1992,16 @@ main(int argc, char **argv) } int err = start_plugin(plugin, plugin_opts, server_str, - plugin_port, "127.0.0.1", server_port, MODE_SERVER); + plugin_port, "127.0.0.1", server_port, +#ifdef __MINGW32__ + plugin_watcher.port, +#endif + MODE_SERVER); if (err) { + ERROR("start_plugin"); FATAL("failed to start the plugin"); } } -#endif // initialize listen context listen_ctx_t listen_ctx_list[server_num]; @@ -2040,11 +2108,9 @@ main(int argc, char **argv) ev_timer_stop(EV_DEFAULT, &block_list_watcher); -#ifndef __MINGW32__ if (plugin != NULL) { stop_plugin(); } -#endif // Clean up @@ -2069,6 +2135,10 @@ main(int argc, char **argv) } #ifdef __MINGW32__ + if (plugin_watcher.valid) { + closesocket(plugin_watcher.fd); + } + winsock_cleanup(); #endif diff --git a/src/tunnel.c b/src/tunnel.c index d2f6afcb..7970d33d 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -102,6 +102,13 @@ static struct ev_signal sigint_watcher; static struct ev_signal sigterm_watcher; #ifndef __MINGW32__ static struct ev_signal sigchld_watcher; +#else +static struct plugin_watcher_t { + ev_io io; + SOCKET fd; + uint16_t port; + int valid; +} plugin_watcher; #endif #ifndef __MINGW32__ @@ -759,6 +766,8 @@ signal_cb(EV_P_ ev_signal *w, int revents) ev_signal_stop(EV_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ ev_signal_stop(EV_DEFAULT, &sigchld_watcher); +#else + ev_io_stop(EV_DEFAULT, &plugin_watcher.io); #endif keep_resolving = 0; ev_unloop(EV_A_ EVUNLOOP_ALL); @@ -766,6 +775,27 @@ signal_cb(EV_P_ ev_signal *w, int revents) } } +#ifdef __MINGW32__ +static void +plugin_watcher_cb(EV_P_ ev_io *w, int revents) +{ + char buf[1]; + SOCKET fd = accept(plugin_watcher.fd, NULL, NULL); + if (fd == INVALID_SOCKET) { + return; + } + recv(fd, buf, 1, 0); + closesocket(fd); + LOGE("plugin service exit unexpectedly"); + ret_val = -1; + ev_signal_stop(EV_DEFAULT, &sigint_watcher); + ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + ev_io_stop(EV_DEFAULT, &plugin_watcher.io); + keep_resolving = 0; + ev_unloop(EV_A_ EVUNLOOP_ALL); +} +#endif + int main(int argc, char **argv) { @@ -790,9 +820,7 @@ main(int argc, char **argv) char *plugin_opts = NULL; char *plugin_host = NULL; char *plugin_port = NULL; -#ifndef __MINGW32__ char tmp_port[8]; -#endif int remote_num = 0; ss_addr_t remote_addr[MAX_REMOTE_NUM]; @@ -1012,7 +1040,6 @@ main(int argc, char **argv) #endif if (plugin != NULL) { -#ifndef __MINGW32__ uint16_t port = get_local_port(); if (port == 0) { FATAL("failed to find a free port"); @@ -1021,10 +1048,15 @@ main(int argc, char **argv) plugin_host = "127.0.0.1"; plugin_port = tmp_port; - LOGI("plugin \"%s\" enabled", plugin); -#else - FATAL("plugins not implemented in MinGW port"); +#ifdef __MINGW32__ + memset(&plugin_watcher, 0, sizeof(plugin_watcher)); + plugin_watcher.port = get_local_port(); + if (plugin_watcher.port == 0) { + LOGE("failed to assign a control port for plugin"); + } #endif + + LOGI("plugin \"%s\" enabled", plugin); } if (method == NULL) { @@ -1068,7 +1100,40 @@ main(int argc, char **argv) FATAL("tunnel port is not defined"); } -#ifndef __MINGW32__ +#ifdef __MINGW32__ + // Listen on plugin control port + if (plugin != NULL && plugin_watcher.port != 0) { + SOCKET fd; + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd != INVALID_SOCKET) { + plugin_watcher.valid = 0; + do { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(plugin_watcher.port); + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) { + LOGE("failed to bind plugin control port"); + break; + } + if (listen(fd, 1)) { + LOGE("failed to listen on plugin control port"); + break; + } + plugin_watcher.fd = fd; + ev_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, EV_READ); + ev_io_start(EV_DEFAULT, &plugin_watcher.io); + plugin_watcher.valid = 1; + } while (0); + if (!plugin_watcher.valid) { + closesocket(fd); + plugin_watcher.port = 0; + } + } + } +#endif + if (plugin != NULL) { int len = 0; size_t buf_size = 256 * remote_num; @@ -1080,12 +1145,16 @@ main(int argc, char **argv) len = strlen(remote_str); } int err = start_plugin(plugin, plugin_opts, remote_str, - remote_port, plugin_host, plugin_port, MODE_CLIENT); + remote_port, plugin_host, plugin_port, +#ifdef __MINGW32__ + plugin_watcher.port, +#endif + MODE_CLIENT); if (err) { + ERROR("start_plugin"); FATAL("failed to start the plugin"); } } -#endif #ifndef __MINGW32__ // ignore SIGPIPE @@ -1190,13 +1259,15 @@ main(int argc, char **argv) ev_run(loop, 0); -#ifndef __MINGW32__ if (plugin != NULL) { stop_plugin(); } -#endif #ifdef __MINGW32__ + if (plugin_watcher.valid) { + closesocket(plugin_watcher.fd); + } + winsock_cleanup(); #endif diff --git a/src/utils.h b/src/utils.h index 3b8167f9..113cefd4 100644 --- a/src/utils.h +++ b/src/utils.h @@ -191,6 +191,7 @@ extern int use_syslog; #endif #ifdef __MINGW32__ +// Override Windows built-in functions #ifdef ERROR #undef ERROR #endif diff --git a/src/winsock.c b/src/winsock.c index 2f065102..80c55956 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -25,6 +25,31 @@ #include "winsock.h" #include "utils.h" +#ifndef ENABLE_QUICK_EDIT +#define ENABLE_QUICK_EDIT 0x0040 +#endif + +#ifndef STD_INPUT_HANDLE +#define STD_INPUT_HANDLE ((DWORD)-10) +#endif + +static void +disable_quick_edit(void) +{ + DWORD mode = 0; + HANDLE console = GetStdHandle(STD_INPUT_HANDLE); + + // Get current console mode + if (console == NULL || !GetConsoleMode(console, &mode)) { + return; + } + + // Clear the quick edit bit in the mode flags + mode &= ~ENABLE_QUICK_EDIT; + mode |= ENABLE_EXTENDED_FLAGS; + SetConsoleMode(console, mode); +} + void winsock_init(void) { @@ -34,6 +59,8 @@ winsock_init(void) if (ret != 0) { FATAL("Failed to initialize winsock"); } + // Disable quick edit mode to prevent stuck + disable_quick_edit(); } void diff --git a/src/winsock.h b/src/winsock.h index 50c8c168..10ea0e9d 100644 --- a/src/winsock.h +++ b/src/winsock.h @@ -97,12 +97,11 @@ #undef ERROR #endif #define ERROR(s) ss_error(s) -#ifndef _UTILS_H -void ss_error(const char *s); -#endif -// Missing unistd.h functions +// Missing Unix functions #define sleep(x) Sleep((x) * 1000) +#define bzero(s,n) memset(s,0,n) +#define strndup(s,n) ss_strndup(s,n) // Winsock compatibility functions int setnonblocking(SOCKET socket);