Browse Source

fix some bugs and clean up

pull/2430/head
PantherJohn 6 years ago
parent
commit
73d8f01a13
16 changed files with 690 additions and 1695 deletions
  1. 1
      acl/delegation.acl
  2. 2
      src/Makefile.am
  3. 5
      src/cache.c
  4. 337
      src/local.c
  5. 92
      src/local.h
  6. 44
      src/netutils.c
  7. 3
      src/netutils.h
  8. 3
      src/plugin.c
  9. 324
      src/redir.c
  10. 76
      src/redir.h
  11. 601
      src/relay.c
  12. 5
      src/relay.h
  13. 403
      src/server.c
  14. 115
      src/server.h
  15. 295
      src/tunnel.c
  16. 79
      src/tunnel.h

1
acl/delegation.acl

@ -4,6 +4,7 @@
#[bypass_all]
[bypass_list]
127.0.0.1
(^|\.)bing\.com$
[proxy_list: Seattle, WA]

2
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

5
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

337
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

92
src/local.h

@ -1,92 +0,0 @@
/*
* local.h - Define the client's buffers and callbacks
*
* Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
*
* 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
* <http://www.gnu.org/licenses/>.
*/
#ifndef _LOCAL_H
#define _LOCAL_H
#include <libcork/ds.h>
#ifdef HAVE_LIBEV_EV_H
#include <libev/ev.h>
#else
#include <ev.h>
#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

44
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);
}

3
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

3
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__

324
src/redir.c

@ -37,9 +37,6 @@
#include <unistd.h>
#include <getopt.h>
#include <limits.h>
//#include <linux/if.h>
//#include <linux/netfilter_ipv4.h>
//#include <linux/netfilter_ipv6/ip6_tables.h>
#include <libcork/core.h>
@ -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);
}

76
src/redir.h

@ -1,76 +0,0 @@
/* * redir.h - Define the redirector's buffers and callbacks
*
* Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
*
* 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
* <http://www.gnu.org/licenses/>.
*/
#ifndef _REDIR_H
#define _REDIR_H
#ifdef HAVE_LIBEV_EV_H
#include <libev/ev.h>
#else
#include <ev.h>
#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

601
src/relay.c

@ -35,6 +35,7 @@
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/un.h>
#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;
}

5
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

403
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);
}

115
src/server.h

@ -1,115 +0,0 @@
/*
* server.h - Define shadowsocks server's buffers and callbacks
*
* Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
*
* 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
* <http://www.gnu.org/licenses/>.
*/
#ifndef _SERVER_H
#define _SERVER_H
#include <time.h>
#include <libcork/ds.h>
#ifdef HAVE_LIBEV_EV_H
#include <libev/ev.h>
#else
#include <ev.h>
#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 <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
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

295
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);
}

79
src/tunnel.h

@ -1,79 +0,0 @@
/*
* tunnel.h - Define tunnel's buffers and callbacks
*
* Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
*
* 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
* <http://www.gnu.org/licenses/>.
*/
#ifndef _TUNNEL_H
#define _TUNNEL_H
#ifdef HAVE_LIBEV_EV_H
#include <libev/ev.h>
#else
#include <ev.h>
#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
Loading…
Cancel
Save