Browse Source

Fix #1061

pull/880/merge
Max Lv 8 years ago
parent
commit
3fde843ec8
11 changed files with 51 additions and 230 deletions
  1. 5
      doc/ss-server.asciidoc
  2. 209
      src/acl.c
  3. 2
      src/acl.h
  4. 5
      src/local.c
  5. 15
      src/plugin.c
  6. 1
      src/plugin.h
  7. 5
      src/redir.c
  8. 29
      src/server.c
  9. 1
      src/server.h
  10. 5
      src/tunnel.c
  11. 4
      src/utils.c

5
doc/ss-server.asciidoc

@ -14,7 +14,7 @@ SYNOPSIS
[-t <timeout>] [-c <config_file>] [-i <interface>]
[-a <user_name>] [-d <addr>] [-n <nofile>]
[-b <local_address] [--fast-open] [--mptcp]
[--firewall] [--acl <acl_config>] [--mtu <MTU>]
[--acl <acl_config>] [--mtu <MTU>]
[--manager-address <path_to_unix_domain>]
DESCRIPTION
@ -111,9 +111,6 @@ Enable Multipath TCP.
+
Only available with MPTCP enabled Linux kernel.
--firewall::
Setup firewall rules for auto blocking.
--acl <acl_config>::
Enable ACL (Access Control List) and specify config file.

209
src/acl.c

