mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-24 00:54:02 +00:00
add ip addr support
This commit is contained in:
@@ -11,7 +11,7 @@ It is a port of [shadowsocks](https://github.com/shadowsocks/shadowsocks)
|
||||
created by [@clowwindy](https://github.com/clowwindy) maintained by
|
||||
[@madeye](https://github.com/madeye) and [@linusyang](https://github.com/linusyang).
|
||||
|
||||
Current version: 2.2.4 | [Changelog](debian/changelog)
|
||||
Current version: 2.3.0 | [Changelog](debian/changelog)
|
||||
|
||||
Travis CI: [](https://travis-ci.org/shadowsocks/shadowsocks-libev) | Jenkins Matrix: [](https://jenkins.shadowvpn.org/job/Shadowsocks-libev/)
|
||||
|
||||
@@ -272,6 +272,12 @@ Usage
|
||||
[--acl <acl_file>] config file of ACL (Access Control List)
|
||||
only available in local and server mode
|
||||
|
||||
[--manager_address <addr>] UNIX domain socket address
|
||||
only available in server and manager mode
|
||||
|
||||
[--executable <path>] path to the executable of ss-server
|
||||
only available in manager mode
|
||||
|
||||
[-v] verbose mode
|
||||
|
||||
notes:
|
||||
|
||||
+37
-2
@@ -23,6 +23,7 @@
|
||||
.ds Re \fBss-redir\fR
|
||||
.ds Se \fBss-server\fR
|
||||
.ds Tu \fBss-tunnel\fR
|
||||
.ds Ma \fBss-manager\fR
|
||||
.ds Me \fBShadowsocks\fR
|
||||
|
||||
.TH SHADOWSOCKS-LIBEV 8 "January 7, 2015"
|
||||
@@ -30,7 +31,7 @@
|
||||
shadowsocks-libev \- a lightweight and secure socks5 proxy
|
||||
|
||||
.SH SYNOPSIS
|
||||
\*(Lo|\*(Re|\*(Se
|
||||
\*(Lo|\*(Re|\*(Se|\*(Tu|\*(Ma
|
||||
\-s server_host \-p server_port
|
||||
\-l local_port \-k password
|
||||
\-m encrypt_method \-f pid_file
|
||||
@@ -48,6 +49,11 @@ machines to proxy TCP traffic. \*(Tu is a tool for local port forwarding.
|
||||
While \*(Lo works as a standard socks5 proxy, \*(Re works as a transparent proxy
|
||||
and requires netfilter's NAT module. For more information, check out the example
|
||||
section.
|
||||
.PP
|
||||
\*(Ma is a controller for multi-user management and traffic statistics, using UNIX
|
||||
domain socket to talk with \*(Se. Also, it provides a UNIX domain socket or IP based
|
||||
API for other software. About the details of this API, please refer to the protocol
|
||||
section.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
@@ -75,7 +81,7 @@ not work.
|
||||
Start shadowsocks as a daemon with specific pid file.
|
||||
.TP
|
||||
.B \-t \fItimeout\fP
|
||||
Set the socket timeout in secondes. The default value is 10.
|
||||
Set the socket timeout in seconds. The default value is 10.
|
||||
.TP
|
||||
.B \-c \fIconfig_file\fP
|
||||
Use a configuration file.
|
||||
@@ -98,6 +104,12 @@ Enable TCP fast open.
|
||||
.TP
|
||||
.B \--acl \fIacl_config\fP
|
||||
Enable ACL (Access Control List).
|
||||
.TP
|
||||
.B \--manager-address \fIpath_to_unix_domain\fP
|
||||
Enable manager mode.
|
||||
.TP
|
||||
.B \--executable \fIpath_to_server_executable\fP
|
||||
Specify the executable path of ss-server for manager mode.
|
||||
|
||||
.SH EXAMPLE
|
||||
\*(Re requires netfilter's NAT function. Here is an example:
|
||||
@@ -136,6 +148,29 @@ Enable ACL (Access Control List).
|
||||
# Start the shadowsocks-redir
|
||||
root@Wrt:~# ss-redir -u -c /etc/config/shadowsocks.json -f /var/run/shadowsocks.pid
|
||||
|
||||
.SH PROTOCOL
|
||||
\*(Ma provides several APIs through UDP protocol:
|
||||
|
||||
Send UDP commands in the following format to the manager-address provided to \*(Ma.
|
||||
|
||||
command: [JSON data]
|
||||
|
||||
To add a port:
|
||||
|
||||
add: {"server_port": 8001, "password":"7cd308cc059"}
|
||||
|
||||
To remove a port:
|
||||
|
||||
remove: {"server_port": 8001}
|
||||
|
||||
To receive a pong:
|
||||
|
||||
ping
|
||||
|
||||
Then \*(Ma will send back the traffic statistics:
|
||||
|
||||
stat: {"8001":11370}
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR iptables (8),
|
||||
/etc/shadowsocks-libev/config.json
|
||||
|
||||
+121
-24
@@ -47,6 +47,7 @@
|
||||
#include <pthread.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#include <libcork/core.h>
|
||||
@@ -71,6 +72,7 @@
|
||||
|
||||
int verbose = 0;
|
||||
char *executable = "ss-server";
|
||||
char working_dir[128];
|
||||
|
||||
static struct cork_hash_table *server_table;
|
||||
|
||||
@@ -91,9 +93,9 @@ static char *construct_command_line(struct manager_ctx *manager, struct server *
|
||||
|
||||
memset(cmd, 0, BUF_SIZE);
|
||||
snprintf(cmd, BUF_SIZE,
|
||||
"%s -p %s -m %s -k %s --manager-address %s -f %s_%s.pid", executable,
|
||||
"%s -p %s -m %s -k %s --manager-address %s -f %s/.shadowsocks_%s.pid", executable,
|
||||
server->port, manager->method, server->password, manager->manager_address,
|
||||
manager->manager_address, server->port);
|
||||
working_dir, server->port);
|
||||
if (manager->acl != NULL) {
|
||||
int len = strlen(cmd);
|
||||
snprintf(cmd + len, BUF_SIZE - len, " --acl %s", manager->acl);
|
||||
@@ -261,7 +263,7 @@ static void stop_server(char *prefix, char *port)
|
||||
{
|
||||
char path[128];
|
||||
int pid;
|
||||
snprintf(path, 128, "%s_%s.pid", prefix, port);
|
||||
snprintf(path, 128, "%s/.shadowsocks_%s.pid", prefix, port);
|
||||
FILE *f = fopen(path, "r");
|
||||
if (f == NULL) {
|
||||
if (verbose) {
|
||||
@@ -334,7 +336,7 @@ static void manager_recv_cb(EV_P_ ev_io *w, int revents)
|
||||
goto ERROR_MSG;
|
||||
}
|
||||
|
||||
remove_server(manager->manager_address, server->port);
|
||||
remove_server(working_dir, server->port);
|
||||
add_server(manager, server);
|
||||
|
||||
char msg[3] = "ok";
|
||||
@@ -353,7 +355,7 @@ static void manager_recv_cb(EV_P_ ev_io *w, int revents)
|
||||
goto ERROR_MSG;
|
||||
}
|
||||
|
||||
remove_server(manager->manager_address, server->port);
|
||||
remove_server(working_dir, server->port);
|
||||
free(server);
|
||||
|
||||
char msg[3] = "ok";
|
||||
@@ -432,6 +434,84 @@ static void signal_cb(EV_P_ ev_signal *w, int revents)
|
||||
}
|
||||
}
|
||||
|
||||
int create_server_socket(const char *host, const char *port)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result, *rp, *ipv4v6bindall;
|
||||
int s, server_sock;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */
|
||||
hints.ai_socktype = SOCK_DGRAM; /* We want a UDP socket */
|
||||
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* For wildcard IP address */
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
|
||||
s = getaddrinfo(host, port, &hints, &result);
|
||||
if (s != 0) {
|
||||
LOGE("getaddrinfo: %s", gai_strerror(s));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rp = result;
|
||||
|
||||
/*
|
||||
On Linux, with 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, if :: implies dualstack mode.
|
||||
*/
|
||||
if (!host) {
|
||||
ipv4v6bindall = result;
|
||||
|
||||
/* Loop over all address infos found until a IPV6 address is found. */
|
||||
while (ipv4v6bindall) {
|
||||
if (ipv4v6bindall->ai_family == AF_INET6) {
|
||||
rp = ipv4v6bindall; /* Take first IPV6 address available */
|
||||
break;
|
||||
}
|
||||
ipv4v6bindall = ipv4v6bindall->ai_next; /* Get next address info, if any */
|
||||
}
|
||||
}
|
||||
|
||||
for (/*rp = result*/; rp != NULL; rp = rp->ai_next) {
|
||||
server_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (server_sock == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rp->ai_family == AF_INET6) {
|
||||
int ipv6only = host ? 1 : 0;
|
||||
setsockopt(server_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only));
|
||||
}
|
||||
|
||||
int opt = 1;
|
||||
setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
#ifdef SO_NOSIGPIPE
|
||||
set_nosigpipe(server_sock);
|
||||
#endif
|
||||
|
||||
s = bind(server_sock, rp->ai_addr, rp->ai_addrlen);
|
||||
if (s == 0) {
|
||||
/* We managed to bind successfully! */
|
||||
break;
|
||||
} else {
|
||||
ERROR("bind");
|
||||
}
|
||||
|
||||
close(server_sock);
|
||||
}
|
||||
|
||||
if (rp == NULL) {
|
||||
LOGE("cannot bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
return server_sock;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
@@ -634,6 +714,16 @@ int main(int argc, char **argv)
|
||||
run_as(user);
|
||||
}
|
||||
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *homedir = pw->pw_dir;
|
||||
snprintf(working_dir, 128, "%s/.shadowsocks", homedir);
|
||||
|
||||
int err = mkdir(working_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
if (err != 0 && errno != EEXIST) {
|
||||
ERROR("mkdir");
|
||||
FATAL("unable to create working directory");
|
||||
}
|
||||
|
||||
server_table = cork_string_hash_table_new(MAX_PORT_NUM, 0);
|
||||
|
||||
if (conf != NULL) {
|
||||
@@ -645,30 +735,37 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
struct sockaddr_un svaddr;
|
||||
int sfd;
|
||||
ss_addr_t ip_addr = { .host = NULL, .port = NULL };
|
||||
parse_addr(manager_address, &ip_addr);
|
||||
|
||||
sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Create server socket */
|
||||
if (sfd == -1) {
|
||||
FATAL("socket");
|
||||
}
|
||||
if (ip_addr.host == NULL || ip_addr.port == NULL) {
|
||||
struct sockaddr_un svaddr;
|
||||
sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Create server socket */
|
||||
if (sfd == -1) {
|
||||
FATAL("socket");
|
||||
}
|
||||
|
||||
setnonblocking(sfd);
|
||||
setnonblocking(sfd);
|
||||
|
||||
/* Construct well-known address and bind server socket to it */
|
||||
if (remove(manager_address) == -1 && errno != ENOENT) {
|
||||
ERROR("bind");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (remove(manager_address) == -1 && errno != ENOENT) {
|
||||
ERROR("bind");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(&svaddr, 0, sizeof(struct sockaddr_un));
|
||||
svaddr.sun_family = AF_UNIX;
|
||||
strncpy(svaddr.sun_path, manager_address, sizeof(svaddr.sun_path) - 1);
|
||||
|
||||
memset(&svaddr, 0, sizeof(struct sockaddr_un));
|
||||
svaddr.sun_family = AF_UNIX;
|
||||
strncpy(svaddr.sun_path, manager_address, sizeof(svaddr.sun_path) - 1);
|
||||
|
||||
if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1) {
|
||||
ERROR("bind");
|
||||
exit(EXIT_FAILURE);
|
||||
if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1) {
|
||||
ERROR("bind");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
sfd = create_server_socket(ip_addr.host, ip_addr.port);
|
||||
if (sfd == -1) {
|
||||
FATAL("socket");
|
||||
}
|
||||
}
|
||||
|
||||
manager.fd = sfd;
|
||||
@@ -690,7 +787,7 @@ int main(int argc, char **argv)
|
||||
|
||||
while((entry = cork_hash_table_iterator_next(&server_iter)) != NULL) {
|
||||
struct server *server = (struct server*)entry->value;
|
||||
stop_server(manager_address, server->port);
|
||||
stop_server(working_dir, server->port);
|
||||
}
|
||||
|
||||
#ifdef __MINGW32__
|
||||
|
||||
+60
-28
@@ -60,6 +60,7 @@
|
||||
#define SET_INTERFACE
|
||||
#endif
|
||||
|
||||
#include "netutils.h"
|
||||
#include "utils.h"
|
||||
#include "acl.h"
|
||||
#include "server.h"
|
||||
@@ -127,7 +128,7 @@ static struct cork_dllist connections;
|
||||
static void stat_update_cb(EV_P_ ev_timer *watcher, int revents)
|
||||
{
|
||||
struct sockaddr_un svaddr, claddr;
|
||||
int sfd;
|
||||
int sfd = -1;
|
||||
size_t msgLen;
|
||||
char resp[BUF_SIZE];
|
||||
|
||||
@@ -135,38 +136,69 @@ static void stat_update_cb(EV_P_ ev_timer *watcher, int revents)
|
||||
LOGI("update traffic stat: tx: %"PRIu64" rx: %"PRIu64"", tx, rx);
|
||||
}
|
||||
|
||||
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", server_port);
|
||||
|
||||
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_address, sizeof(svaddr.sun_path) - 1);
|
||||
|
||||
snprintf(resp, BUF_SIZE, "stat: {\"%s\":%"PRIu64"}", server_port, tx + rx);
|
||||
msgLen = strlen(resp) + 1;
|
||||
if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *) &svaddr,
|
||||
sizeof(struct sockaddr_un)) != msgLen) {
|
||||
ERROR("stat_sendto");
|
||||
return;
|
||||
|
||||
ss_addr_t ip_addr = { .host = NULL, .port = NULL };
|
||||
parse_addr(manager_address, &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", server_port);
|
||||
|
||||
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_address, 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, 0) == -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);
|
||||
unlink(claddr.sun_path);
|
||||
}
|
||||
|
||||
static void free_connections(struct ev_loop *loop)
|
||||
|
||||
Reference in New Issue
Block a user