From 98c51cd0d5bded63e3bf90883c49421e5556def5 Mon Sep 17 00:00:00 2001 From: anonymous-contributor Date: Wed, 1 Feb 2017 18:18:43 +0800 Subject: [PATCH] Obfsproxy standalone mode (#1156) * ss-libev: Introduce mode parameter for start_plugin Since obfsproxy handles client and server in different ways, no matter standalone or manged mode, so introduce the mode for start_plugin() to support obfsproxy. With more comments added to plugin.h for later developers. Signed-off-by: Adam Anonymous * ss-libev: Move SS protocol plugin initialization code to start_ss_plugin Move SS protocol plugin initialization to start_ss_plugin() function, as the support for obfsproxy will not use that environment. Also, remove the use of get_current_dir_name() which is a glibc extension, such macro hack will just make code less readable. Not to mention it already screw up code folding of vim. Signed-off-by: Adam Anonymous * ss-libev: Introduce obfsproxy standalone mode support as plugin Now shadowsocks-libev supports to use obfsproxy as plugin. Currently we only support to use standalone(proxy) mode of obfsproxy, as managed mode needs to use SOCKS5 as upstream forward, while ss-libev doesn't support it yet. And the output from plugin is just trashed for now. Support to pipe them into stdout/stderr will be added later. Signed-off-by: Adam Anonymous * ss-libev: doc: Fix wrong cmdline parameter for plugin options Command line option for plugin options is "--plugin-opts", not the json option "--plugin_opts" Fix it. Signed-off-by: Adam Anonymous --- doc/ss-local.asciidoc | 4 +- doc/ss-manager.asciidoc | 4 +- doc/ss-redir.asciidoc | 4 +- doc/ss-server.asciidoc | 4 +- doc/ss-tunnel.asciidoc | 4 +- src/local.c | 2 +- src/plugin.c | 178 ++++++++++++++++++++++++++++++++-------- src/plugin.h | 39 ++++++++- src/redir.c | 2 +- src/server.c | 2 +- src/tunnel.c | 2 +- 11 files changed, 195 insertions(+), 50 deletions(-) diff --git a/doc/ss-local.asciidoc b/doc/ss-local.asciidoc index a1f2b0f2..42fdde55 100644 --- a/doc/ss-local.asciidoc +++ b/doc/ss-local.asciidoc @@ -14,7 +14,7 @@ SYNOPSIS [-t ] [-c ] [-i ] [-a ] [-b ] [--fast-open] [--acl ] [--mtu ] - [--plugin ] [--plugin_opts ] [--plugin-opts :: Enable SIP003 plugin. (Experimental) ---plugin_opts :: +--plugin-opts :: Set SIP003 plugin options. (Experimental) -v:: diff --git a/doc/ss-manager.asciidoc b/doc/ss-manager.asciidoc index 5c817e62..ac1e553d 100644 --- a/doc/ss-manager.asciidoc +++ b/doc/ss-manager.asciidoc @@ -15,7 +15,7 @@ SYNOPSIS [-b ] [-a ] [--manager-address ] [--executable ] - [--plugin ] [--plugin_opts ] + [--plugin ] [--plugin-opts ] DESCRIPTION ----------- @@ -109,7 +109,7 @@ Only available in manager mode. --plugin :: Enable SIP003 plugin. (Experimental) ---plugin_opts :: +--plugin-opts :: Set SIP003 plugin options. (Experimental) -v:: diff --git a/doc/ss-redir.asciidoc b/doc/ss-redir.asciidoc index 84b9c8bd..abec4b7c 100644 --- a/doc/ss-redir.asciidoc +++ b/doc/ss-redir.asciidoc @@ -13,7 +13,7 @@ SYNOPSIS [-k ] [-m ] [-f ] [-t ] [-c ] [-b ] [-a ] [-n ] [--mtu ] - [--plugin ] [--plugin_opts ] [--plugin-opts :: Enable SIP003 plugin. (Experimental) ---plugin_opts :: +--plugin-opts :: Set SIP003 plugin options. (Experimental) -v:: diff --git a/doc/ss-server.asciidoc b/doc/ss-server.asciidoc index 8c6a2464..0fffaca9 100644 --- a/doc/ss-server.asciidoc +++ b/doc/ss-server.asciidoc @@ -16,7 +16,7 @@ SYNOPSIS [-b ] [--mtu ] [--manager-address ] - [--plugin ] [--plugin_opts ] [--plugin-opts :: Enable SIP003 plugin. (Experimental) ---plugin_opts :: +--plugin-opts :: Set SIP003 plugin options. (Experimental) -v:: diff --git a/doc/ss-tunnel.asciidoc b/doc/ss-tunnel.asciidoc index ffd6ed88..17fb3878 100644 --- a/doc/ss-tunnel.asciidoc +++ b/doc/ss-tunnel.asciidoc @@ -14,7 +14,7 @@ SYNOPSIS [-t ] [-c ] [-i ] [-b ] [-a ] [-n ] [-L addr:port] [--mtu ] - [--plugin ] [--plugin_opts ] [--plugin-opts :: Enable SIP003 plugin. (Experimental) ---plugin_opts :: +--plugin-opts :: Set SIP003 plugin options. (Experimental) -v:: diff --git a/src/local.c b/src/local.c index a2bb6ac1..98674e3c 100644 --- a/src/local.c +++ b/src/local.c @@ -1453,7 +1453,7 @@ main(int argc, char **argv) len = strlen(remote_str); } int err = start_plugin(plugin, plugin_opts, remote_str, - remote_port, plugin_host, plugin_port); + remote_port, plugin_host, plugin_port, MODE_CLIENT); if (err) { FATAL("failed to start the plugin"); } diff --git a/src/plugin.c b/src/plugin.c index 8fd892bb..80454e93 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -78,51 +78,23 @@ struct cork_stream_consumer plugin_log = { .free = plugin_log__free, }; -int -start_plugin(const char *plugin, - const char *plugin_opts, - const char *remote_host, - const char *remote_port, - const char *local_host, - const char *local_port) +static int start_ss_plugin(const char *plugin, + const char *plugin_opts, + const char *remote_host, + const char *remote_port, + const char *local_host, + const char *local_port, + enum plugin_mode mode) { char *new_path = NULL; char *cmd = NULL; - if (plugin == NULL) - return -1; - - if (strlen(plugin) == 0) - return 0; - size_t plugin_len = strlen(plugin); size_t cmd_len = plugin_len + CMD_RESRV_LEN; cmd = ss_malloc(cmd_len); snprintf(cmd, cmd_len, "exec %s", plugin); - env = cork_env_clone_current(); - const char *path = cork_env_get(env, "PATH"); - if (path != NULL) { -#ifdef __GLIBC__ - char *cwd = get_current_dir_name(); - if (cwd) { -#else - char cwd[PATH_MAX]; - if (!getcwd(cwd, PATH_MAX)) { -#endif - size_t path_len = strlen(path) + strlen(cwd) + 2; - new_path = ss_malloc(path_len); - snprintf(new_path, path_len, "%s:%s", cwd, path); -#ifdef __GLIBC__ - free(cwd); -#endif - } - } - - if (new_path != NULL) - cork_env_add(env, "PATH", new_path); - cork_env_add(env, "SS_REMOTE_HOST", remote_host); cork_env_add(env, "SS_REMOTE_PORT", remote_port); @@ -148,6 +120,142 @@ start_plugin(const char *plugin, return err; } +#define OBFSPROXY_OPTS_MAX 4096 +/* + * For obfsproxy, we use standalone mode for now. + * Managed mode needs to use SOCKS5 proxy as forwarder, which is not supported + * yet. + * + * The idea of using standalone mode is quite simple, just assemble the + * internal port into obfsproxy parameters. + * + * Using manually ran scramblesuit as an example: + * obfsproxy \ + * --data-dir /tmp/ss_libev_plugin_with_suffix \ + * scramblesuit \ + * --password SOMEMEANINGLESSPASSWORDASEXAMPLE \ + * --dest some.server.org:12345 \ + * client \ + * 127.0.0.1:54321 + * + * In above case, @plugin = "obfsproxy", + * @plugin_opts = "scramblesuit --password SOMEMEANINGLESSPASSWORDASEXAMPLE" + * For obfs3, it's even easier, just pass @plugin = "obfsproxy" + * @plugin_opts = "obfs3" + * + * And the rest parameters are all assembled here. + * Some old obfsproxy will not be supported as it doesn't even support + * "--data-dir" option + */ +static int start_obfsproxy(const char *plugin, + const char *plugin_opts, + const char *remote_host, + const char *remote_port, + const char *local_host, + const char *local_port, + enum plugin_mode mode) +{ + char *pch; + char *opts_dump; + char buf[PATH_MAX]; + int ret; + + opts_dump = strndup(plugin_opts, OBFSPROXY_OPTS_MAX); + if (!opts_dump) { + ERROR("start_obfsproxy strndup failed"); + return -ENOMEM; + } + exec = cork_exec_new(plugin); + + /* The first parameter will be skipped, so pass @plugin again */ + cork_exec_add_param(exec, plugin); + + cork_exec_add_param(exec, "--data-dir"); + snprintf(buf, PATH_MAX, "/tmp/%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, " "); + } + + /* The rest options */ + if (mode == MODE_CLIENT) { + /* Client mode */ + cork_exec_add_param(exec, "--dest"); + snprintf(buf, PATH_MAX, "%s:%s", remote_host, remote_port); + cork_exec_add_param(exec, buf); + cork_exec_add_param(exec, "client"); + snprintf(buf, PATH_MAX, "%s:%s", local_host, local_port); + cork_exec_add_param(exec, buf); + } else { + /* Server mode */ + cork_exec_add_param(exec, "--dest"); + snprintf(buf, PATH_MAX, "%s:%s", local_host, local_port); + cork_exec_add_param(exec, buf); + cork_exec_add_param(exec, "server"); + snprintf(buf, PATH_MAX, "%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); + ret = cork_subprocess_start(sub); + ss_free(opts_dump); + return ret; +} + +int +start_plugin(const char *plugin, + const char *plugin_opts, + const char *remote_host, + const char *remote_port, + const char *local_host, + const char *local_port, + enum plugin_mode mode) +{ + char *new_path = NULL; + const char *current_path; + char cwd[PATH_MAX]; + size_t new_path_len; + int ret; + + if (plugin == NULL) + return -1; + + if (strlen(plugin) == 0) + return 0; + + /* + * Add current dir to PATH, so we can search plugin in current dir + */ + env = cork_env_clone_current(); + current_path = cork_env_get(env, "PATH"); + if (current_path != NULL) { + if (!getcwd(cwd, PATH_MAX)) { + new_path_len = strlen(current_path) + strlen(cwd) + 2; + new_path = ss_malloc(new_path_len); + snprintf(new_path, new_path_len, "%s:%s", cwd, current_path); + } + } + if (new_path != NULL) + cork_env_add(env, "PATH", new_path); + + if (!strncmp(plugin, "obfsproxy", strlen("obfsproxy"))) + ret = start_obfsproxy(plugin, plugin_opts, remote_host, remote_port, + local_host, local_port, mode); + else + ret = start_ss_plugin(plugin, plugin_opts, remote_host, remote_port, + local_host, local_port, mode); + ss_free(new_path); + env = NULL; + return ret; +} + uint16_t get_local_port() { diff --git a/src/plugin.h b/src/plugin.h index 71035a4d..84fdcc3e 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -27,12 +27,49 @@ #define PLUGIN_EXIT_NORMAL -1 #define PLUGIN_RUNNING 0 +enum plugin_mode { + MODE_CLIENT, + MODE_SERVER +}; + +/* + * XXX: Since we have SS plugins and obfsproxy support, for now we will + * do extra check against the plugin name. + * For obfsproxy, we will not follow the SS specified protocol and + * do special routine for obfsproxy. + * This may change when the protocol is finally settled + * + * Main function to start a plugin. + * + * @plugin: name of the plugin + * search from PATH and current directory. + * @plugin_opts: Special options for plugin + * @remote_host: + * CLIENT mode: + * The remote server address, which also runs corresponding plugin + * SERVER mode: + * The real listen address, which plugin will listen to + * @remote_port: + * CLIENT mode: + * The remote server port, which corresponding plugin is listening to + * SERVER mode: + * The real listen port, which plugin will listen to + * @local_host: + * Where ss-libev will connect/listen to. + * Normally localhost for both modes. + * @local_port: + * Where ss-libev will connect/listen to. + * Internal user port. + * @mode: + * Indicates which mode the plugin should run at. + */ int start_plugin(const char *plugin, const char *plugin_opts, const char *remote_host, const char *remote_port, const char *local_host, - const char *local_port); + const char *local_port, + enum plugin_mode mode); uint16_t get_local_port(); void stop_plugin(); int is_plugin_running(); diff --git a/src/redir.c b/src/redir.c index f03ca31a..3855e083 100644 --- a/src/redir.c +++ b/src/redir.c @@ -1028,7 +1028,7 @@ main(int argc, char **argv) len = strlen(remote_str); } int err = start_plugin(plugin, plugin_opts, remote_str, - remote_port, plugin_host, plugin_port); + remote_port, plugin_host, plugin_port, MODE_CLIENT); if (err) { FATAL("failed to start the plugin"); } diff --git a/src/server.c b/src/server.c index ef59bb78..8143635f 100644 --- a/src/server.c +++ b/src/server.c @@ -1884,7 +1884,7 @@ main(int argc, char **argv) } int err = start_plugin(plugin, plugin_opts, server_str, - plugin_port, "127.0.0.1", server_port); + plugin_port, "127.0.0.1", server_port, MODE_SERVER); if (err) { FATAL("failed to start the plugin"); } diff --git a/src/tunnel.c b/src/tunnel.c index 242c9175..a32f424e 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -1011,7 +1011,7 @@ main(int argc, char **argv) len = strlen(remote_str); } int err = start_plugin(plugin, plugin_opts, remote_str, - remote_port, plugin_host, plugin_port); + remote_port, plugin_host, plugin_port, MODE_CLIENT); if (err) { FATAL("failed to start the plugin"); }