@ -45,218 +45,15 @@ static struct ip_set outbound_block_list_ipv4;
static struct ip_set outbound_block_list_ipv6;
static struct cork_dllist outbound_block_list_rules;
#ifdef __linux__
#include <unistd.h>
#include <stdio.h>
#define NO_FIREWALL_MODE 0
#define IPTABLES_MODE 1
#define FIREWALLD_MODE 2
static FILE *shell_stdin;
static int mode = NO_FIREWALL_MODE;
static char chain_name[64];
static char *iptables_init_chain =
"iptables -N %s; iptables -F %s; iptables -A OUTPUT -p tcp --tcp-flags RST RST -j %s";
static char *iptables_remove_chain =
"iptables -D OUTPUT -p tcp --tcp-flags RST RST -j %s; iptables -F %s; iptables -X %s";
static char *iptables_add_rule = "iptables -A %s -d %s -j DROP";
static char *iptables_remove_rule = "iptables -D %s -d %s -j DROP";
static char *ip6tables_init_chain =
"ip6tables -N %s; ip6tables -F %s; ip6tables -A OUTPUT -p tcp --tcp-flags RST RST -j %s";
static char *ip6tables_remove_chain =
"ip6tables -D OUTPUT -p tcp --tcp-flags RST RST -j %s; ip6tables -F %s; ip6tables -X %s";
static char *ip6tables_add_rule = "ip6tables -A %s -d %s -j DROP";
static char *ip6tables_remove_rule = "ip6tables -D %s -d %s -j DROP";
static char *firewalld_init_chain =
"firewall-cmd --direct --add-chain ipv4 filter %s; \
firewall-cmd --direct --passthrough ipv4 -F %s; \
firewall-cmd --direct --passthrough ipv4 -A OUTPUT -p tcp --tcp-flags RST RST -j %s";
static char *firewalld_remove_chain =
"firewall-cmd --direct --passthrough ipv4 -D OUTPUT -p tcp --tcp-flags RST RST -j %s; \
firewall-cmd --direct --passthrough ipv4 -F %s; \
firewall-cmd --direct --remove-chain ipv4 filter %s";
static char *firewalld_add_rule = "firewall-cmd --direct --passthrough ipv4 -A %s -d %s -j DROP";
static char *firewalld_remove_rule = "firewall-cmd --direct --passthrough ipv4 -D %s -d %s -j DROP";
static char *firewalld6_init_chain =
"firewall-cmd --direct --add-chain ipv6 filter %s; \
firewall-cmd --direct --passthrough ipv6 -F %s; \
firewall-cmd --direct --passthrough ipv6 -A OUTPUT -p tcp --tcp-flags RST RST -j %s";
static char *firewalld6_remove_chain =
"firewall-cmd --direct --passthrough ipv6 -D OUTPUT -p tcp --tcp-flags RST RST -j %s; \
firewall-cmd --direct --passthrough ipv6 -F %s; \
firewall-cmd --direct --remove-chain ipv6 filter %s";
static char *firewalld6_add_rule = "firewall-cmd --direct --passthrough ipv6 -A %s -d %s -j DROP";
static char *firewalld6_remove_rule = "firewall-cmd --direct --passthrough ipv6 -D %s -d %s -j DROP";
static int
run_cmd(const char *cmd)
{
int ret = 0;
char cmdstring[256];
sprintf(cmdstring, "%s\n", cmd);
size_t len = strlen(cmdstring);
if (shell_stdin != NULL) {
ret = fwrite(cmdstring, 1, len, shell_stdin);
fflush(shell_stdin);
}
return ret == len;
}
static int
init_firewall()
{
int ret = 0;
char cli[256];
FILE *fp;
if (getuid() != 0)
return -1;
sprintf(cli, "firewall-cmd --version 2>&1");
fp = popen(cli, "r");
if (fp == NULL)
return -1;
if (pclose(fp) == 0) {
mode = FIREWALLD_MODE;
} else {
/* Check whether we have permission to operate iptables.
* Note that checking `iptables --version` is insufficient:
* eg, running within a child user namespace.
*/
sprintf(cli, "iptables -L 2>&1");
fp = popen(cli, "r");
if (fp == NULL)
return -1;
if (pclose(fp) == 0)
mode = IPTABLES_MODE;
}
sprintf(chain_name, "SHADOWSOCKS_LIBEV_%d", getpid());
if (mode == FIREWALLD_MODE) {
sprintf(cli, firewalld6_init_chain, chain_name, chain_name, chain_name);
ret |= system(cli);
sprintf(cli, firewalld_init_chain, chain_name, chain_name, chain_name);
ret |= system(cli);
} else if (mode == IPTABLES_MODE) {
sprintf(cli, ip6tables_init_chain, chain_name, chain_name, chain_name);
ret |= system(cli);
sprintf(cli, iptables_init_chain, chain_name, chain_name, chain_name);
ret |= system(cli);
}
shell_stdin = popen("/bin/sh", "w");
return ret;
}
static int
reset_firewall()
{
int ret = 0;
char cli[256];
if (getuid() != 0)
return -1;
if (mode == IPTABLES_MODE) {
sprintf(cli, ip6tables_remove_chain, chain_name, chain_name, chain_name);
ret |= system(cli);
sprintf(cli, iptables_remove_chain, chain_name, chain_name, chain_name);
ret |= system(cli);
} else if (mode == FIREWALLD_MODE) {
sprintf(cli, firewalld6_remove_chain, chain_name, chain_name, chain_name);
ret |= system(cli);
sprintf(cli, firewalld_remove_chain, chain_name, chain_name, chain_name);
ret |= system(cli);
}
if (shell_stdin != NULL) {
run_cmd("exit 0");
pclose(shell_stdin);
}
return ret;
}
static int
set_firewall_rule(char *addr, int add)
{
char cli[256];
struct cork_ip ip;
if (getuid() != 0)
return -1;
if (cork_ip_init(&ip, addr))
return -1;
if (add) {
if (mode == IPTABLES_MODE)
sprintf(cli, ip.version == 4 ? iptables_add_rule : ip6tables_add_rule,
chain_name, addr);
else if (mode == FIREWALLD_MODE)
sprintf(cli, ip.version == 4 ? firewalld_add_rule : firewalld6_add_rule,
chain_name, addr);
return run_cmd(cli);
} else {
if (mode == IPTABLES_MODE)
sprintf(cli, ip.version == 4 ? iptables_remove_rule : ip6tables_remove_rule,
chain_name, addr);
else if (mode == FIREWALLD_MODE)
sprintf(cli, ip.version == 4 ? firewalld_remove_rule : firewalld6_remove_rule,
chain_name, addr);
return run_cmd(cli);
}
return 0;
}
static void
free_firewall_rule(void *key, void *element)
{
if (key == NULL)
return;
char *addr = (char *)key;
set_firewall_rule(addr, 0);
ss_free(element);
}
#endif
void
init_block_list(int firewall)
init_block_list()
{
// Initialize cache
#ifdef __linux__
if (firewall)
init_firewall();
else
mode = NO_FIREWALL_MODE;
cache_create(&block_list, 256, free_firewall_rule);
#else
cache_create(&block_list, 256, NULL);
#endif
}
void
free_block_list()
{
#ifdef __linux__
if (mode != NO_FIREWALL_MODE)
reset_firewall();
#endif
cache_clear(block_list, 0); // Remove all items
}
@ -306,10 +103,6 @@ update_block_list(char *addr, int err_level)
int *count = (int *)ss_malloc(sizeof(int));
*count = 1;
cache_insert(block_list, addr, addr_len, count);
#ifdef __linux__
if (mode != NO_FIREWALL_MODE)
set_firewall_rule(addr, 1);
#endif
}
return 0;

2
src/acl.h

@ -42,7 +42,7 @@ int acl_remove_ip(const char *ip);
int get_acl_mode(void);
void init_block_list(int firewall);
void init_block_list();
void free_block_list();
int check_block_list(char *addr);
int update_block_list(char *addr, int err_level);

5
src/local.c

@ -1132,7 +1132,10 @@ signal_cb(EV_P_ ev_signal *w, int revents)
switch (w->signum) {
#ifndef __MINGW32__
case SIGCHLD:
LOGE("plugin service exit unexpectedly");
if (!is_plugin_running())
LOGE("plugin service exit unexpectedly");
else
return;
case SIGUSR1:
#endif
case SIGINT:

15
src/plugin.c

@ -29,6 +29,7 @@
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <libcork/core.h>
@ -179,6 +180,14 @@ stop_plugin()
}
}
int is_plugin_running()
{
if (sub != NULL) {
return cork_subprocess_is_finished(sub);
}
return 0;
}
#else
#include "stdint.h"
@ -210,4 +219,10 @@ stop_plugin()
FATAL("Plugin is not supported on MinGW.");
}
int
is_plugin_running()
{
FATAL("Plugin is not supported on MinGW.");
}
#endif

