mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-25 10:13:44 +00:00
Fix #1061
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+4
-1
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
-1
@@ -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);
|
||||
|
||||
+19
-10
@@ -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);
|
||||
|
||||
@@ -53,6 +53,7 @@ typedef struct server {
|
||||
int fd;
|
||||
int stage;
|
||||
int auth;
|
||||
int frag;
|
||||
|
||||
buffer_t *buf;
|
||||
buffer_t *header_buf;
|
||||
|
||||
+4
-1
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user