From ea9d293abe52e1671ae5b4a6a7456e3eaa100ecd Mon Sep 17 00:00:00 2001 From: Max Lv Date: Mon, 6 Jul 2015 13:48:32 +0800 Subject: [PATCH] add UDP relay only mode --- src/common.h | 4 ++ src/local.c | 149 +++++++++++++++++++++++++++------------------- src/redir.c | 47 +++++++++------ src/server.c | 75 +++++++++++++---------- src/shadowsocks.h | 4 +- src/tunnel.c | 46 ++++++++------ src/utils.c | 7 ++- 7 files changed, 197 insertions(+), 135 deletions(-) diff --git a/src/common.h b/src/common.h index de8e0460..a63a8fdc 100644 --- a/src/common.h +++ b/src/common.h @@ -43,6 +43,10 @@ #define SOL_TCP IPPROTO_TCP #endif +#define TCP_ONLY 0 +#define TCP_AND_UDP 1 +#define UDP_ONLY 3 + int init_udprelay(const char *server_host, const char *server_port, #ifdef UDPRELAY_LOCAL const struct sockaddr *remote_addr, const int remote_addr_len, diff --git a/src/local.c b/src/local.c index 3439d944..e37ea1dc 100644 --- a/src/local.c +++ b/src/local.c @@ -79,9 +79,11 @@ #define BUF_SIZE 2048 #endif -int acl = 0; int verbose = 0; -int udprelay = 0; + +static int acl = 0; +static int mode = TCP_ONLY; + static int fast_open = 0; #ifdef HAVE_SETRLIMIT #ifndef LIB_ONLY @@ -342,7 +344,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) int udp_assc = 0; - if (udprelay && request->cmd == 3) { + if (mode != TCP_ONLY && request->cmd == 3) { udp_assc = 1; socklen_t addr_len = sizeof(sock_addr); getsockname(server->fd, (struct sockaddr *)&sock_addr, @@ -429,7 +431,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) LOGI("connect to %s:%s", host, port); } - if ((acl && (request->atyp == 1 || request->atyp == 3) && acl_contains_ip(host))) { + if ((acl && (request->atyp == 1 || request->atyp == 4) && acl_contains_ip(host))) { if (verbose) { LOGI("bypass %s:%s", host, port); } @@ -901,7 +903,7 @@ int main(int argc, char **argv) USE_TTY(); - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:uv", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:uUv", long_options, &option_index)) != -1) { switch (c) { case 0: @@ -950,7 +952,10 @@ int main(int argc, char **argv) user = optarg; break; case 'u': - udprelay = 1; + mode = TCP_AND_UDP; + break; + case 'U': + mode = UDP_ONLY; break; case 'v': verbose = 1; @@ -1060,18 +1065,6 @@ int main(int argc, char **argv) LOGI("initialize ciphers... %s", method); int m = enc_init(password, method); - // Setup socket - int listenfd; - listenfd = create_and_bind(local_addr, local_port); - if (listenfd < 0) { - FATAL("bind() error"); - } - if (listen(listenfd, SOMAXCONN) == -1) { - FATAL("listen() error"); - } - setnonblocking(listenfd); - LOGI("listening at %s:%s", local_addr, local_port); - // Setup proxy context struct listen_ctx listen_ctx; listen_ctx.remote_num = remote_num; @@ -1079,7 +1072,7 @@ int main(int argc, char **argv) for (i = 0; i < remote_num; i++) { char *host = remote_addr[i].host; char *port = remote_addr[i].port == NULL ? remote_port : - remote_addr[i].port; + remote_addr[i].port; struct sockaddr_storage *storage = malloc(sizeof(struct sockaddr_storage)); memset(storage, 0, sizeof(struct sockaddr_storage)); if (get_sockaddr(host, port, storage, 1) == -1) { @@ -1088,21 +1081,38 @@ int main(int argc, char **argv) listen_ctx.remote_addr[i] = (struct sockaddr *)storage; } listen_ctx.timeout = atoi(timeout); - listen_ctx.fd = listenfd; listen_ctx.iface = iface; listen_ctx.method = m; struct ev_loop *loop = EV_DEFAULT; - ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx.io); + + if (mode != UDP_ONLY) { + // Setup socket + int listenfd; + listenfd = create_and_bind(local_addr, local_port); + if (listenfd < 0) { + FATAL("bind() error"); + } + if (listen(listenfd, SOMAXCONN) == -1) { + FATAL("listen() error"); + } + setnonblocking(listenfd); + + listen_ctx.fd = listenfd; + + ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); + ev_io_start(loop, &listen_ctx.io); + } // Setup UDP - if (udprelay) { + if (mode != TCP_ONLY) { LOGI("udprelay enabled"); init_udprelay(local_addr, local_port, listen_ctx.remote_addr[0], get_sockaddr_len(listen_ctx.remote_addr[0]), m, listen_ctx.timeout, iface); } + LOGI("listening at %s:%s", local_addr, local_port); + // setuid if (user != NULL) { run_as(user); @@ -1119,10 +1129,15 @@ int main(int argc, char **argv) } // Clean up - free_connections(loop); - free_udprelay(); + if (mode != UDP_ONLY) { + ev_io_stop(loop, &listen_ctx.io); + free_connections(loop); + } + + if (mode != TCP_ONLY) { + free_udprelay(); + } - ev_io_stop(loop, &listen_ctx.io); for (i = 0; i < remote_num; i++) { free(listen_ctx.remote_addr[i]); } @@ -1153,7 +1168,7 @@ int start_ss_local_server(profile_t profile) int local_port = profile.local_port; int timeout = profile.timeout; - udprelay = profile.udp_relay; + mode = profile.mode; fast_open = profile.fast_open; verbose = profile.verbose; @@ -1191,50 +1206,57 @@ int start_ss_local_server(profile_t profile) LOGI("initialize ciphers... %s", method); int m = enc_init(password, method); - // Setup socket - int listenfd; - listenfd = create_and_bind(local_addr, local_port_str); - if (listenfd < 0) { - ERROR("bind()"); - return -1; - } - if (listen(listenfd, SOMAXCONN) == -1) { - ERROR("listen()"); - return -1; - } - setnonblocking(listenfd); - LOGI("listening at %s:%s", local_addr, local_port_str); - - // Setup proxy context - struct listen_ctx listen_ctx; - - listen_ctx.remote_num = 1; - listen_ctx.remote_addr = malloc(sizeof(struct sockaddr *)); struct sockaddr_storage *storage = malloc(sizeof(struct sockaddr_storage)); memset(storage, 0, sizeof(struct sockaddr_storage)); if (get_sockaddr(remote_host, remote_port_str, storage, 1) == -1) { return -1; } + + // Setup proxy context + struct ev_loop *loop = EV_DEFAULT; + struct listen_ctx listen_ctx; + + listen_ctx.remote_num = 1; + listen_ctx.remote_addr = malloc(sizeof(struct sockaddr *)); listen_ctx.remote_addr[0] = (struct sockaddr *)storage; listen_ctx.timeout = timeout; - listen_ctx.fd = listenfd; listen_ctx.method = m; listen_ctx.iface = NULL; - struct ev_loop *loop = EV_DEFAULT; - ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx.io); + if (mode != UDP_ONLY) { + // Setup socket + int listenfd; + listenfd = create_and_bind(local_addr, local_port_str); + if (listenfd < 0) { + ERROR("bind()"); + return -1; + } + if (listen(listenfd, SOMAXCONN) == -1) { + ERROR("listen()"); + return -1; + } + setnonblocking(listenfd); - // Setup UDP - if (udprelay) { - LOGI("udprelay enabled"); - init_udprelay(local_addr, local_port_str, listen_ctx.remote_addr[0], - get_sockaddr_len(listen_ctx.remote_addr[0]), m, - listen_ctx.timeout, NULL); + listen_ctx.fd = listenfd; + + ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); + ev_io_start(loop, &listen_ctx.io); } - // Init connections - cork_dllist_init(&connections); + // Setup UDP + if (mode != TCP_ONLY) { + LOGI("udprelay enabled"); + struct sockaddr *addr = (struct sockaddr *)storage; + init_udprelay(local_addr, local_port_str, addr, + get_sockaddr_len(addr), m, timeout, NULL); + } + + LOGI("listening at %s:%s", local_addr, local_port_str); + + if (mode != UDP_ONLY) { + // Init connections + cork_dllist_init(&connections); + } // Enter the loop ev_run(loop, 0); @@ -1244,14 +1266,17 @@ int start_ss_local_server(profile_t profile) } // Clean up - free_connections(loop); - if (udprelay) { + if (mode != TCP_ONLY) { free_udprelay(); } - ev_io_stop(loop, &listen_ctx.io); + if (mode != UDP_ONLY) { + ev_io_stop(loop, &listen_ctx.io); + free_connections(loop); + close(listen_ctx.fd); + } + free(listen_ctx.remote_addr); - close(listen_ctx.fd); #ifdef __MINGW32__ winsock_cleanup(); diff --git a/src/redir.c b/src/redir.c index 23621552..c8b0011e 100644 --- a/src/redir.c +++ b/src/redir.c @@ -81,7 +81,8 @@ static void free_server(struct server *server); static void close_and_free_server(EV_P_ struct server *server); int verbose = 0; -int udprelay = 0; + +static int mode = TCP_ONLY; int getdestaddr(int fd, struct sockaddr_storage *destaddr) { @@ -613,7 +614,7 @@ int main(int argc, char **argv) opterr = 0; - while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:c:b:a:vu")) != -1) { + while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:c:b:a:uUv")) != -1) { switch (c) { case 's': if (remote_num < MAX_REMOTE_NUM) { @@ -650,7 +651,10 @@ int main(int argc, char **argv) user = optarg; break; case 'u': - udprelay = 1; + mode = TCP_AND_UDP; + break; + case 'U': + mode = UDP_ONLY; break; case 'v': verbose = 1; @@ -724,18 +728,6 @@ int main(int argc, char **argv) LOGI("initialize ciphers... %s", method); int m = enc_init(password, method); - // Setup socket - int listenfd; - listenfd = create_and_bind(local_addr, local_port); - if (listenfd < 0) { - FATAL("bind() error"); - } - if (listen(listenfd, SOMAXCONN) == -1) { - FATAL("listen() error"); - } - setnonblocking(listenfd); - LOGI("listening at %s:%s", local_addr, local_port); - // Setup proxy context struct listen_ctx listen_ctx; listen_ctx.remote_num = remote_num; @@ -752,20 +744,37 @@ int main(int argc, char **argv) listen_ctx.remote_addr[i] = (struct sockaddr *)storage; } listen_ctx.timeout = atoi(timeout); - listen_ctx.fd = listenfd; listen_ctx.method = m; struct ev_loop *loop = EV_DEFAULT; - ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx.io); + + if (mode != UDP_ONLY) { + // Setup socket + int listenfd; + listenfd = create_and_bind(local_addr, local_port); + if (listenfd < 0) { + FATAL("bind() error"); + } + if (listen(listenfd, SOMAXCONN) == -1) { + FATAL("listen() error"); + } + setnonblocking(listenfd); + + listen_ctx.fd = listenfd; + + ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); + ev_io_start(loop, &listen_ctx.io); + } // Setup UDP - if (udprelay) { + if (mode != TCP_ONLY) { LOGI("udprelay enabled"); init_udprelay(local_addr, local_port, listen_ctx.remote_addr[0], get_sockaddr_len(listen_ctx.remote_addr[0]), m, listen_ctx.timeout, NULL); } + LOGI("listening at %s:%s", local_addr, local_port); + // setuid if (user != NULL) { run_as(user); diff --git a/src/server.c b/src/server.c index 5f04da4c..d789f19d 100644 --- a/src/server.c +++ b/src/server.c @@ -99,9 +99,11 @@ static void close_and_free_server(EV_P_ struct server *server); static void server_resolve_cb(struct sockaddr *addr, void *data); -int acl = 0; int verbose = 0; -int udprelay = 0; + +static int acl = 0; +static int mode = TCP_ONLY; + static int fast_open = 0; #ifdef HAVE_SETRLIMIT static int nofile = 0; @@ -1100,7 +1102,7 @@ int main(int argc, char **argv) USE_TTY(); - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:i:d:a:uv", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:i:d:a:uUv", long_options, &option_index)) != -1) { switch (c) { case 0: @@ -1147,7 +1149,10 @@ int main(int argc, char **argv) user = optarg; break; case 'u': - udprelay = 1; + mode = TCP_AND_UDP; + break; + case 'U': + mode = UDP_ONLY; break; case 'v': verbose = 1; @@ -1288,39 +1293,41 @@ int main(int argc, char **argv) int index = --server_num; const char * host = server_host[index]; - // Bind to port - int listenfd; - listenfd = create_and_bind(host, server_port); - if (listenfd < 0) { - FATAL("bind() error"); + if (mode != UDP_ONLY) { + // Bind to port + int listenfd; + listenfd = create_and_bind(host, server_port); + if (listenfd < 0) { + FATAL("bind() error"); + } + if (listen(listenfd, SSMAXCONN) == -1) { + FATAL("listen() error"); + } + setnonblocking(listenfd); + struct listen_ctx *listen_ctx = &listen_ctx_list[index]; + + // Setup proxy context + listen_ctx->timeout = atoi(timeout); + listen_ctx->fd = listenfd; + listen_ctx->method = m; + listen_ctx->iface = iface; + listen_ctx->loop = loop; + + ev_io_init(&listen_ctx->io, accept_cb, listenfd, EV_READ); + ev_io_start(loop, &listen_ctx->io); } - if (listen(listenfd, SSMAXCONN) == -1) { - FATAL("listen() error"); - } - setnonblocking(listenfd); - LOGI("listening at %s:%s", host ? host : "*", server_port); - - struct listen_ctx *listen_ctx = &listen_ctx_list[index]; - - // Setup proxy context - listen_ctx->timeout = atoi(timeout); - listen_ctx->fd = listenfd; - listen_ctx->method = m; - listen_ctx->iface = iface; - listen_ctx->loop = loop; - - ev_io_init(&listen_ctx->io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx->io); // Setup UDP - if (udprelay) { + if (mode != TCP_ONLY) { init_udprelay(server_host[index], server_port, m, atoi(timeout), iface); } + LOGI("listening at %s:%s", host ? host : "*", server_port); + } - if (udprelay) { + if (mode != TCP_ONLY) { LOGI("udprelay enabled"); } @@ -1342,13 +1349,17 @@ int main(int argc, char **argv) // Clean up for (int i = 0; i <= server_num; i++) { struct listen_ctx *listen_ctx = &listen_ctx_list[i]; - ev_io_stop(loop, &listen_ctx->io); - close(listen_ctx->fd); + if (mode != UDP_ONLY) { + ev_io_stop(loop, &listen_ctx->io); + close(listen_ctx->fd); + } } - free_connections(loop); + if (mode != UDP_ONLY) { + free_connections(loop); + } - if (udprelay) { + if (mode != TCP_ONLY) { free_udprelay(); } diff --git a/src/shadowsocks.h b/src/shadowsocks.h index 3e939029..2a2bc780 100644 --- a/src/shadowsocks.h +++ b/src/shadowsocks.h @@ -36,7 +36,7 @@ typedef struct { char *acl; // file path to acl char *log; // file path to log int fast_open; // enable tcp fast open - int udp_relay; // enable udp relay + int mode; // enable udp relay int verbose; // verbose mode } profile_t; @@ -53,7 +53,7 @@ typedef struct { .acl = NULL, .log = NULL, .fast_open = 0, - .udp_relay = 0, + .mode = 0, .verbose = 0 }; */ diff --git a/src/tunnel.c b/src/tunnel.c index cdc3a50b..0056238d 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -86,7 +86,7 @@ static void free_server(struct server *server); static void close_and_free_server(EV_P_ struct server *server); int verbose = 0; -int udprelay = 0; +static int mode = TCP_ONLY; #ifndef __MINGW32__ static int setnonblocking(int fd) @@ -654,7 +654,7 @@ int main(int argc, char **argv) USE_TTY(); - while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:uv")) != -1) { + while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:uUv")) != -1) { switch (c) { case 's': if (remote_num < MAX_REMOTE_NUM) { @@ -691,7 +691,10 @@ int main(int argc, char **argv) local_addr = optarg; break; case 'u': - udprelay = 1; + mode = TCP_AND_UDP; + break; + case 'U': + mode = UDP_ONLY; break; case 'L': tunnel_addr_str = optarg; @@ -782,18 +785,6 @@ int main(int argc, char **argv) LOGI("initialize ciphers... %s", method); int m = enc_init(password, method); - // Setup socket - int listenfd; - listenfd = create_and_bind(local_addr, local_port); - if (listenfd < 0) { - FATAL("bind() error:"); - } - if (listen(listenfd, SOMAXCONN) == -1) { - FATAL("listen() error:"); - } - setnonblocking(listenfd); - LOGI("listening at %s:%s", local_addr, local_port); - // Setup proxy context struct listen_ctx listen_ctx; listen_ctx.tunnel_addr = tunnel_addr; @@ -811,22 +802,39 @@ int main(int argc, char **argv) listen_ctx.remote_addr[i] = (struct sockaddr *)storage; } listen_ctx.timeout = atoi(timeout); - listen_ctx.fd = listenfd; listen_ctx.iface = iface; listen_ctx.method = m; struct ev_loop *loop = EV_DEFAULT; - ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx.io); + + if (mode != UDP_ONLY) { + // Setup socket + int listenfd; + listenfd = create_and_bind(local_addr, local_port); + if (listenfd < 0) { + FATAL("bind() error:"); + } + if (listen(listenfd, SOMAXCONN) == -1) { + FATAL("listen() error:"); + } + setnonblocking(listenfd); + + listen_ctx.fd = listenfd; + + ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); + ev_io_start(loop, &listen_ctx.io); + } // Setup UDP - if (udprelay) { + if (mode != TCP_ONLY) { LOGI("udprelay enabled"); init_udprelay(local_addr, local_port, listen_ctx.remote_addr[0], get_sockaddr_len(listen_ctx.remote_addr[0]), tunnel_addr, m, listen_ctx.timeout, iface); } + LOGI("listening at %s:%s", local_addr, local_port); + // setuid if (user != NULL) { run_as(user); diff --git a/src/utils.c b/src/utils.c index 5ee3dbd9..bb4c0fbd 100644 --- a/src/utils.c +++ b/src/utils.c @@ -238,7 +238,12 @@ void usage() " not available in server mode\n"); printf("\n"); printf( - " [-u] enable udprelay mode,\n"); + " [-u] enable UDP relay,\n"); + printf( + " TPROXY is required in redir mode\n"); + printf("\n"); + printf( + " [-U] enable UDP relay and disable TCP relay,\n"); printf( " TPROXY is required in redir mode\n"); printf("\n");