1
src/plugin.h

@ -34,5 +34,6 @@ int start_plugin(const char *plugin,
const char *local_port);
uint16_t get_local_port();
void stop_plugin();
int is_plugin_running();
#endif // _PLUGIN_H

5
src/redir.c

@ -759,7 +759,10 @@ signal_cb(EV_P_ ev_signal *w, int revents)
if (revents & EV_SIGNAL) {
switch (w->signum) {
case SIGCHLD:
LOGE("plugin service exit unexpectedly");
if (!is_plugin_running())
LOGE("plugin service exit unexpectedly");
else
return;
case SIGINT:
case SIGTERM:
ev_signal_stop(EV_DEFAULT, &sigint_watcher);

29
src/server.c

@ -80,6 +80,10 @@
#define SSMAXCONN 1024
#endif
#ifndef MAX_FRAG
#define MAX_FRAG 2
#endif
static void signal_cb(EV_P_ ev_signal *w, int revents);
static void accept_cb(EV_P_ ev_io *w, int revents);
static void server_send_cb(EV_P_ ev_io *w, int revents);
@ -655,6 +659,13 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
tx += r;
if (server->frag >= MAX_FRAG) {
LOGE("fragmentation detected");
close_and_free_remote(EV_A_ remote);
close_and_free_server(EV_A_ server);
return;
}
if (server->stage == STAGE_ERROR) {
server->buf->len = 0;
server->buf->idx = 0;
@ -667,6 +678,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
if (buf->len <= enc_get_iv_len() + 1) {
// wait for more
server->frag++;
return;
}
} else {
@ -719,6 +731,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
} else {
if (ret == -1)
server->stage = STAGE_ERROR;
server->frag++;
server->buf->len = 0;
server->buf->idx = 0;
return;
@ -1397,6 +1410,7 @@ new_server(int fd, listen_ctx_t *listener)
server->send_ctx->server = server;
server->send_ctx->connected = 0;
server->stage = STAGE_INIT;
server->frag = 0;
server->query = NULL;
server->listen_ctx = listener;
server->remote = NULL;
@ -1492,7 +1506,10 @@ signal_cb(EV_P_ ev_signal *w, int revents)
if (revents & EV_SIGNAL) {
switch (w->signum) {
case SIGCHLD:
LOGE("plugin service exit unexpectedly");
if (!is_plugin_running())
LOGE("plugin service exit unexpectedly");
else
return;
case SIGINT:
case SIGTERM:
ev_signal_stop(EV_DEFAULT, &sigint_watcher);
@ -1559,7 +1576,6 @@ main(int argc, char **argv)
int i, c;
int pid_flags = 0;
int mptcp = 0;
int firewall = 0;
int mtu = 0;
char *user = NULL;
char *password = NULL;
@ -1588,7 +1604,6 @@ main(int argc, char **argv)
{ "plugin", required_argument, 0, 0 },
#ifdef __linux__
{ "mptcp", no_argument, 0, 0 },
{ "firewall", no_argument, 0, 0 },
#endif
{ 0, 0, 0, 0 }
};
@ -1619,9 +1634,6 @@ main(int argc, char **argv)
} else if (option_index == 6) {
mptcp = 1;
LOGI("enable multipath TCP");
} else if (option_index == 8) {
firewall = 1;
LOGI("enable firewall rules");
}
break;
case 's':
@ -1953,14 +1965,11 @@ main(int argc, char **argv)
#ifndef __MINGW32__
if (geteuid() == 0) {
LOGI("running from root user");
} else if (firewall) {
LOGE("firewall setup requires running from root user");
exit(-1);
}
#endif
// init block list
init_block_list(firewall);
init_block_list();
// Init connections
cork_dllist_init(&connections);

1
src/server.h

@ -53,6 +53,7 @@ typedef struct server {
int fd;
int stage;
int auth;
int frag;
buffer_t *buf;
buffer_t *header_buf;

5
src/tunnel.c

@ -723,7 +723,10 @@ signal_cb(EV_P_ ev_signal *w, int revents)
if (revents & EV_SIGNAL) {
switch (w->signum) {
case SIGCHLD:
LOGE("plugin service exit unexpectedly");
if (!is_plugin_running())
LOGE("plugin service exit unexpectedly");
else
return;
case SIGINT:
case SIGTERM:
ev_signal_stop(EV_DEFAULT, &sigint_watcher);

4
src/utils.c

@ -356,10 +356,6 @@ usage()
#ifdef __linux__
printf(
" [--mptcp] Enable Multipath TCP on MPTCP Kernel.\n");
#ifdef MODULE_REMOTE
printf(
" [--firewall] Setup firewall rules for auto blocking.\n");
#endif
#endif
printf(
" [--plugin <plugin_args>] Enable SIP003 plugin. (Experimental)\n");

Loading…
Cancel
Save