From 73d8f01a1301a04557ee593989573dcbedb20413 Mon Sep 17 00:00:00 2001 From: PantherJohn Date: Sun, 30 Jun 2019 13:21:55 +0800 Subject: [PATCH] fix some bugs and clean up --- acl/delegation.acl | 1 + src/Makefile.am | 2 +- src/cache.c | 5 +- src/local.c | 337 +------------------------ src/local.h | 92 ------- src/netutils.c | 44 +--- src/netutils.h | 3 +- src/plugin.c | 3 + src/redir.c | 324 ++++-------------------- src/redir.h | 76 ------ src/relay.c | 601 ++++++++++++++++++++++++++++++++++++++++++++- src/relay.h | 5 +- src/server.c | 403 +----------------------------- src/server.h | 115 --------- src/tunnel.c | 295 +--------------------- src/tunnel.h | 79 ------ 16 files changed, 690 insertions(+), 1695 deletions(-) delete mode 100644 src/local.h delete mode 100644 src/redir.h delete mode 100644 src/server.h delete mode 100644 src/tunnel.h diff --git a/acl/delegation.acl b/acl/delegation.acl index 6b1396ec..65b94200 100644 --- a/acl/delegation.acl +++ b/acl/delegation.acl @@ -4,6 +4,7 @@ #[bypass_all] [bypass_list] +127.0.0.1 (^|\.)bing\.com$ [proxy_list: Seattle, WA] diff --git a/src/Makefile.am b/src/Makefile.am index ddeae6f3..02464150 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -121,6 +121,6 @@ libshadowsocks_libev_la_LIBADD = $(ss_local_LDADD) include_HEADERS = shadowsocks.h noinst_HEADERS = acl.h crypto.h stream.h aead.h json.h netutils.h tls.h uthash.h \ - cache.h http.h plugin.h resolv.h tunnel.h utils.h base64.h ppbloom.h \ + cache.h http.h plugin.h resolv.h utils.h base64.h ppbloom.h \ common.h jconf.h manager.h protocol.h rule.h socks5.h udprelay.h relay.h winsock.h EXTRA_DIST = ss-nat diff --git a/src/cache.c b/src/cache.c index 56c47f2e..f73f8371 100644 --- a/src/cache.c +++ b/src/cache.c @@ -220,11 +220,10 @@ cache_lookup(struct cache *cache, void *key, size_t key_len, void *result) tmp->ts = ev_time(); HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp); *(void **)result = tmp->data; // okay no memcpy here sweetie - } else { - *(void **)result = NULL; + return 0; } - return 0; + return -1; } int diff --git a/src/local.c b/src/local.c index 585cfcd2..4013e76c 100644 --- a/src/local.c +++ b/src/local.c @@ -85,17 +85,6 @@ char *stat_path = NULL; #endif static int no_delay = 0; -static int ret_val = 0; - -struct ev_signal sigint_watcher; -struct ev_signal sigterm_watcher; -#ifndef __MINGW32__ -struct ev_signal sigchld_watcher; -struct ev_signal sigusr1_watcher; -#endif - -static void accept_cb(EV_P_ ev_io *w, int revents); -static void signal_cb(EV_P_ ev_signal *w, int revents); static int server_handshake_reply(EV_P_ ev_io *w, int udp_assc, struct socks5_response *response) @@ -684,33 +673,6 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } } -static void -signal_cb(EV_P_ ev_signal *w, int revents) -{ - if (revents & EV_SIGNAL) { - switch (w->signum) { -#ifndef __MINGW32__ - case SIGCHLD: - if (!is_plugin_running()) { - LOGE("plugin service exit unexpectedly"); - ret_val = -1; - } else - return; - case SIGUSR1: -#endif - case SIGINT: - case SIGTERM: - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); -#ifndef __MINGW32__ - ev_signal_stop(EV_DEFAULT, &sigchld_watcher); - ev_signal_stop(EV_DEFAULT, &sigusr1_watcher); -#endif - ev_unloop(EV_A_ EVUNLOOP_ALL); - } - } -} - void accept_cb(EV_P_ ev_io *w, int revents) { @@ -738,293 +700,10 @@ int new_shadowsocks_(ssocks_module_t module, jconf_t *conf, ss_callback_t callback, void *data) { - int i; - int plugin_enabled = 0; - - if (!(conf->remotes != NULL && - conf->remote_num > 0)) { - LOGE("at least one server should be specified"); - return -1; - } - -#ifndef HAVE_LAUNCHD - if (conf->local_port == NULL) { - conf->local_port = "0"; - LOGE("warning: random local port will be assigned"); - } -#endif - - if (conf->log) { - USE_LOGFILE(conf->log); - LOGI("enabled %slogging %s", conf->verbose ? "verbose " : "", conf->log); - } - - if (conf->mtu > 0) { - LOGI("setting MTU to %d", conf->mtu); - } - - if (conf->mptcp) { - LOGI("enabled multipath TCP"); - } - - if (conf->no_delay) { - LOGI("enabled TCP no-delay"); - } - - if (conf->ipv6_first) { - LOGI("prioritized IPv6 addresses in domain resolution"); - } - - if (!conf->remote_dns) { - LOGI("disabled remote domain resolution"); - } - - if (conf->acl != NULL) { - LOGI("initializing acl..."); - acl = !init_acl(conf); - } - -#ifdef HAVE_SETRLIMIT - /* - * No need to check the return value here. - * We will show users an error message if setrlimit(2) fails. - */ - if (conf->nofile > 1024) { - if (conf->verbose) { - LOGI("setting NOFILE to %d", conf->nofile); - } - set_nofile(conf->nofile); - } -#endif - - if (conf->fast_open) { -#ifdef TCP_FASTOPEN - LOGI("using tcp fast open"); -#else - LOGE("tcp fast open is not supported by this environment"); - conf->fast_open = 0; -#endif - } - - no_delay = conf->no_delay; - fast_open = conf->fast_open; - verbose = conf->verbose; - ipv6first = conf->ipv6_first; - remote_dns = conf->remote_dns; #ifdef __ANDROID__ - stat_path = conf->stat_path; + stat_path = conf->stat_path; #endif - -#ifdef __MINGW32__ - winsock_init(); -#endif - -#ifndef __MINGW32__ - // ignore SIGPIPE - signal(SIGPIPE, SIG_IGN); - signal(SIGABRT, SIG_IGN); -#endif - - // Setup signal handler - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); -#ifndef __MINGW32__ - ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); - ev_signal_start(EV_DEFAULT, &sigchld_watcher); -#endif - - // Setup proxy context - struct ev_loop *loop = EV_DEFAULT; - listen_ctx_t listen_ctx = { - .mtu = conf->mtu, - .mptcp = conf->mptcp, - .reuse_port = conf->reuse_port, - .remote_num = conf->remote_num, - .remotes = ss_calloc(conf->remote_num, sizeof(remote_cnf_t *)), - .timeout = atoi(conf->timeout), - .loop = loop - }; - port_service_init(); - - for (i = 0; i < conf->remote_num; i++) { - ss_remote_t *r = conf->remotes[i]; - - char *host = r->addr, - *port = elvis(r->port, conf->remote_port), - *password = elvis(r->password, conf->password), - *key = elvis(r->key, conf->key), - *method = elvis(r->method, conf->method), - *iface = elvis(r->iface, conf->iface), - *plugin = elvis(r->plugin, conf->plugin), - *plugin_opts - = elvis(r->plugin_opts, conf->plugin_opts); - - if (host == NULL || port == NULL || - (password == NULL && key == NULL)) - { - usage(); - exit(EXIT_FAILURE); - } - - // Setup keys - LOGI("[%d/%d] server %s %s:%s", i + 1, conf->remote_num, - elvis(r->tag, "-"), host, port); - LOGI("initializing ciphers... %s", method); - crypto_t *crypto = crypto_init(password, key, method); - if (crypto == NULL) - FATAL("failed to initialize ciphers"); - - struct sockaddr_storage *storage - = ss_calloc(1, sizeof(struct sockaddr_storage)); - if (get_sockaddr(host, port, storage, 1, conf->ipv6_first) == -1) { - FATAL("failed to resolve %s", host); - } - - if (plugin != NULL) { - if (!plugin_enabled) { - init_plugin(MODE_CLIENT); - plugin_enabled = 1; - } - - uint16_t plugin_port = get_local_port(); - switch (storage->ss_family) { - case AF_INET: { - *(struct sockaddr_in *)storage = (struct sockaddr_in) { - .sin_addr = - (struct in_addr) { htonl(INADDR_LOOPBACK) }, - .sin_port = plugin_port - }; - } break; - case AF_INET6: { - *(struct sockaddr_in6 *)storage = (struct sockaddr_in6) { - .sin6_addr = in6addr_loopback, - .sin6_port = plugin_port - }; - } break; - } - - if (plugin_port == 0) - FATAL("failed to find a free port"); - - LOGI("plugin \"%s\" enabled", plugin); - - int err = start_plugin(plugin, plugin_opts, // user-defined plugin options - host, port, // user-defined destination address - sockaddr_readable("%a", storage), - sockaddr_readable("%p", storage)); - if (err) - FATAL("failed to start plugin %s", plugin); - } - - remote_cnf_t *remote_cnf - = ss_calloc(1, sizeof(*remote_cnf)); - remote_cnf->iface = iface; - remote_cnf->addr = storage; - remote_cnf->crypto = crypto; - - listen_ctx.remotes[i] = remote_cnf; - } - - ss_dscp_t **dscp = conf->dscp; - char *local_addr = conf->local_addr, - *local_port = conf->local_port; - - listen_ctx_t listen_ctx_current = listen_ctx; - do { - struct sockaddr_storage *storage = &(struct sockaddr_storage) {}; - if (get_sockaddr(local_addr, local_port, - storage, 1, conf->ipv6_first) == -1) - { - FATAL("failed to resolve %s", local_addr); - } - - if (listen_ctx_current.tos) { - LOGI("listening on %s (TOS 0x%x)", - sockaddr_readable("%a:%p", storage), listen_ctx_current.tos); - } else { - LOGI("listening on %s", - sockaddr_readable("%a:%p", storage)); - } - - int socket = -1, socket_u = -1; - if (conf->mode != UDP_ONLY) { - // Setup socket - socket = -#ifdef HAVE_LAUNCHD - launch_or_create(storage, &listen_ctx_current); -#else - bind_and_listen(storage, IPPROTO_TCP, &listen_ctx_current); -#endif - if (socket != -1) { - if (fast_open) - set_fastopen_passive(socket); - ev_io_init(&listen_ctx_current.io, accept_cb, listen_ctx_current.fd, EV_READ); - ev_io_start(loop, &listen_ctx_current.io); - } - } - - // Setup UDP - if (conf->mode != TCP_ONLY) { - listen_ctx_t listen_ctx_dgram = listen_ctx_current; - int socket_u = bind_and_listen(storage, IPPROTO_UDP, &listen_ctx_dgram); - if ((listen_ctx_dgram.fd = socket_u) != -1) { - init_udprelay(&listen_ctx_dgram); - } - } - - if (callback != NULL) { - callback(socket, socket_u, data); - } - - if (conf->mode == UDP_ONLY) { - LOGI("TCP relay disabled"); - } - - // Handle additional TOS/DSCP listening ports - if (*dscp != NULL) { - listen_ctx_current = listen_ctx; - local_port = (*dscp)->port; - listen_ctx_current.tos = (*dscp)->dscp << 2; - } - } while (*(dscp++) != NULL); - - // Init connections - cork_dllist_init(&connections); - - // start ev loop - ev_run(loop, 0); - - if (verbose) { - LOGI("closed gracefully"); - } - - if (plugin_enabled) - stop_plugin(); - - if (conf->mode != UDP_ONLY) { - ev_io_stop(loop, &listen_ctx.io); - free_connections(loop); - - for (i = 0; i < listen_ctx.remote_num; i++) { - remote_cnf_t *remote_cnf = listen_ctx.remotes[i]; - if (remote_cnf != NULL) { - ss_free(listen_ctx.remotes[i]); - } - } - ss_free(listen_ctx.remotes); - } - - if (conf->mode != TCP_ONLY) { - free_udprelay(loop); - } - -#ifdef __MINGW32__ - winsock_cleanup(); -#endif - - return ret_val; + return start_relay(conf, NULL, NULL); } #ifndef LIB_ONLY @@ -1059,9 +738,15 @@ main(int argc, char **argv) } #endif - ret_val = new_shadowsocks(module_local, &conf); - - return ret_val; + no_delay = conf.no_delay; + fast_open = conf.fast_open; + verbose = conf.verbose; + ipv6first = conf.ipv6_first; + remote_dns = conf.remote_dns; +#ifdef __ANDROID__ + stat_path = conf.stat_path; +#endif + return new_shadowsocks(module_local, &conf); } #endif diff --git a/src/local.h b/src/local.h deleted file mode 100644 index bc8c1c68..00000000 --- a/src/local.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * local.h - Define the client's buffers and callbacks - * - * Copyright (C) 2013 - 2019, Max Lv - * - * This file is part of the shadowsocks-libev. - * - * shadowsocks-libev is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * shadowsocks-libev is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with shadowsocks-libev; see the file COPYING. If not, see - * . - */ - -#ifndef _LOCAL_H -#define _LOCAL_H - -#include - -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif - -#ifdef __MINGW32__ -#include "winsock.h" -#endif - -#include "crypto.h" -#include "jconf.h" -#include "protocol.h" - -#include "common.h" - -typedef struct server_ctx { - ev_io io; - int connected; - struct server *server; -} server_ctx_t; - -typedef struct server { - int fd; - int stage; - - struct server_ctx *recv_ctx; - struct server_ctx *send_ctx; - struct listen_ctx *listen_ctx; - struct remote *remote; - - buffer_t *buf; - buffer_t *abuf; - - struct cork_dllist_item entries; -} server_t; - -typedef struct remote_ctx { - ev_io io; - ev_timer watcher; - int connected; - struct remote *remote; -} remote_ctx_t; - -typedef struct remote { - int fd; - int direct; -#ifdef TCP_FASTOPEN_WINSOCK - OVERLAPPED olap; - int connect_ex_done; -#endif - - buffer_t *buf; - - crypto_t *crypto; - cipher_ctx_t *e_ctx; - cipher_ctx_t *d_ctx; - - struct remote_ctx *recv_ctx; - struct remote_ctx *send_ctx; - struct server *server; - struct sockaddr_storage *addr; -} remote_t; - -#endif // _LOCAL_H diff --git a/src/netutils.c b/src/netutils.c index 5465f01c..31c73575 100644 --- a/src/netutils.c +++ b/src/netutils.c @@ -129,7 +129,7 @@ getdestaddr(int fd, struct sockaddr_storage *destaddr) } if (!getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen)) { return 0; - } + } #else FATAL("transparent proxy not supported in this build"); #endif @@ -185,33 +185,6 @@ setnonblocking(int fd) #endif -int -bind_to_address(int socket_fd, const char *host) -{ - static struct sockaddr_storage storage = { 0 }; - if (storage.ss_family == AF_INET) { - return bind(socket_fd, (struct sockaddr *)&storage, sizeof(struct sockaddr_in)); - } else if (storage.ss_family == AF_INET6) { - return bind(socket_fd, (struct sockaddr *)&storage, sizeof(struct sockaddr_in6)); - } else if (host != NULL) { - struct cork_ip ip; - if (cork_ip_init(&ip, host) != -1) { - if (ip.version == 4) { - struct sockaddr_in *addr = (struct sockaddr_in *)&storage; - inet_pton(AF_INET, host, &addr->sin_addr); - addr->sin_family = AF_INET; - return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); - } else if (ip.version == 6) { - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; - inet_pton(AF_INET6, host, &addr->sin6_addr); - addr->sin6_family = AF_INET6; - return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)); - } - } - } - return -1; -} - int create_and_bind(struct sockaddr_storage *storage, int protocol, listen_ctx_t *listen_ctx) @@ -713,14 +686,15 @@ parse_addr_cidr(const char *str, char *host, int *cidr) } } -ss_service +int port_service(uint16_t port) { - ss_service *service = NULL; - if (port > 0) - cache_lookup(port_cache, &port, sizeof(port), &service); - else return PORT_SERVICE_UNKNOWN; - return elvis(*service, PORT_SERVICE_UNKNOWN); + int *service = NULL; + if (cache_lookup(port_cache, + &port, sizeof(port), &service) == 0) { + return *service; + } + return PORT_SERVICE_UNKNOWN; } int @@ -752,7 +726,7 @@ port_service_init(void) } break; } if (!cache_key_exist(port_cache, &port, sizeof(port))) { - ss_service *service_type = ss_malloc(sizeof(ss_service)); + int *service_type = ss_malloc(sizeof(*service_type)); *service_type = service_port->service; cache_insert(port_cache, &port, sizeof(port), service_type); } diff --git a/src/netutils.h b/src/netutils.h index 5eb6a91b..e7a1f435 100644 --- a/src/netutils.h +++ b/src/netutils.h @@ -217,7 +217,6 @@ int setnonblocking(int fd); #endif typedef struct listen_ctx listen_ctx_t; -int bind_to_address(int socket_fd, const char *address); int create_and_bind(struct sockaddr_storage *storage, int protocol, listen_ctx_t *listen_ctx); int bind_and_listen(struct sockaddr_storage *storage, @@ -263,7 +262,7 @@ int is_addr_loopback(const struct sockaddr *addr); void parse_addr_cidr(const char *str, char *host, int *cidr); -ss_service port_service(uint16_t port); +int port_service(uint16_t port); int port_service_init(void); #endif diff --git a/src/plugin.c b/src/plugin.c index f1d94466..7bc6a4fb 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -410,6 +410,9 @@ stop_plugin() if (plugin != NULL) { if (plugin->proc != NULL) { cork_subprocess_abort(plugin->proc); + if (cork_subprocess_wait(plugin->proc) == -1) { + LOGI("error terminating plugin"); + } cork_subprocess_free(plugin->proc); } #ifdef __MINGW32__ diff --git a/src/redir.c b/src/redir.c index 0d252785..b7aa25e4 100644 --- a/src/redir.c +++ b/src/redir.c @@ -37,9 +37,6 @@ #include #include #include -//#include -//#include -//#include #include @@ -54,8 +51,6 @@ #include "acl.h" #include "relay.h" -static void accept_cb(EV_P_ ev_io *w, int revents); - int verbose = 0; int ipv6first = 0; int remote_dns = 1; // resolve hostname remotely @@ -63,11 +58,6 @@ int fast_open = 0; int acl = 0; static int no_delay = 0; -static int ret_val = 0; - -struct ev_signal sigint_watcher; -struct ev_signal sigterm_watcher; -struct ev_signal sigchld_watcher; void server_recv_cb(EV_P_ ev_io *w, int revents) @@ -103,17 +93,28 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (create_remote(EV_A_ remote, remote->buf, server->destaddr, acl) == -1) { + if (server->stage != STAGE_SNI + && server->destaddr->dname_len == -1) + { + if (verbose) + LOGE("partial HTTP/s request detected"); + server->stage = STAGE_SNI; + return; + } ERROR("create_remote"); close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); } else { - int r = connect(remote->fd, (struct sockaddr *)remote->addr, sizeof(*remote->addr)); + if (!fast_open) { + int r = connect(remote->fd, (struct sockaddr *)remote->addr, + get_sockaddr_len((struct sockaddr *)remote->addr)); - if (r == -1 && errno != CONNECT_IN_PROGRESS) { - ERROR("connect"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); - return; + if (r == -1 && errno != CONNECT_IN_PROGRESS) { + ERROR("connect"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } } ev_io_stop(EV_A_ & server_recv_ctx->io); @@ -205,7 +206,6 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; remote_t *remote = remote_recv_ctx->remote; server_t *server = remote->server; - crypto_t *crypto = remote->crypto; ssize_t r = recv(remote->fd, server->buf->data, SOCKET_BUF_SIZE, 0); @@ -230,6 +230,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) server->buf->len = r; if (!remote->direct) { + crypto_t *crypto = remote->crypto; int err = crypto->decrypt(server->buf, remote->d_ctx, SOCKET_BUF_SIZE); if (err == CRYPTO_ERROR) { LOGE("invalid password or cipher"); @@ -277,29 +278,26 @@ remote_send_cb(EV_P_ ev_io *w, int revents) remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; remote_t *remote = remote_send_ctx->remote; server_t *server = remote->server; - crypto_t *crypto = remote->crypto; ev_timer_stop(EV_A_ & remote_send_ctx->watcher); if (!remote_send_ctx->connected) { int r = 0; + if (remote->addr == NULL) { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(struct sockaddr_storage)); + struct sockaddr_storage addr = { 0 }; socklen_t len = sizeof addr; r = getpeername(remote->fd, (struct sockaddr *)&addr, &len); } + if (r == 0) { remote_send_ctx->connected = 1; - ev_io_stop(EV_A_ & remote_send_ctx->io); - ev_io_stop(EV_A_ & server->recv_ctx->io); - ev_io_start(EV_A_ & remote->recv_ctx->io); - - if (server->abuf) - bprepend(remote->buf, server->abuf, SOCKET_BUF_SIZE); - if (!remote->direct) { + crypto_t *crypto = remote->crypto; + if (server->abuf) + bprepend(remote->buf, server->abuf, SOCKET_BUF_SIZE); + if (crypto->encrypt(remote->buf, remote->e_ctx, SOCKET_BUF_SIZE)) { LOGE("invalid password or cipher"); close_and_free_remote(EV_A_ remote); @@ -307,6 +305,10 @@ remote_send_cb(EV_P_ ev_io *w, int revents) return; } } + + ev_io_stop(EV_A_ & remote_send_ctx->io); + ev_io_stop(EV_A_ & server->recv_ctx->io); + ev_io_start(EV_A_ & remote->recv_ctx->io); } else { ERROR("getpeername"); // not connected @@ -377,7 +379,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } } -static void +void accept_cb(EV_P_ ev_io *w, int revents) { listen_ctx_t *listener = (listen_ctx_t *)w; @@ -423,36 +425,12 @@ accept_cb(EV_P_ ev_io *w, int revents) ev_io_start(EV_A_ & server->recv_ctx->io); } -static void -signal_cb(EV_P_ ev_signal *w, int revents) -{ - if (revents & EV_SIGNAL) { - switch (w->signum) { - case SIGCHLD: - if (!is_plugin_running()) { - LOGE("plugin service exit unexpectedly"); - ret_val = -1; - } else - return; - case SIGINT: - case SIGTERM: - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); - ev_signal_stop(EV_DEFAULT, &sigchld_watcher); - - ev_unloop(EV_A_ EVUNLOOP_ALL); - } - } -} - int main(int argc, char **argv) { USE_TTY(); srand(time(NULL)); - int i; - int plugin_enabled = 0; int pid_flags = 0; jconf_t conf = jconf_default; @@ -461,247 +439,27 @@ main(int argc, char **argv) exit(EXIT_FAILURE); } - if (!(conf.remotes != NULL && - conf.remote_num > 0)) { - FATAL("at least one server should be specified"); - } - - if (conf.local_port == NULL) { - conf.local_port = "0"; - LOGE("warning: random local port will be assigned"); - } - - if (conf.log) { - USE_LOGFILE(conf.log); - LOGI("enabled %slogging %s", conf.verbose ? "verbose " : "", conf.log); - } - - if (conf.mtu > 0) { - LOGI("setting MTU to %d", conf.mtu); - } - - if (conf.mptcp) { - LOGI("enabled multipath TCP"); - } - - if (conf.no_delay) { - LOGI("enabled TCP no-delay"); - } - - if (conf.ipv6_first) { - LOGI("prioritized IPv6 addresses in domain resolution"); - } - - if (conf.acl != NULL) { - LOGI("initializing acl..."); - acl = !init_acl(&conf); - } - -#ifdef HAVE_SETRLIMIT - /* - * No need to check the return value here. - * We will show users an error message if setrlimit(2) fails. - */ - if (conf.nofile > 1024) { - if (conf.verbose) { - LOGI("setting NOFILE to %d", conf.nofile); - } - set_nofile(conf.nofile); - } -#endif - - if (conf.fast_open) { -#ifdef TCP_FASTOPEN - LOGI("using tcp fast open"); -#else - LOGE("tcp fast open is not supported by this environment"); - conf.fast_open = 0; -#endif - } - pid_flags = conf.pid_path != NULL; USE_SYSLOG(argv[0], pid_flags); if (pid_flags) { daemonize(conf.pid_path); } - no_delay = conf.no_delay; - fast_open = conf.fast_open; - verbose = conf.verbose; - ipv6first = conf.ipv6_first; - remote_dns = conf.remote_dns; - - // ignore SIGPIPE - signal(SIGPIPE, SIG_IGN); - signal(SIGABRT, SIG_IGN); - - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); - ev_signal_start(EV_DEFAULT, &sigchld_watcher); - - // Setup proxy context - struct ev_loop *loop = EV_DEFAULT; - struct listen_ctx listen_ctx = { - .mtu = conf.mtu, - .mptcp = conf.mptcp, - .reuse_port = conf.reuse_port, - .remote_num = conf.remote_num, - .remotes = ss_calloc(conf.remote_num, sizeof(struct remote_cnf *)), - .timeout = atoi(conf.timeout), - .loop = loop - }; - port_service_init(); - - for (i = 0; i < conf.remote_num; i++) { - ss_remote_t *r = conf.remotes[i]; - - char *host = r->addr, - *port = elvis(r->port, conf.remote_port), - *password = elvis(r->password, conf.password), - *key = elvis(r->key, conf.key), - *method = elvis(r->method, conf.method), - *iface = elvis(r->iface, conf.iface), - *plugin = elvis(r->plugin, conf.plugin), - *plugin_opts - = elvis(r->plugin_opts, conf.plugin_opts); - - if (host == NULL || port == NULL || - (password == NULL && key == NULL)) - { - usage(); - exit(EXIT_FAILURE); - } - - LOGI("[%d/%d] server %s:%s", i + 1, conf.remote_num, host, port); - - LOGI("initializing ciphers... %s", method); - crypto_t *crypto = crypto_init(password, key, method); - if (crypto == NULL) - FATAL("failed to initialize ciphers"); - - struct sockaddr_storage *storage - = ss_calloc(1, sizeof(struct sockaddr_storage)); - if (get_sockaddr(host, port, storage, 1, conf.ipv6_first) == -1) { - FATAL("failed to resolve %s", host); - } - - if (plugin != NULL) { - if (!plugin_enabled) { - init_plugin(MODE_CLIENT); - plugin_enabled = 1; - } - - uint16_t plugin_port = get_local_port(); - switch (storage->ss_family) { - case AF_INET: { - *(struct sockaddr_in *)storage = (struct sockaddr_in) { - .sin_addr = - (struct in_addr) { htonl(INADDR_LOOPBACK) }, - .sin_port = plugin_port - }; - } break; - case AF_INET6: { - *(struct sockaddr_in6 *)storage = (struct sockaddr_in6) { - .sin6_addr = in6addr_loopback, - .sin6_port = plugin_port - }; - } break; - } - - if (plugin_port == 0) - FATAL("failed to find a free port"); - - LOGI("plugin \"%s\" enabled", plugin); - - int err = start_plugin(plugin, plugin_opts, // user-defined plugin options - host, port, // user-defined destination address - sockaddr_readable("%a", storage), - sockaddr_readable("%p", storage)); - if (err) - FATAL("failed to start plugin"); - } - - listen_ctx.remotes[i] = &(remote_cnf_t) { - .iface = iface, - .addr = storage, - .crypto = crypto - }; - } - - ss_dscp_t **dscp = conf.dscp; - char *local_addr = conf.local_addr, - *local_port = conf.local_port; - - listen_ctx_t listen_ctx_current = listen_ctx; - do { - struct sockaddr_storage *storage = &(struct sockaddr_storage) {}; - if (get_sockaddr(local_addr, local_port, - storage, 1, conf.ipv6_first) == -1) - { - FATAL("failed to resolve %s", local_addr); - } - - if (listen_ctx_current.tos) { - LOGI("listening on %s (TOS 0x%x)", - sockaddr_readable("%a:%p", storage), listen_ctx_current.tos); - } else { - LOGI("listening on %s", - sockaddr_readable("%a:%p", storage)); - } - - if (conf.mode != UDP_ONLY) { - // Setup socket - int listenfd = bind_and_listen(storage, IPPROTO_TCP, &listen_ctx_current); - if (listenfd != -1) { - if (fast_open) - set_fastopen_passive(listenfd); - ev_io_init(&listen_ctx_current.io, accept_cb, listen_ctx_current.fd, EV_READ); - ev_io_start(loop, &listen_ctx_current.io); - } - } - - // Setup UDP - if (conf.mode != TCP_ONLY) { - listen_ctx_t listen_ctx_dgram = listen_ctx; - int listenfd = bind_and_listen(storage, IPPROTO_UDP, &listen_ctx_dgram); - if ((listen_ctx_dgram.fd = listenfd) != -1) { - init_udprelay(&listen_ctx_dgram); - } - } - - if (conf.mode == UDP_ONLY) { - LOGI("TCP relay disabled"); - } - - // Handle additional TOS/DSCP listening ports - if (*dscp != NULL) { - listen_ctx_current = listen_ctx; - local_port = (*dscp)->port; - listen_ctx_current.tos = (*dscp)->dscp << 2; - } - } while (*(dscp++) != NULL); - +#ifndef __MINGW32__ // setuid - if (conf.user && !run_as(conf.user)) + if (conf.user && !run_as(conf.user)) { FATAL("failed to switch user"); + } - if (geteuid() == 0) + if (geteuid() == 0) { LOGI("running from root user"); - - // Init connections - cork_dllist_init(&connections); - - ev_run(loop, 0); - - if (plugin_enabled) - stop_plugin(); - - if (conf.mode != TCP_ONLY) { - free_udprelay(loop); } +#endif - return ret_val; + no_delay = conf.no_delay; + fast_open = conf.fast_open; + verbose = conf.verbose; + ipv6first = conf.ipv6_first; + remote_dns = conf.remote_dns; + return start_relay(&conf, NULL, NULL); } diff --git a/src/redir.h b/src/redir.h deleted file mode 100644 index 4b8d0142..00000000 --- a/src/redir.h +++ /dev/null @@ -1,76 +0,0 @@ -/* * redir.h - Define the redirector's buffers and callbacks - * - * Copyright (C) 2013 - 2019, Max Lv - * - * This file is part of the shadowsocks-libev. - * - * shadowsocks-libev is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * shadowsocks-libev is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with shadowsocks-libev; see the file COPYING. If not, see - * . - */ - -#ifndef _REDIR_H -#define _REDIR_H - -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif - -#include "crypto.h" -#include "jconf.h" - -typedef struct server_ctx { - ev_io io; - int connected; - struct server *server; -} server_ctx_t; - -typedef struct server { - int fd; - - buffer_t *buf; - buffer_t *abuf; - - struct server_ctx *recv_ctx; - struct server_ctx *send_ctx; - struct listen_ctx *listen_ctx; - struct remote *remote; - - ssocks_addr_t destaddr; - ev_timer delayed_connect_watcher; -} server_t; - -typedef struct remote_ctx { - ev_io io; - ev_timer watcher; - int connected; - struct remote *remote; -} remote_ctx_t; - -typedef struct remote { - int fd; - buffer_t *buf; - - crypto_t *crypto; - cipher_ctx_t *e_ctx; - cipher_ctx_t *d_ctx; - struct remote_ctx *recv_ctx; - struct remote_ctx *send_ctx; - struct server *server; - uint32_t counter; - struct sockaddr *addr; -} remote_t; - -#endif // _REDIR_H diff --git a/src/relay.c b/src/relay.c index 3461b359..ac215ba3 100644 --- a/src/relay.c +++ b/src/relay.c @@ -35,6 +35,7 @@ #include #include #include +#include #endif #ifdef HAVE_CONFIG_H #include "config.h" @@ -54,11 +55,12 @@ #include "http.h" #include "tls.h" #include "acl.h" +#include "plugin.h" #include "relay.h" +extern int acl; extern int verbose; -extern int fast_open; extern int remote_dns; extern int ipv6first; @@ -69,7 +71,24 @@ extern int vpn; #ifdef MODULE_REMOTE static int remote_conn = 0; static int server_conn = 0; +static char *manager_addr = NULL; +static struct cork_dllist listeners; +extern uint64_t tx, rx; + +#ifndef __MINGW32__ +ev_timer stat_watcher; +#endif + #endif +static struct cork_dllist connections; + +struct ev_signal sigint_watcher; +struct ev_signal sigterm_watcher; +#ifndef __MINGW32__ +struct ev_signal sigchld_watcher; +#endif + +static int ret_val = 0; #ifdef MODULE_LOCAL remote_t * @@ -263,7 +282,7 @@ init_remote(EV_P_ remote_t *remote, remote_cnf_t *conf) } #endif - if (conf->crypto) { + if (!remote->direct && conf->crypto) { crypto_t *crypto = conf->crypto; remote->crypto = crypto; remote->e_ctx = ss_malloc(sizeof(cipher_ctx_t)); @@ -376,6 +395,113 @@ server_timeout_cb(EV_P_ ev_timer *watcher, int revents) close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); } + +#ifndef __MINGW32__ +static void +stat_update_cb(EV_P_ ev_timer *watcher, int revents) +{ + listen_ctx_t *listen_ctx + = cork_container_of(watcher, listen_ctx_t, stat_watcher); + struct sockaddr_storage addr; + + if (getsockname(listen_ctx->fd, + (struct sockaddr *)&addr, NULL) != 0) { + return; + } + + struct sockaddr_un svaddr, claddr; + int sfd = -1; + size_t msgLen; + char resp[SOCKET_BUF_SIZE]; + + if (verbose) { + LOGI("update traffic stat: tx: %" PRIu64 " rx: %" PRIu64 "", tx, rx); + } + + snprintf(resp, SOCKET_BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", sockaddr_readable("%p", &addr), tx + rx); + msgLen = strlen(resp) + 1; + + ss_addr_t ip_addr = { .host = NULL, .port = NULL }; + parse_addr(manager_addr, &ip_addr); + + if (ip_addr.host == NULL || ip_addr.port == NULL) { + sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sfd == -1) { + ERROR("stat_socket"); + return; + } + + memset(&claddr, 0, sizeof(struct sockaddr_un)); + claddr.sun_family = AF_UNIX; + snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/shadowsocks.%s", sockaddr_readable("%p", &addr)); + + unlink(claddr.sun_path); + + if (bind(sfd, (struct sockaddr *)&claddr, sizeof(struct sockaddr_un)) == -1) { + ERROR("stat_bind"); + close(sfd); + return; + } + + memset(&svaddr, 0, sizeof(struct sockaddr_un)); + svaddr.sun_family = AF_UNIX; + strncpy(svaddr.sun_path, manager_addr, sizeof(svaddr.sun_path) - 1); + + if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&svaddr, + sizeof(struct sockaddr_un)) != msgLen) { + ERROR("stat_sendto"); + close(sfd); + return; + } + + unlink(claddr.sun_path); + } else { + struct sockaddr_storage storage; + memset(&storage, 0, sizeof(struct sockaddr_storage)); + if (get_sockaddr(ip_addr.host, ip_addr.port, &storage, 1, ipv6first) == -1) { + ERROR("failed to parse the manager addr"); + return; + } + + sfd = socket(storage.ss_family, SOCK_DGRAM, 0); + + if (sfd == -1) { + ERROR("stat_socket"); + return; + } + + size_t addr_len = get_sockaddr_len((struct sockaddr *)&storage); + if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&storage, + addr_len) != msgLen) { + ERROR("stat_sendto"); + close(sfd); + return; + } + } + + close(sfd); +} + +#endif + +static void +free_listeners(struct ev_loop *loop) +{ + listen_ctx_t *listener = NULL; + struct cork_dllist_item *curr, *next; + cork_dllist_foreach(&listeners, curr, next, + listen_ctx_t, listener, entries) { + if (listener != NULL) { +#ifndef __MINGW32__ + if (manager_addr != NULL) + ev_timer_stop(EV_A_ & stat_watcher); +#endif + ev_io_stop(EV_A_ & listener->io); + close(listener->fd); + } + } +} + #endif void @@ -493,3 +619,474 @@ free_connections(struct ev_loop *loop) } } } + +static void +signal_cb(EV_P_ ev_signal *w, int revents) +{ + if (revents & EV_SIGNAL) { + switch (w->signum) { +#ifndef __MINGW32__ + case SIGCHLD: + if (!is_plugin_running()) { + LOGE("plugin service exit unexpectedly"); + ret_val = -1; + } else + return; +#endif + case SIGINT: + case SIGTERM: + ev_signal_stop(EV_DEFAULT, &sigint_watcher); + ev_signal_stop(EV_DEFAULT, &sigterm_watcher); +#ifndef __MINGW32__ + ev_signal_stop(EV_DEFAULT, &sigchld_watcher); +#endif + ev_unloop(EV_A_ EVUNLOOP_ALL); + } + } +} + +int +start_relay(jconf_t *conf, + ss_callback_t callback, void *data) +{ + int plugin_enabled = 0; + + if (!(conf->remotes != NULL && + conf->remote_num > 0)) { + LOGE("at least one server should be specified"); + return -1; + } + +#ifndef HAVE_LAUNCHD + if (conf->local_port == NULL) { + conf->local_port = "0"; + LOGE("warning: random local port will be assigned"); + } +#endif + + if (conf->log) { + USE_LOGFILE(conf->log); + LOGI("enabled %slogging %s", conf->verbose ? "verbose " : "", conf->log); + } + + if (conf->mtu > 0) { + LOGI("setting MTU to %d", conf->mtu); + } + + if (conf->mptcp) { + LOGI("enabled multipath TCP"); + } + + if (conf->no_delay) { + LOGI("enabled TCP no-delay"); + } + + if (conf->ipv6_first) { + LOGI("prioritized IPv6 addresses in domain resolution"); + } + + if (!conf->remote_dns) { + LOGI("disabled remote domain resolution"); + } + + if (conf->acl != NULL) { + LOGI("initializing acl..."); + acl = !init_acl(conf); + } + +#ifdef HAVE_SETRLIMIT + /* + * No need to check the return value here. + * We will show users an error message if setrlimit(2) fails. + */ + if (conf->nofile > 1024) { + if (conf->verbose) { + LOGI("setting NOFILE to %d", conf->nofile); + } + set_nofile(conf->nofile); + } +#endif + + if (conf->fast_open) { +#ifdef TCP_FASTOPEN + LOGI("using tcp fast open"); +#else + LOGE("tcp fast open is not supported by this environment"); + conf->fast_open = 0; +#endif + } + +#ifdef __MINGW32__ + winsock_init(); +#endif + +#ifndef __MINGW32__ + // ignore SIGPIPE + signal(SIGPIPE, SIG_IGN); + signal(SIGABRT, SIG_IGN); +#endif + + // Setup signal handler + ev_signal_init(&sigint_watcher, signal_cb, SIGINT); + ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ev_signal_start(EV_DEFAULT, &sigint_watcher); + ev_signal_start(EV_DEFAULT, &sigterm_watcher); +#ifndef __MINGW32__ + ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); + ev_signal_start(EV_DEFAULT, &sigchld_watcher); +#endif + + // Setup proxy context + struct ev_loop *loop = EV_DEFAULT; +#ifdef MODULE_LOCAL + listen_ctx_t listen_ctx = { + .mtu = conf->mtu, + .mptcp = conf->mptcp, + .reuse_port = conf->reuse_port, + .remote_num = conf->remote_num, + .remotes = ss_calloc(conf->remote_num, sizeof(remote_cnf_t *)), + .timeout = atoi(conf->timeout), + .loop = loop + }; + +#ifdef MODULE_TUNNEL + ss_addr_t *tunnel_addr = &conf->tunnel_addr; + if (tunnel_addr->host == NULL || + tunnel_addr->port == NULL) { + FATAL("tunnel address either undefined or invalid"); + } + + ssocks_addr_t *destaddr = &listen_ctx.destaddr; + destaddr->addr = ss_calloc(1, sizeof(struct sockaddr_storage)); + if (get_sockaddr(tunnel_addr->host, tunnel_addr->port, + destaddr->addr, !conf->remote_dns, conf->ipv6_first) == -1) + { + destaddr->dname = tunnel_addr->host; + destaddr->dname_len = strlen(tunnel_addr->host); + destaddr->port = htons(atoi(tunnel_addr->port)); + } +#endif + port_service_init(); + + for (int i = 0; i < conf->remote_num; i++) { + ss_remote_t *r = conf->remotes[i]; + + char *host = r->addr, + *port = elvis(r->port, conf->remote_port), + *password = elvis(r->password, conf->password), + *key = elvis(r->key, conf->key), + *method = elvis(r->method, conf->method), + *iface = elvis(r->iface, conf->iface), + *plugin = elvis(r->plugin, conf->plugin), + *plugin_opts + = elvis(r->plugin_opts, conf->plugin_opts); + + if (host == NULL || port == NULL || + (password == NULL && key == NULL)) + { + usage(); + exit(EXIT_FAILURE); + } + + LOGI("[%d/%d] server %s %s:%s", + i + 1, conf->remote_num, elvis(r->tag, "-"), host, port); + + LOGI("initializing ciphers... %s", method); + crypto_t *crypto = crypto_init(password, key, method); + if (crypto == NULL) + FATAL("failed to initialize ciphers"); + + struct sockaddr_storage *storage + = ss_calloc(1, sizeof(struct sockaddr_storage)); + if (get_sockaddr(host, port, storage, 1, conf->ipv6_first) == -1) { + FATAL("failed to resolve %s", host); + } + + if (plugin != NULL) { + if (!plugin_enabled) { + init_plugin(MODE_CLIENT); + plugin_enabled = 1; + } + + uint16_t plugin_port = get_local_port(); + switch (storage->ss_family) { + case AF_INET: { + *(struct sockaddr_in *)storage = (struct sockaddr_in) { + .sin_addr = + (struct in_addr) { htonl(INADDR_LOOPBACK) }, + .sin_port = plugin_port + }; + } break; + case AF_INET6: { + *(struct sockaddr_in6 *)storage = (struct sockaddr_in6) { + .sin6_addr = in6addr_loopback, + .sin6_port = plugin_port + }; + } break; + } + + if (plugin_port == 0) + FATAL("failed to find a free port"); + + LOGI("plugin \"%s\" enabled", plugin); + + int err = start_plugin(plugin, plugin_opts, // user-defined plugin options + host, port, // user-defined destination address + sockaddr_readable("%a", storage), + sockaddr_readable("%p", storage)); + if (err) + FATAL("failed to start plugin %s", plugin); + } + + remote_cnf_t *remote_cnf + = ss_calloc(1, sizeof(*remote_cnf)); + remote_cnf->iface = iface; + remote_cnf->addr = storage; + remote_cnf->crypto = crypto; + + listen_ctx.remotes[i] = remote_cnf; + } + + ss_dscp_t **dscp = conf->dscp; + char *local_addr = conf->local_addr, + *local_port = conf->local_port; + + listen_ctx_t listen_ctx_current = listen_ctx; + do { + struct sockaddr_storage *storage = &(struct sockaddr_storage) {}; + if (get_sockaddr(local_addr, local_port, + storage, 1, conf->ipv6_first) == -1) + { + FATAL("failed to resolve %s", local_addr); + } + + if (listen_ctx_current.tos) { + LOGI("listening on %s (TOS 0x%x)", + sockaddr_readable("%a:%p", storage), listen_ctx_current.tos); + } else { + LOGI("listening on %s", + sockaddr_readable("%a:%p", storage)); + } + + int socket = -1, socket_u = -1; + if (conf->mode != UDP_ONLY) { + // Setup socket + socket = +#ifdef HAVE_LAUNCHD + launch_or_create(storage, &listen_ctx_current); +#else + bind_and_listen(storage, IPPROTO_TCP, &listen_ctx_current); +#endif + if (socket != -1) { + if (conf->fast_open) + set_fastopen_passive(socket); + ev_io_init(&listen_ctx_current.io, accept_cb, listen_ctx_current.fd, EV_READ); + ev_io_start(EV_A_ &listen_ctx_current.io); + } + } + + // Setup UDP + if (conf->mode != TCP_ONLY) { + listen_ctx_t listen_ctx_dgram = listen_ctx_current; + int socket_u = bind_and_listen(storage, IPPROTO_UDP, &listen_ctx_dgram); + if ((listen_ctx_dgram.fd = socket_u) != -1) { + init_udprelay(&listen_ctx_dgram); + } + } + + if (callback != NULL) { + callback(socket, socket_u, data); + } + + if (conf->mode == UDP_ONLY) { + LOGI("TCP relay disabled"); + } + + // Handle additional TOS/DSCP listening ports + if (*dscp != NULL) { + listen_ctx_current = listen_ctx; + local_port = (*dscp)->port; + listen_ctx_current.tos = (*dscp)->dscp << 2; + } + } while (*(dscp++) != NULL); + +#elif MODULE_REMOTE + resolv_init(loop, conf->nameserver, conf->ipv6_first); + if (conf->nameserver != NULL) + LOGI("using nameserver: %s", conf->nameserver); + port_service_init(); + + cork_dllist_init(&listeners); + + for (int i = 0; i < conf->remote_num; i++) { + ss_remote_t *r = conf->remotes[i]; + char *host = r->addr, + *port = elvis(r->port, conf->remote_port), + *password = elvis(r->password, conf->password), + *key = elvis(r->key, conf->key), + *method = elvis(r->method, conf->method), + *iface = elvis(r->iface, conf->iface), + *plugin = elvis(r->plugin, conf->plugin), + *plugin_opts + = elvis(r->plugin_opts, conf->plugin_opts); + + if (port == NULL || method == NULL || + (password == NULL && key == NULL)) + { + usage(); + exit(EXIT_FAILURE); + } + + LOGI("[%d/%d] listening on %s:%s", i + 1, conf->remote_num, host, port); + + LOGI("initializing ciphers... %s", method); + crypto_t *crypto = crypto_init(password, key, method); + if (crypto == NULL) + FATAL("failed to initialize ciphers"); + + /** + * "If node(host) is NULL, then the network address + * will be set to the loopback interface address," meaning that + * by default the server will bind to `lo' instead of 0.0.0.0. + * + * On Linux, when net.ipv6.bindv6only = 0 (the default), + * getaddrinfo(NULL) with AI_PASSIVE returns 0.0.0.0 and :: (in this order). + * AI_PASSIVE was meant to return a list of addresses to listen on, + * but it is impossible to listen on 0.0.0.0 and :: at the same time, + * unless `bindv6only' is enabled. + */ + struct sockaddr_storage *storage = + ss_calloc(1, sizeof(struct sockaddr_storage)); + if (get_sockaddr(host, port, storage, 1, conf->ipv6_first) == -1) { + FATAL("failed to resolve %s", host); + } + + if (plugin != NULL) { + if (!plugin_enabled) { + init_plugin(MODE_SERVER); + plugin_enabled = 1; + } + + uint16_t plugin_port = get_local_port(); + switch (storage->ss_family) { + case AF_INET: { + *(struct sockaddr_in *)storage = (struct sockaddr_in) { + .sin_addr = + (struct in_addr) { htonl(INADDR_LOOPBACK) }, + .sin_port = plugin_port + }; + } break; + case AF_INET6: { + *(struct sockaddr_in6 *)storage = (struct sockaddr_in6) { + .sin6_addr = in6addr_loopback, + .sin6_port = plugin_port + }; + } break; + } + + if (plugin_port == 0) + FATAL("failed to find a free port"); + + LOGI("plugin \"%s\" enabled", plugin); + + int err = start_plugin(plugin, plugin_opts, // user-defined plugin options + sockaddr_readable("%a", storage), + sockaddr_readable("%p", storage), // plugin destination override + host, port); + if (err) + FATAL("failed to start the plugin"); + } + + listen_ctx_t listen_ctx = { + .iface = iface, + .addr = storage, + .crypto = crypto, + .timeout = atoi(conf->timeout), + .loop = loop + }; + +#ifndef __MINGW32__ + manager_addr = conf->manager_addr; + if (conf->manager_addr != NULL) { + ev_timer_init(&listen_ctx.stat_watcher, stat_update_cb, + UPDATE_INTERVAL, UPDATE_INTERVAL); + ev_timer_start(EV_DEFAULT, &listen_ctx.stat_watcher); + } +#endif + + int socket = -1, socket_u = -1; + if (conf->mode != UDP_ONLY) { + int socket = bind_and_listen(storage, IPPROTO_TCP, &listen_ctx); + if (socket != -1) { + if (conf->fast_open) + set_fastopen_passive(socket); + ev_io_init(&listen_ctx.io, accept_cb, listen_ctx.fd, EV_READ); + ev_io_start(loop, &listen_ctx.io); + cork_dllist_add(&listeners, &listen_ctx.entries); + } + } + + // Setup UDP + if (conf->mode != TCP_ONLY) { + listen_ctx_t listen_ctx_dgram = listen_ctx; + int socket_u = bind_and_listen(storage, IPPROTO_UDP, &listen_ctx_dgram); + if ((listen_ctx_dgram.fd = socket_u) != -1) { + init_udprelay(&listen_ctx_dgram); + } + } + + if (callback != NULL) { + callback(socket, socket_u, data); + } + } +#endif + + // Init connections + cork_dllist_init(&connections); + + // start ev loop + ev_run(EV_A_ 0); + + if (conf->verbose) { + LOGI("closed gracefully"); + } + +#ifdef MODULE_LOCAL + if (conf->mode != UDP_ONLY) { + ev_io_stop(EV_A_ & listen_ctx.io); + free_connections(loop); + + for (int i = 0; i < listen_ctx.remote_num; i++) { + remote_cnf_t *remote_cnf = listen_ctx.remotes[i]; + if (remote_cnf != NULL) { + ss_free(listen_ctx.remotes[i]); + } + } + ss_free(listen_ctx.remotes); + } +#elif MODULE_REMOTE +#ifndef __MINGW32__ + if (manager_addr != NULL) { + ev_timer_stop(EV_A_ & stat_watcher); + } +#endif + resolv_shutdown(loop); + if (conf->mode != UDP_ONLY) { + free_listeners(loop); + free_connections(loop); + } +#endif + + if (plugin_enabled) + stop_plugin(); + + if (conf->mode != TCP_ONLY) { + free_udprelay(loop); + } + +#ifdef __MINGW32__ + winsock_cleanup(); +#endif + + return ret_val; +} diff --git a/src/relay.h b/src/relay.h index 826b5deb..3924fb07 100644 --- a/src/relay.h +++ b/src/relay.h @@ -136,6 +136,7 @@ enum { STAGE_STOP /* Server stop to respond */ }; +void accept_cb(EV_P_ ev_io *, int); void server_recv_cb(EV_P_ ev_io *, int); void server_send_cb(EV_P_ ev_io *, int); void remote_recv_cb(EV_P_ ev_io *, int); @@ -159,7 +160,7 @@ int create_remote(EV_P_ remote_t *remote, buffer_t *buf, ssocks_addr_t *destaddr, int acl_enabled); #endif -struct cork_dllist connections; -void free_connections(struct ev_loop *loop); +int start_relay(jconf_t *conf, + ss_callback_t callback, void *data); #endif // _RELAY_H diff --git a/src/server.c b/src/server.c index b0a6245b..e49e6542 100644 --- a/src/server.c +++ b/src/server.c @@ -77,9 +77,6 @@ #endif -static void signal_cb(EV_P_ ev_signal *w, int revents); -static void accept_cb(EV_P_ ev_io *w, int revents); - static remote_t * connect_to_remote(EV_P_ server_t *server, struct sockaddr_storage *addr); @@ -92,129 +89,8 @@ int no_delay = 0; int fast_open = 0; int ipv6first = 0; -static int ret_val = 0; - -static char *manager_addr = NULL; uint64_t tx = 0, rx = 0; -#ifndef __MINGW32__ -ev_timer stat_watcher; -#endif - -struct ev_signal sigint_watcher; -struct ev_signal sigterm_watcher; -#ifndef __MINGW32__ -struct ev_signal sigchld_watcher; -#endif - -static struct cork_dllist listeners; - -#ifndef __MINGW32__ -static void -stat_update_cb(EV_P_ ev_timer *watcher, int revents) -{ - listen_ctx_t *listen_ctx - = cork_container_of(watcher, listen_ctx_t, stat_watcher); - struct sockaddr_storage addr; - - if (getsockname(listen_ctx->fd, - (struct sockaddr *)&addr, NULL) != 0) { - return; - } - - struct sockaddr_un svaddr, claddr; - int sfd = -1; - size_t msgLen; - char resp[SOCKET_BUF_SIZE]; - - if (verbose) { - LOGI("update traffic stat: tx: %" PRIu64 " rx: %" PRIu64 "", tx, rx); - } - - snprintf(resp, SOCKET_BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", sockaddr_readable("%p", &addr), tx + rx); - msgLen = strlen(resp) + 1; - - ss_addr_t ip_addr = { .host = NULL, .port = NULL }; - parse_addr(manager_addr, &ip_addr); - - if (ip_addr.host == NULL || ip_addr.port == NULL) { - sfd = socket(AF_UNIX, SOCK_DGRAM, 0); - if (sfd == -1) { - ERROR("stat_socket"); - return; - } - - memset(&claddr, 0, sizeof(struct sockaddr_un)); - claddr.sun_family = AF_UNIX; - snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/shadowsocks.%s", sockaddr_readable("%p", &addr)); - - unlink(claddr.sun_path); - - if (bind(sfd, (struct sockaddr *)&claddr, sizeof(struct sockaddr_un)) == -1) { - ERROR("stat_bind"); - close(sfd); - return; - } - - memset(&svaddr, 0, sizeof(struct sockaddr_un)); - svaddr.sun_family = AF_UNIX; - strncpy(svaddr.sun_path, manager_addr, sizeof(svaddr.sun_path) - 1); - - if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&svaddr, - sizeof(struct sockaddr_un)) != msgLen) { - ERROR("stat_sendto"); - close(sfd); - return; - } - - unlink(claddr.sun_path); - } else { - struct sockaddr_storage storage; - memset(&storage, 0, sizeof(struct sockaddr_storage)); - if (get_sockaddr(ip_addr.host, ip_addr.port, &storage, 1, ipv6first) == -1) { - ERROR("failed to parse the manager addr"); - return; - } - - sfd = socket(storage.ss_family, SOCK_DGRAM, 0); - - if (sfd == -1) { - ERROR("stat_socket"); - return; - } - - size_t addr_len = get_sockaddr_len((struct sockaddr *)&storage); - if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&storage, - addr_len) != msgLen) { - ERROR("stat_sendto"); - close(sfd); - return; - } - } - - close(sfd); -} - -#endif - -static void -free_listeners(struct ev_loop *loop) -{ - listen_ctx_t *listener = NULL; - struct cork_dllist_item *curr, *next; - cork_dllist_foreach(&listeners, curr, next, - listen_ctx_t, listener, entries) { - if (listener != NULL) { -#ifndef __MINGW32__ - if (manager_addr != NULL) - ev_timer_stop(loop, &stat_watcher); -#endif - ev_io_stop(loop, &listener->io); - close(listener->fd); - } - } -} - static void report_addr(EV_P_ server_t *server, const char *info) { @@ -905,32 +781,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } } -static void -signal_cb(EV_P_ ev_signal *w, int revents) -{ - if (revents & EV_SIGNAL) { - switch (w->signum) { -#ifndef __MINGW32__ - case SIGCHLD: - if (!is_plugin_running()) { - LOGE("plugin service exit unexpectedly"); - ret_val = -1; - } else - return; -#endif - case SIGINT: - case SIGTERM: - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); -#ifndef __MINGW32__ - ev_signal_stop(EV_DEFAULT, &sigchld_watcher); -#endif - ev_unloop(EV_A_ EVUNLOOP_ALL); - } - } -} - -static void +void accept_cb(EV_P_ ev_io *w, int revents) { listen_ctx_t *listener = (listen_ctx_t *)w; @@ -978,229 +829,20 @@ main(int argc, char **argv) USE_TTY(); srand(time(NULL)); - int i; - int plugin_enabled = 0; int pid_flags = 0; jconf_t conf = jconf_default; - if (parse_argopts(&conf, argc, argv) != 0 || - !(conf.remotes != NULL && conf.remote_num > 0)) - { + if (parse_argopts(&conf, argc, argv) != 0) { usage(); exit(EXIT_FAILURE); } - if (conf.log) { - USE_LOGFILE(conf.log); - LOGI("enabled %slogging %s", conf.verbose ? "verbose " : "", conf.log); - } - - if (conf.mtu > 0) { - LOGI("setting MTU to %d", conf.mtu); - } - - if (conf.mptcp) { - LOGI("enabled multipath TCP"); - } - - if (conf.no_delay) { - LOGI("enabled TCP no-delay"); - } - - if (conf.mode != TCP_ONLY) { - LOGI("UDP relay enabled"); - } - - if (conf.mode == UDP_ONLY) { - LOGI("TCP relay disabled"); - } - - if (conf.ipv6_first) { - LOGI("prioritized IPv6 addresses in domain resolution"); - } - - if (conf.acl != NULL) { - LOGI("initializing acl..."); - acl = !init_acl(&conf); - } - -#ifdef HAVE_SETRLIMIT - /* - * No need to check the return value here. - * We will show users an error message if setrlimit(2) fails. - */ - if (conf.nofile > 1024) { - if (conf.verbose) { - LOGI("setting NOFILE to %d", conf.nofile); - } - set_nofile(conf.nofile); - } -#endif - - if (conf.fast_open) { -#ifdef TCP_FASTOPEN - LOGI("using tcp fast open"); -#else - LOGE("tcp fast open is not supported by this environment"); - conf.fast_open = 0; -#endif - } - pid_flags = conf.pid_path != NULL; USE_SYSLOG(argv[0], pid_flags); if (pid_flags) { daemonize(conf.pid_path); } - no_delay = conf.no_delay; - fast_open = conf.fast_open; - verbose = conf.verbose; - ipv6first = conf.ipv6_first; - manager_addr = conf.manager_addr; - -#ifdef __MINGW32__ - winsock_init(); -#endif - -#ifndef __MINGW32__ - // ignore SIGPIPE - signal(SIGPIPE, SIG_IGN); - signal(SIGABRT, SIG_IGN); -#endif - - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); -#ifndef __MINGW32__ - ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); - ev_signal_start(EV_DEFAULT, &sigchld_watcher); -#endif - - struct ev_loop *loop = EV_DEFAULT; - resolv_init(loop, conf.nameserver, conf.ipv6_first); - if (conf.nameserver != NULL) - LOGI("using nameserver: %s", conf.nameserver); - port_service_init(); - - cork_dllist_init(&listeners); - - for (i = 0; i < conf.remote_num; i++) { - ss_remote_t *r = conf.remotes[i]; - char *host = r->addr, - *port = elvis(r->port, conf.remote_port), - *password = elvis(r->password, conf.password), - *key = elvis(r->key, conf.key), - *method = elvis(r->method, conf.method), - *iface = elvis(r->iface, conf.iface), - *plugin = elvis(r->plugin, conf.plugin), - *plugin_opts - = elvis(r->plugin_opts, conf.plugin_opts); - - if (port == NULL || method == NULL || - (password == NULL && key == NULL)) - { - usage(); - exit(EXIT_FAILURE); - } - - LOGI("[%d/%d] listening on %s:%s", i + 1, conf.remote_num, host, port); - - LOGI("initializing ciphers... %s", method); - crypto_t *crypto = crypto_init(password, key, method); - if (crypto == NULL) - FATAL("failed to initialize ciphers"); - - /** - * "If node(host) is NULL, then the network address - * will be set to the loopback interface address," meaning that - * by default the server will bind to `lo' instead of 0.0.0.0. - * - * On Linux, when net.ipv6.bindv6only = 0 (the default), - * getaddrinfo(NULL) with AI_PASSIVE returns 0.0.0.0 and :: (in this order). - * AI_PASSIVE was meant to return a list of addresses to listen on, - * but it is impossible to listen on 0.0.0.0 and :: at the same time, - * unless `bindv6only' is enabled. - */ - struct sockaddr_storage *storage = - ss_calloc(1, sizeof(struct sockaddr_storage)); - if (get_sockaddr(host, port, storage, 1, conf.ipv6_first) == -1) { - FATAL("failed to resolve %s", host); - } - - if (plugin != NULL) { - if (!plugin_enabled) { - init_plugin(MODE_SERVER); - plugin_enabled = 1; - } - - uint16_t plugin_port = get_local_port(); - switch (storage->ss_family) { - case AF_INET: { - *(struct sockaddr_in *)storage = (struct sockaddr_in) { - .sin_addr = - (struct in_addr) { htonl(INADDR_LOOPBACK) }, - .sin_port = plugin_port - }; - } break; - case AF_INET6: { - *(struct sockaddr_in6 *)storage = (struct sockaddr_in6) { - .sin6_addr = in6addr_loopback, - .sin6_port = plugin_port - }; - } break; - } - - if (plugin_port == 0) - FATAL("failed to find a free port"); - - LOGI("plugin \"%s\" enabled", plugin); - - int err = start_plugin(plugin, plugin_opts, // user-defined plugin options - sockaddr_readable("%a", storage), - sockaddr_readable("%p", storage), // plugin destination override - host, port); - if (err) - FATAL("failed to start the plugin"); - } - - listen_ctx_t listen_ctx = { - .iface = iface, - .addr = storage, - .crypto = crypto, - .timeout = atoi(conf.timeout), - .loop = loop - }; - -#ifndef __MINGW32__ - if (conf.manager_addr != NULL) { - ev_timer_init(&listen_ctx.stat_watcher, stat_update_cb, - UPDATE_INTERVAL, UPDATE_INTERVAL); - ev_timer_start(EV_DEFAULT, &listen_ctx.stat_watcher); - } -#endif - - if (conf.mode != UDP_ONLY) { - int listenfd = bind_and_listen(storage, IPPROTO_TCP, &listen_ctx); - if (listenfd != -1) { - if (fast_open) - set_fastopen_passive(listenfd); - ev_io_init(&listen_ctx.io, accept_cb, listen_ctx.fd, EV_READ); - ev_io_start(loop, &listen_ctx.io); - cork_dllist_add(&listeners, &listen_ctx.entries); - } - } - - // Setup UDP - if (conf.mode != TCP_ONLY) { - listen_ctx_t listen_ctx_dgram = listen_ctx; - int listenfd = bind_and_listen(storage, IPPROTO_UDP, &listen_ctx_dgram); - if ((listen_ctx_dgram.fd = listenfd) != -1) { - init_udprelay(&listen_ctx_dgram); - } - } - } - #ifndef __MINGW32__ // setuid if (conf.user && !run_as(conf.user)) { @@ -1212,40 +854,9 @@ main(int argc, char **argv) } #endif - // Init connections - cork_dllist_init(&connections); - - // start ev loop - ev_run(loop, 0); - - if (verbose) { - LOGI("closed gracefully"); - } - -#ifndef __MINGW32__ - if (conf.manager_addr != NULL) { - ev_timer_stop(loop, &stat_watcher); - } -#endif - - if (plugin_enabled) { - stop_plugin(); - } - - resolv_shutdown(loop); - - if (conf.mode != UDP_ONLY) { - free_listeners(loop); - free_connections(loop); - } - - if (conf.mode != TCP_ONLY) { - free_udprelay(loop); - } - -#ifdef __MINGW32__ - winsock_cleanup(); -#endif - - return ret_val; + no_delay = conf.no_delay; + fast_open = conf.fast_open; + verbose = conf.verbose; + ipv6first = conf.ipv6_first; + return start_relay(&conf, NULL, NULL); } diff --git a/src/server.h b/src/server.h deleted file mode 100644 index 3428a2ef..00000000 --- a/src/server.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * server.h - Define shadowsocks server's buffers and callbacks - * - * Copyright (C) 2013 - 2019, Max Lv - * - * This file is part of the shadowsocks-libev. - * - * shadowsocks-libev is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * shadowsocks-libev is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with shadowsocks-libev; see the file COPYING. If not, see - * . - */ - -#ifndef _SERVER_H -#define _SERVER_H - -#include -#include - -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif - -#ifdef __MINGW32__ -#include "winsock.h" -#endif - -#include "crypto.h" -#include "jconf.h" -#include "resolv.h" -#include "netutils.h" - -#include "common.h" - -typedef struct server_ctx { - ev_io io; - ev_timer watcher; - int connected; - struct server *server; -} server_ctx_t; - -#ifdef USE_NFCONNTRACK_TOS - -#include -#include - -struct dscptracker { - struct nf_conntrack *ct; - long unsigned int mark; - unsigned int dscp; - unsigned int packet_count; -}; - -#endif - -struct query; - -typedef struct server { - int fd; - int stage; - int frag; - - buffer_t *buf; - - crypto_t *crypto; - cipher_ctx_t *e_ctx; - cipher_ctx_t *d_ctx; - struct server_ctx *recv_ctx; - struct server_ctx *send_ctx; - struct listen_ctx *listen_ctx; - struct remote *remote; - - struct query *query; - - struct cork_dllist_item entries; -#ifdef USE_NFCONNTRACK_TOS - struct dscptracker *tracker; -#endif -} server_t; - -typedef struct query { - server_t *server; - char hostname[MAX_HOSTNAME_LEN]; -} query_t; - -typedef struct remote_ctx { - ev_io io; - int connected; - struct remote *remote; -} remote_ctx_t; - -typedef struct remote { - int fd; -#ifdef TCP_FASTOPEN_WINSOCK - OVERLAPPED olap; - int connect_ex_done; -#endif - buffer_t *buf; - struct remote_ctx *recv_ctx; - struct remote_ctx *send_ctx; - struct server *server; -} remote_t; - -#endif // _SERVER_H diff --git a/src/tunnel.c b/src/tunnel.c index 5e3ed9d4..b4efdf31 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -56,8 +56,6 @@ #include "winsock.h" #include "relay.h" -static void accept_cb(EV_P_ ev_io *w, int revents); - int verbose = 0; int acl = 0; int ipv6first = 0; @@ -68,13 +66,6 @@ int vpn = 0; int remote_dns = 1; // resolve hostname remotely static int no_delay = 0; -static int ret_val = 0; - -static struct ev_signal sigint_watcher; -static struct ev_signal sigterm_watcher; -#ifndef __MINGW32__ -static struct ev_signal sigchld_watcher; -#endif void server_recv_cb(EV_P_ ev_io *w, int revents) @@ -395,7 +386,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } } -static void +void accept_cb(EV_P_ ev_io *w, int revents) { struct listen_ctx *listener = (struct listen_ctx *)w; @@ -435,39 +426,12 @@ accept_cb(EV_P_ ev_io *w, int revents) } } -static void -signal_cb(EV_P_ ev_signal *w, int revents) -{ - if (revents & EV_SIGNAL) { - switch (w->signum) { -#ifndef __MINGW32__ - case SIGCHLD: - if (!is_plugin_running()) { - LOGE("plugin service exit unexpectedly"); - ret_val = -1; - } else - return; -#endif - case SIGINT: - case SIGTERM: - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); -#ifndef __MINGW32__ - ev_signal_stop(EV_DEFAULT, &sigchld_watcher); -#endif - ev_unloop(EV_A_ EVUNLOOP_ALL); - } - } -} - int main(int argc, char **argv) { USE_TTY(); srand(time(NULL)); - int i; - int plugin_enabled = 0; int pid_flags = 0; jconf_t conf = jconf_default; @@ -476,65 +440,23 @@ main(int argc, char **argv) exit(EXIT_FAILURE); } - if (!(conf.remotes != NULL && - conf.remote_num > 0)) { - FATAL("at least one server should be specified"); - } - - if (conf.local_port == NULL) { - conf.local_port = "0"; - LOGE("warning: random local port will be assigned"); - } - - if (conf.log) { - USE_LOGFILE(conf.log); - LOGI("enabled %slogging %s", conf.verbose ? "verbose " : "", conf.log); - } - - if (conf.mtu > 0) { - LOGI("setting MTU to %d", conf.mtu); - } - - if (conf.mptcp) { - LOGI("enabled multipath TCP"); - } - - if (conf.no_delay) { - LOGI("enabled TCP no-delay"); + pid_flags = conf.pid_path != NULL; + USE_SYSLOG(argv[0], pid_flags); + if (pid_flags) { + daemonize(conf.pid_path); } - if (conf.ipv6_first) { - LOGI("prioritized IPv6 addresses in domain resolution"); +#ifndef __MINGW32__ + // setuid + if (conf.user && !run_as(conf.user)) { + FATAL("failed to switch user"); } -#ifdef HAVE_SETRLIMIT - /* - * No need to check the return value here. - * We will show users an error message if setrlimit(2) fails. - */ - if (conf.nofile > 1024) { - if (conf.verbose) { - LOGI("setting NOFILE to %d", conf.nofile); - } - set_nofile(conf.nofile); + if (geteuid() == 0) { + LOGI("running from root user"); } #endif - if (conf.fast_open) { -#ifdef TCP_FASTOPEN - LOGI("using tcp fast open"); -#else - LOGE("tcp fast open is not supported by this environment"); - conf.fast_open = 0; -#endif - } - - pid_flags = conf.pid_path != NULL; - USE_SYSLOG(argv[0], pid_flags); - if (pid_flags) { - daemonize(conf.pid_path); - } - no_delay = conf.no_delay; ipv6first = conf.ipv6_first; fast_open = conf.fast_open; @@ -542,198 +464,5 @@ main(int argc, char **argv) #ifdef __ANDROID__ vpn = conf.vpn; #endif - -#ifndef __MINGW32__ - // ignore SIGPIPE - signal(SIGPIPE, SIG_IGN); - signal(SIGABRT, SIG_IGN); -#endif - - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); -#ifndef __MINGW32__ - ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); - ev_signal_start(EV_DEFAULT, &sigchld_watcher); -#endif - - // Setup proxy context - struct ev_loop *loop = EV_DEFAULT; - struct listen_ctx listen_ctx = { - .mtu = conf.mtu, - .mptcp = conf.mptcp, - .reuse_port = conf.reuse_port, - .remote_num = conf.remote_num, - .remotes = ss_calloc(conf.remote_num, sizeof(struct remote_cnf *)), - .timeout = atoi(conf.timeout), - .loop = loop - }; - - ss_addr_t *tunnel_addr = &conf.tunnel_addr; - if (tunnel_addr->host == NULL || - tunnel_addr->port == NULL) { - FATAL("tunnel address either undefined or invalid"); - } - - ssocks_addr_t *destaddr = &listen_ctx.destaddr; - destaddr->addr = ss_calloc(1, sizeof(struct sockaddr_storage)); - if (get_sockaddr(tunnel_addr->host, tunnel_addr->port, - destaddr->addr, !remote_dns, conf.ipv6_first) == -1) - { - destaddr->dname = tunnel_addr->host; - destaddr->dname_len = strlen(tunnel_addr->host); - destaddr->port = htons(atoi(tunnel_addr->port)); - } - - // else LOGI("%s", sockaddr_readable("%a:%p", destaddr->addr)); - - for (i = 0; i < conf.remote_num; i++) { - ss_remote_t *r = conf.remotes[i]; - - char *host = r->addr, - *port = elvis(r->port, conf.remote_port), - *password = elvis(r->password, conf.password), - *key = elvis(r->key, conf.key), - *method = elvis(r->method, conf.method), - *iface = elvis(r->iface, conf.iface), - *plugin = elvis(r->plugin, conf.plugin), - *plugin_opts - = elvis(r->plugin_opts, conf.plugin_opts); - - if (host == NULL || port == NULL || - (password == NULL && key == NULL)) - { - usage(); - exit(EXIT_FAILURE); - } - - // Setup keys - LOGI("[%d/%d] server %s:%s", i + 1, conf.remote_num, host, port); - LOGI("initializing ciphers... %s", method); - crypto_t *crypto = crypto_init(password, key, method); - if (crypto == NULL) - FATAL("failed to initialize ciphers"); - - struct sockaddr_storage *storage - = ss_calloc(1, sizeof(struct sockaddr_storage)); - if (get_sockaddr(host, port, storage, 1, conf.ipv6_first) == -1) { - FATAL("failed to resolve %s", host); - } - - if (plugin != NULL) { - if (!plugin_enabled) { - init_plugin(MODE_CLIENT); - plugin_enabled = 1; - } - - uint16_t plugin_port = get_local_port(); - switch (storage->ss_family) { - case AF_INET: { - *(struct sockaddr_in *)storage = (struct sockaddr_in) { - .sin_addr = - (struct in_addr) { htonl(INADDR_LOOPBACK) }, - .sin_port = plugin_port - }; - } break; - case AF_INET6: { - *(struct sockaddr_in6 *)storage = (struct sockaddr_in6) { - .sin6_addr = in6addr_loopback, - .sin6_port = plugin_port - }; - } break; - } - - if (plugin_port == 0) - FATAL("failed to find a free port"); - - LOGI("plugin \"%s\" enabled", plugin); - - int err = start_plugin(plugin, plugin_opts, // user-defined plugin options - host, port, // user-defined destination address - sockaddr_readable("%a", storage), - sockaddr_readable("%p", storage)); - if (err) - FATAL("failed to start plugin %s", plugin); - } - - listen_ctx.remotes[i] = &(remote_cnf_t) { - .iface = iface, - .addr = storage, - .crypto = crypto - }; - } - - ss_dscp_t **dscp = conf.dscp; - char *local_addr = conf.local_addr, - *local_port = conf.local_port; - - listen_ctx_t listen_ctx_current = listen_ctx; - do { - if (listen_ctx_current.tos) { - LOGI("listening on %s:%s (TOS 0x%x)", - local_addr, local_port, listen_ctx_current.tos); - } else { - LOGI("listening on %s:%s", local_addr, local_port); - } - - struct sockaddr_storage *storage = &(struct sockaddr_storage) {}; - if (get_sockaddr(local_addr, local_port, - storage, 1, conf.ipv6_first) == -1) - { - FATAL("failed to resolve %s", local_addr); - } - - if (conf.mode != UDP_ONLY) { - // Setup socket - int listenfd = bind_and_listen(storage, IPPROTO_TCP, &listen_ctx_current); - if (listenfd != -1) { - if (fast_open) - set_fastopen_passive(listenfd); - ev_io_init(&listen_ctx_current.io, accept_cb, listen_ctx_current.fd, EV_READ); - ev_io_start(loop, &listen_ctx_current.io); - } - } - - // Setup UDP - if (conf.mode != TCP_ONLY) { - listen_ctx_t listen_ctx_dgram = listen_ctx; - int listenfd = bind_and_listen(storage, IPPROTO_UDP, &listen_ctx_dgram); - if ((listen_ctx_dgram.fd = listenfd) != -1) { - init_udprelay(&listen_ctx_dgram); - } - } - - if (conf.mode == UDP_ONLY) { - LOGI("TCP relay disabled"); - } - - // Handle additional TOS/DSCP listening ports - if (*dscp != NULL) { - listen_ctx_current = listen_ctx; - local_port = (*dscp)->port; - listen_ctx_current.tos = (*dscp)->dscp << 2; - } - } while (*(dscp++) != NULL); - - // setuid - if (conf.user && !run_as(conf.user)) - FATAL("failed to switch user"); - - if (geteuid() == 0) - LOGI("running from root user"); - - // Init connections - cork_dllist_init(&connections); - - ev_run(loop, 0); - - if (plugin_enabled) - stop_plugin(); - - if (conf.mode != TCP_ONLY) { - free_udprelay(loop); - } - - return ret_val; + return start_relay(&conf, NULL, NULL); } diff --git a/src/tunnel.h b/src/tunnel.h deleted file mode 100644 index c3a97ffa..00000000 --- a/src/tunnel.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * tunnel.h - Define tunnel's buffers and callbacks - * - * Copyright (C) 2013 - 2019, Max Lv - * - * This file is part of the shadowsocks-libev. - * - * shadowsocks-libev is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * shadowsocks-libev is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with shadowsocks-libev; see the file COPYING. If not, see - * . - */ - -#ifndef _TUNNEL_H -#define _TUNNEL_H - -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif - -#ifdef __MINGW32__ -#include "winsock.h" -#endif - -#include "crypto.h" -#include "jconf.h" - -#include "common.h" - -typedef struct server_ctx { - ev_io io; - int connected; - struct server *server; -} server_ctx_t; - -typedef struct server { - int fd; - - buffer_t *buf; - - struct server_ctx *recv_ctx; - struct server_ctx *send_ctx; - struct remote *remote; - - struct listen_ctx *listener; -} server_t; - -typedef struct remote_ctx { - ev_io io; - ev_timer watcher; - int connected; - struct remote *remote; -} remote_ctx_t; - -typedef struct remote { - int fd; -#ifdef TCP_FASTOPEN_WINSOCK - OVERLAPPED olap; - int connect_ex_done; -#endif - buffer_t *buf; - struct remote_ctx *recv_ctx; - struct remote_ctx *send_ctx; - struct server *server; - struct sockaddr *addr; -} remote_t; - -#endif // _TUNNEL_H