|
|
@ -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); |
|
|
|
|
|
|
|
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"); |
|
|
|
} |
|
|
|
|
|
|
|
sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Create server socket */ |
|
|
|
if (sfd == -1) { |
|
|
|
FATAL("socket"); |
|
|
|
} |
|
|
|
|
|
|
|
setnonblocking(sfd); |
|
|
|
|
|
|
|
/* Construct well-known address and bind server socket to it */ |
|
|
|
setnonblocking(sfd); |
|
|
|
|
|
|
|
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__ |
|
|
|