shadowsocks-libev: rewrite
authorYousong Zhou <yszhou4tech@gmail.com>
Sat, 24 Jun 2017 00:56:18 +0000 (08:56 +0800)
committerYousong Zhou <yszhou4tech@gmail.com>
Sun, 2 Jul 2017 02:01:53 +0000 (10:01 +0800)
- Selecting only a single or subset of all components of shadowsocks-libev is
  now possible (this is the main motivation behind the rewrite)
- Configuring multiple instances of the same component is now also possible
- Same option names as with the json config
- Unified configuration generation method for each component
- Add support for ss-local, ss-tunnel, ss-server
- Most data validation is now done with validate_data
- USE_PROCD=1
- Update ss-rules with the one from shadowsocks/luci-app-shadowsocks
- Add README.md
- Set myself as the maintainer

Addresses #4435

Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
net/shadowsocks-libev/Makefile
net/shadowsocks-libev/README.md [new file with mode: 0644]
net/shadowsocks-libev/files/firewall.include [deleted file]
net/shadowsocks-libev/files/firewall.ss-rules [new file with mode: 0644]
net/shadowsocks-libev/files/shadowsocks-libev.config
net/shadowsocks-libev/files/shadowsocks-libev.init
net/shadowsocks-libev/files/ss-rules
net/shadowsocks-libev/files/ss-rules.defaults [new file with mode: 0755]
net/shadowsocks-libev/patches/0001-decouple-use_syslog-from-pid_flags.patch [new file with mode: 0644]

index 8b5123245c2f9f878d402deef3ae742310c29641..31d5d74ffa3403f767161130928fbacab0023117 100644 (file)
@@ -1,5 +1,4 @@
 #
-# Copyright (C) 2015 OpenWrt.org
 # Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
 #
 # This is free software, licensed under the GNU General Public License v2.
 
 include $(TOPDIR)/rules.mk
 
+# Checklist when bumping versions
+#
+# - update cipher list by checking src/crypto.c:crypto_init()
+# - check if default mode has changed from TCP_ONLY
+# - check if ss-rules has been upstreamed
+#
+# TODO
+#
+# - add validate type: user
+#
 PKG_NAME:=shadowsocks-libev
 PKG_VERSION:=3.0.6
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://github.com/shadowsocks/shadowsocks-libev/releases/download/v$(PKG_VERSION)
 PKG_HASH:=7d9b43b0235a57c115bfe160efd54abef96bffcbfff61c5496e7c2800f0734ca
 
-PKG_MAINTAINER:=Jian Chang <aa65535@live.com>
-PKG_LICENSE:=GPLv2
+PKG_MAINTAINER:=Yousong Zhou <yszhou4tech@gmail.com>
+
+PKG_LICENSE:=GPL-3.0+
 PKG_LICENSE_FILES:=LICENSE
 
-PKG_INSTALL:=1
 PKG_FIXUP:=autoreconf
+PKG_INSTALL:=1
 PKG_USE_MIPS16:=0
 PKG_BUILD_PARALLEL:=1
 
 include $(INCLUDE_DIR)/package.mk
 
-define Package/shadowsocks-libev
-       SECTION:=net
-       CATEGORY:=Network
-       TITLE:=Lightweight Secured Socks5 Proxy
-       URL:=https://github.com/shadowsocks/shadowsocks-libev
-       DEPENDS:=+libev +libmbedtls +libpthread +libsodium +libudns \
-               +ipset +ip +iptables-mod-tproxy +libpcre +zlib
+
+define Package/shadowsocks-libev-config
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=Web Servers/Proxies
+  TITLE:=shadowsocks-libev config scripts
+  URL:=https://github.com/shadowsocks/shadowsocks-libev
 endef
 
-define Package/shadowsocks-libev/description
-Shadowsocks-libev is a lightweight secured socks5 proxy for embedded devices and low end boxes.
+define Package/shadowsocks-libev-config/install
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_DATA) ./files/shadowsocks-libev.config $(1)/etc/config/shadowsocks-libev
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/shadowsocks-libev.init $(1)/etc/init.d/shadowsocks-libev
+endef
+
+
+define Package/shadowsocks-libev/Default
+  define Package/shadowsocks-libev-$(1)
+    SECTION:=net
+    CATEGORY:=Network
+    SUBMENU:=Web Servers/Proxies
+    TITLE:=shadowsocks-libev $(1)
+    URL:=https://github.com/shadowsocks/shadowsocks-libev
+    DEPENDS:=+libev +libmbedtls +libpcre +libpthread +libsodium +libudns +shadowsocks-libev-config +zlib
+  endef
+
+  define Package/shadowsocks-libev-$(1)/install
+       $$(INSTALL_DIR) $$(1)/usr/bin
+       $$(INSTALL_BIN) $$(PKG_INSTALL_DIR)/usr/bin/$(1) $$(1)/usr/bin
+  endef
+
 endef
 
-define Package/shadowsocks-libev/conffiles
-/etc/config/shadowsocks-libev
+SHADOWSOCKS_COMPONENTS:=ss-local ss-redir ss-tunnel ss-server
+define shadowsocks-libev/templates
+  $(foreach component,$(SHADOWSOCKS_COMPONENTS),
+    $(call Package/shadowsocks-libev/Default,$(component))
+  )
 endef
+$(eval $(call shadowsocks-libev/templates))
 
-define Package/shadowsocks-libev/postinst
-#!/bin/sh
-uci -q batch <<-EOF >/dev/null
-       delete firewall.shadowsocks_libev
-       set firewall.shadowsocks_libev=include
-       set firewall.shadowsocks_libev.type=script
-       set firewall.shadowsocks_libev.path=/usr/share/shadowsocks-libev/firewall.include
-       set firewall.shadowsocks_libev.reload=1
-       commit firewall
-EOF
-exit 0
+
+define Package/shadowsocks-libev-ss-rules
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=Web Servers/Proxies
+  TITLE:=shadowsocks-libev ss-rules
+  URL:=https://github.com/shadowsocks/shadowsocks-libev
+  DEPENDS:=+ip +ipset +iptables-mod-tproxy +shadowsocks-libev-ss-redir +shadowsocks-libev-config
 endef
 
-define Package/shadowsocks-libev/install
+define Package/shadowsocks-libev-ss-rules/install
        $(INSTALL_DIR) $(1)/usr/bin
-       $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/ss-{redir,tunnel} $(1)/usr/bin
        $(INSTALL_BIN) ./files/ss-rules $(1)/usr/bin
-       $(INSTALL_DIR) $(1)/etc/config
-       $(INSTALL_DATA) ./files/shadowsocks-libev.config $(1)/etc/config/shadowsocks-libev
-       $(INSTALL_DIR) $(1)/etc/init.d
-       $(INSTALL_BIN) ./files/shadowsocks-libev.init $(1)/etc/init.d/shadowsocks-libev
-       $(INSTALL_DIR) $(1)/usr/share/shadowsocks-libev
-       $(INSTALL_DATA) ./files/firewall.include $(1)/usr/share/shadowsocks-libev/firewall.include
+       $(INSTALL_DIR) $(1)/etc/uci-defaults
+       $(INSTALL_DATA) ./files/firewall.ss-rules $(1)/etc
+       $(INSTALL_BIN) ./files/ss-rules.defaults $(1)/etc/uci-defaults
+endef
+
+define Package/shadowsocks-libev-ss-rules/prerm
+#!/bin/sh
+s=firewall.ss_rules
+uci get "$$s" >/dev/null || exit 0
+uci batch <<-EOF
+       delete $$s
+       commit firewall
+EOF
 endef
 
-CONFIGURE_ARGS += --disable-documentation
 
-$(eval $(call BuildPackage,shadowsocks-libev))
+$(eval $(call BuildPackage,shadowsocks-libev-config))
+$(eval $(call BuildPackage,shadowsocks-libev-ss-rules))
+$(foreach component,$(SHADOWSOCKS_COMPONENTS), \
+  $(eval $(call BuildPackage,shadowsocks-libev-$(component))) \
+)
diff --git a/net/shadowsocks-libev/README.md b/net/shadowsocks-libev/README.md
new file mode 100644 (file)
index 0000000..75790ea
--- /dev/null
@@ -0,0 +1,86 @@
+## components
+
+`ss-local` provides SOCKS5 proxy.
+
+        socks5                                     ss              plain
+       --------> tcp:udp:local_address:local_port ----> ss server -------> dest
+
+`ss-redir`.  The REDIRECT and TPROXY part are to be provided by `ss-rules` script.  REDIRECT only works for tcp traffic (see also darkk/redsocks).  TPROXY is used to proxy udp messages, but it's only available in the PREROUTING chain and as such cannot proxy local out traffic.
+
+         plain             plain                                 ss              plain
+       ---------> REDIRECT ------> tcp:local_address:local_port ----> ss server -----> original dest
+
+         plain            plain                                 ss              plain
+       ---------> TPROXY -------> udp:local_address:local_port -----> ss server -----> original dest
+
+`ss-tunnel` provides ssh `-L` local-forwarding-like tunnel.  Typically it's used to tunnel DNS traffic to the remote.
+
+         plain                                       ss               plain
+       ---------> tcp|udp:local_address:local_port ------> ss server -------> tunnel_address
+
+`ss-server`, the "ss server" in the above diagram
+
+## uci
+
+Option names are the same as those used in json config files.  Check `validate_xxx` func definition of the [service script](files/shadowsocks-libev.init) and shadowsocks-libev's own documentation for supported options and expected value types.  A [sample config file](files/shadowsocks-libev.config) is also provided for reference.
+
+Every section have a `disabled` option to temporarily turn off the component instance or component instances referring to it.
+
+Section type `server` is for definition of remote shadowsocks servers.  They will be referred to from other component sections and as such should be named (as compared to anonymous section).
+
+Section type `ss_local`, `ss_redir`, `ss_tunnel` are for specification of shadowsocks-libev components.  They share mostly a common set of options like `local_port`, `verbose`, `fast_open`, `timeout`, etc.
+
+We can have multiple instances of component and `server` sections.  The relationship between them is many-to-one.  This will have the following implications
+
+ - It's possible to have both `ss_local` and `ss_redir` referring to the same `server` definition
+ - It's possible to have multiple instances of `ss_redir` listening on the same address:port with `reuse_port` enabled referring to the same or different `server` sections
+
+`ss_rules` section is for configuring the behaviour of `ss-rules` script.  There can only exist at most one such section with the name also being `ss_rules`
+
+       redir_tcp               name of ss_redir section with mode tcp_only or tcp_and_udp
+       redir_udp               name of ss_redir section with mode udp_only or tcp_and_udp
+
+       --- incoming packets having source address in
+
+       src_ips_bypass          will bypass the redir chain
+       src_ips_forward         will always go through the redir chain
+       src_ips_checkdst        will continue to have their destination addresses checked
+
+       --- otherwise, the default action can be specified with
+
+       src_default             bypass, forward, [checkdst]
+
+       --- for local out tcp packets, the default action can be specified with
+
+       local_default           [bypass], forward, checkdst
+
+       --- if the previous check result is checkdst,
+       --- then packets having destination address in
+
+       dst_ips_bypass_file
+       dst_ips_bypass          will bypass the redir chain
+       dst_ips_forward_file
+       dst_ips_forward         will go through the redir chain
+
+## notes and faq
+
+Useful paths and commands for debugging
+
+       # check current running status
+       ubus call service list '{"name": "shadowsocks-libev"}'
+       ubus call service list '{"name": "shadowsocks-libev", "verbose": true}'
+
+       # dump validate definition
+       ubus call service validate '{"package": "shadowsocks-libev"}'
+       ubus call service validate '{"package": "shadowsocks-libev"}' \
+               | jsonfilter -e '$["shadowsocks-libev"]["ss_tunnel"]'
+
+       # check json config
+       ls -l /var/etc/shadowsocks-libev/
+
+       # set uci config option verbose to 1, restart the service and follow the log
+       logread -f
+
+ss-redir needs to open a new socket and setsockopt IP_TRANSPARENT when sending udp reply to client.  This requires `CAP_NET_ADMIN` and as such the process cannot run as `nobody`
+
+ss-local, ss-redir, etc. supports specifying an array of remote ss server, but supporting this in uci seems to be overkill.  The workaround can be defining multiple `server` sections and multiple `ss-redir` instances with `reuse_port` enabled
diff --git a/net/shadowsocks-libev/files/firewall.include b/net/shadowsocks-libev/files/firewall.include
deleted file mode 100644 (file)
index 3a00e80..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-if pidof ss-redir>/dev/null; then
-       /etc/init.d/shadowsocks-libev rules
-       logger -t ShadowSocks-libev "Reloading ShadowSocks-libev due to restart of firewall"
-fi
diff --git a/net/shadowsocks-libev/files/firewall.ss-rules b/net/shadowsocks-libev/files/firewall.ss-rules
new file mode 100644 (file)
index 0000000..3a1d32c
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+/etc/init.d/shadowsocks-libev reload
index 95aec7b2ec441fcb7d08857e63b6bc4603a87270..9b3fe0852b5957a4528559fd7f0c4f84a53080f2 100644 (file)
@@ -1,15 +1,60 @@
-
-config shadowsocks-libev
-       option enable '1'
-       option server '127.0.0.1'
-       option server_port '8388'
+config ss_local
+       option disabled 1
+       option server 'sss0'
+       option local_address '0.0.0.0'
        option local_port '1080'
-       option password 'barfoo!'
+       option timeout '30'
+
+config ss_tunnel
+       option disabled 1
+       option server 'sss0'
+       option local_address '0.0.0.0'
+       option local_port '1090'
+       option tunnel_address 'example.com:80'
+       option mode 'tcp_and_udp'
        option timeout '60'
-       option encrypt_method 'rc4-md5'
-       option ignore_list '/dev/null'
-       option udp_mode '0'
-       option tunnel_enable '1'
-       option tunnel_port '5300'
-       option tunnel_forward '8.8.4.4:53'
-       option lan_ac_mode '0'
+
+config ss_redir hi
+       option disabled 1
+       option server 'sss0'
+       option local_address '0.0.0.0'
+       option local_port '1100'
+       option mode 'tcp_and_udp'
+       option timeout '60'
+       option fast_open 1
+       option verbose 1
+       option reuse_port 1
+
+config ss_redir hj
+       option disabled 1
+       option server 'sss0'
+       option local_address '0.0.0.0'
+       option local_port '1100'
+       option mode 'tcp_and_udp'
+       option timeout '60'
+       option fast_open 1
+       option verbose 1
+       option reuse_port 1
+
+config ss_rules 'ss_rules'
+       option disabled 1
+       option redir_tcp 'hi'
+       option redir_udp 'hi'
+       option src_default 'bypass'
+       option local_default 'checkdst'
+       list src_ips_forward '192.168.1.4'
+       list dst_ips_forward '8.8.8.8'
+
+config server 'sss0'
+       option disabled 1
+       option server '192.168.1.3'
+       option server_port '9001'
+       option password '********'
+       option method 'aes-256-cfb'
+
+config ss_server
+       option disabled 1
+       option server_port '9001'
+       option password '********'
+       option method 'aes-256-cfb'
+       option bind_address '192.168.7.72'
index 9a64038a722a140ff3547a9e26ea278c95af3c99..68ec93b035d5f5c1a616290a58d670367a42fb53 100644 (file)
 #!/bin/sh /etc/rc.common
+#
+# Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
+#
+# This is free software, licensed under the GNU General Public License v3.
+# See /LICENSE for more information.
+#
 
-START=90
-STOP=15
-
-SERVICE_USE_PID=1
-SERVICE_WRITE_PID=1
-SERVICE_DAEMONIZE=1
-EXTRA_COMMANDS="rules"
-CONFIG_FILE=/var/etc/shadowsocks-libev.json
-
-get_config() {
-       config_get_bool enable $1 enable
-       config_get server $1 server
-       config_get server_port $1 server_port
-       config_get local_port $1 local_port
-       config_get timeout $1 timeout
-       config_get password $1 password
-       config_get encrypt_method $1 encrypt_method
-       config_get ignore_list $1 ignore_list
-       config_get udp_mode $1 udp_mode
-       config_get udp_server $1 udp_server
-       config_get udp_server_port $1 udp_server_port
-       config_get udp_local_port $1 udp_local_port
-       config_get udp_timeout $1 udp_timeout
-       config_get udp_password $1 udp_password
-       config_get udp_encrypt_method $1 udp_encrypt_method
-       config_get_bool tunnel_enable $1 tunnel_enable
-       config_get tunnel_port $1 tunnel_port
-       config_get tunnel_forward $1 tunnel_forward
-       config_get lan_ac_mode $1 lan_ac_mode
-       config_get lan_ac_ip $1 lan_ac_ip
-       config_get wan_bp_ip $1 wan_bp_ip
-       config_get wan_fw_ip $1 wan_fw_ip
-       config_get ipt_ext $1 ipt_ext
-       : ${timeout:=60}
-       : ${udp_timeout:=60}
-       : ${tunnel_port:=5300}
-       : ${tunnel_forward:=8.8.4.4:53}
-}
-
-start_rules() {
-       local ac_args
-
-       if [ -n "$lan_ac_ip" ]; then
-               case $lan_ac_mode in
-                       1) ac_args="w$lan_ac_ip"
-                       ;;
-                       2) ac_args="b$lan_ac_ip"
-                       ;;
-               esac
+USE_PROCD=1
+START=99
+
+ss_confdir=/var/etc/shadowsocks-libev
+ss_bindir=/usr/bin
+q='"'
+
+ss_mkjson() {
+       echo "{" >"$confjson"
+       if ss_mkjson_ "$@" >>$confjson; then
+               sed -i -e '/^\s*$/d' -e '2,$s/^/\t/' -e '$s/,$//' "$confjson"
+               echo "}" >>"$confjson"
+       else
+               rm -f "$confjson"
+               return 1
        fi
-       /usr/bin/ss-rules \
-               -s "$server" \
-               -l "$local_port" \
-               -S "$udp_server" \
-               -L "$udp_local_port" \
-               -i "$ignore_list" \
-               -a "$ac_args" \
-               -b "$wan_bp_ip" \
-               -w "$wan_fw_ip" \
-               -e "$ipt_ext" \
-               -o $udp
-       return $?
-}
-
-start_redir() {
-       cat <<-EOF >$CONFIG_FILE
-               {
-                   "server": "$server",
-                   "server_port": $server_port,
-                   "local_address": "0.0.0.0",
-                   "local_port": $local_port,
-                   "password": "$password",
-                   "timeout": $timeout,
-                   "method": "$encrypt_method"
-               }
-EOF
-       if [ "$udp_mode" = 2 ]; then
-               /usr/bin/ss-redir \
-                       -c $CONFIG_FILE \
-                       -f /var/run/ss-redir_t.pid
-               cat <<-EOF >$CONFIG_FILE
-                       {
-                           "server": "$udp_server",
-                           "server_port": $udp_server_port,
-                           "local_address": "0.0.0.0",
-                           "local_port": $udp_local_port,
-                           "password": "$udp_password",
-                           "timeout": $udp_timeout,
-                           "method": "$udp_encrypt_method"
-                       }
-EOF
+}
+
+ss_mkjson_() {
+       local func
+
+       for func in "$@"; do
+               if ! "$func"; then
+                       return 1
+               fi
+       done
+}
+
+ss_mkjson_server_conf() {
+       local cfgserver
+
+       config_get cfgserver "$cfg" server
+       [ -n "$cfgserver" ] || return 1
+       eval "$(validate_server_section "$cfg" ss_validate_mklocal)"
+       validate_server_section "$cfgserver" || return 1
+       [ "$disabled" = 0 ] || return 1
+       ss_mkjson_server_conf_ "$cfgserver"
+}
+
+ss_mkjson_server_conf_() {
+       [ -n "$server_port" ] || return 1
+       cat <<-EOF
+               ${server:+${q}server${q}: ${q}$server${q},}
+               "server_port": $server_port,
+               ${method:+${q}method${q}: ${q}$method${q},}
+               ${key:+${q}key${q}: ${q}$key${q},}
+               ${password:+${q}password${q}: ${q}$password${q},}
+       EOF
+}
+
+ss_mkjson_common_conf() {
+       [ "$fast_open" = 0 ] && fast_open=false || fast_open=true
+       [ "$reuse_port" = 0 ] && reuse_port=false || reuse_port=true
+       cat <<-EOF
+               "use_syslog": true,
+               "fast_open": $fast_open,
+               "reuse_port": $reuse_port,
+               ${local_address:+${q}local_address${q}: ${q}$local_address${q},}
+               ${local_port:+${q}local_port${q}: $local_port,}
+               ${mode:+${q}mode${q}: ${q}$mode${q},}
+               ${mtu:+${q}mtu${q}: $mtu,}
+               ${timeout:+${q}timeout${q}: $timeout,}
+               ${user:+${q}user${q}: ${q}$user${q},}
+       EOF
+}
+
+ss_mkjson_ss_local_conf() {
+       ss_mkjson_server_conf
+}
+
+ss_mkjson_ss_redir_conf() {
+       ss_mkjson_server_conf
+}
+
+ss_mkjson_ss_server_conf() {
+       ss_mkjson_server_conf_
+}
+
+ss_mkjson_ss_tunnel_conf() {
+       ss_mkjson_server_conf || return 1
+       [ -n "$tunnel_address" ] || return 1
+       cat <<-EOF
+               ${tunnel_address:+${q}tunnel_address${q}: ${q}$tunnel_address${q},}
+       EOF
+}
+
+ss_xxx() {
+       local cfg="$1"
+       local cfgtype="$2"
+       local bin="$ss_bindir/${cfgtype/_/-}"
+       local confjson="$ss_confdir/$cfgtype.$cfg.json"
+
+       [ -x "$bin" ] || return
+       eval "$("validate_${cfgtype}_section" "$cfg" ss_validate_mklocal)"
+       "validate_${cfgtype}_section" "$cfg"
+       [ "$disabled" = 0 ] || return
+
+       if ss_mkjson \
+                       ss_mkjson_common_conf \
+                       ss_mkjson_${cfgtype}_conf \
+                       ; then
+               procd_open_instance "$cfgtype.$cfg"
+               procd_set_param command "$bin" -c "$confjson"
+               [ "$verbose" = 0 ] || procd_append_param command -v
+               [ -z "$bind_address" ] || procd_append_param command -b "$bind_address"
+               [ -z "$manager_address" ] || procd_append_param command --manager-address "$manager_address"
+               procd_set_param file "$confjson"
+               procd_set_param respawn
+               procd_close_instance
+               ss_rules_cb "$cfg"
        fi
-       /usr/bin/ss-redir \
-               -c $CONFIG_FILE \
-               -f /var/run/ss-redir.pid \
-               $udp
-       return $?
 }
 
-start_tunnel() {
-       : ${udp:="-u"}
-       /usr/bin/ss-tunnel \
-               -c $CONFIG_FILE \
-               -l $tunnel_port \
-               -L $tunnel_forward \
-               -f /var/run/ss-tunnel.pid \
-               $udp
-       return $?
+ss_rules_cb() {
+       local cfgserver
+       local server
+
+       [ "$cfgtype" != ss_server ] || return
+       config_get cfgserver "$cfg" server
+       config_get server "$cfgserver" server
+
+       ss_rules_servers="$ss_rules_servers $server"
+       if [ "$cfgtype" = ss_redir ]; then
+               if [ "$mode" = tcp_only -o "$mode" = "tcp_and_udp" ]; then
+                       eval "ss_rules_redir_tcp_$cfg=$local_port"
+               fi
+               if [ "$mode" = udp_only -o "$mode" = "tcp_and_udp" ]; then
+                       eval "ss_rules_redir_udp_$cfg=$local_port"
+                       eval "ss_rules_redir_server_udp_$cfg=$server"
+               fi
+       fi
 }
 
-rules() {
-       config_load shadowsocks-libev
-       config_foreach get_config shadowsocks-libev
-       [ "$enable" = 1 ] || exit 0
-       mkdir -p /var/run /var/etc
-
-       : ${server:?}
-       : ${server_port:?}
-       : ${local_port:?}
-       : ${password:?}
-       : ${encrypt_method:?}
-       case $udp_mode in
-               1) udp="-u"
-               ;;
-               2)
-                       udp="-U"
-                       : ${udp_server:?}
-                       : ${udp_server_port:?}
-                       : ${udp_local_port:?}
-                       : ${udp_password:?}
-                       : ${udp_encrypt_method:?}
-               ;;
+ss_rules() {
+       local cfg="ss_rules"
+       local bin="$ss_bindir/ss-rules"
+       local cfgtype
+       local args local_port_tcp local_port_udp server_udp
+       local i a_args d_args
+
+       [ -x "$bin" ] || return 1
+       config_get cfgtype "$cfg" TYPE
+       [ "$cfgtype" = ss_rules ] || return 1
+
+       eval "$(validate_ss_rules_section "$cfg" ss_validate_mklocal)"
+       validate_ss_rules_section "$cfg"
+       [ "$disabled" = 0 ] || return 1
+
+       eval local_port_tcp="\$ss_rules_redir_tcp_$redir_tcp"
+       eval local_port_udp="\$ss_rules_redir_udp_$redir_udp"
+       eval server_udp="\$ss_rules_redir_server_udp_$redir_udp"
+       [ -z "$local_port_udp" ] || args="$args -U"
+       case "$local_default" in
+               forward) args="$args -O" ;;
+               checkdst) args="$args -o" ;;
+       esac
+       case "$src_default" in
+               bypass) d_args=RETURN ;;
+               forward) d_args=SS_SPEC_WAN_FW ;;
+               checkdst) d_args=SS_SPEC_WAN_AC ;;
        esac
+       ss_rules_servers="$(echo "$ss_rules_servers" | tr ' ' '\n' | sort -u)"
+       for i in $src_ips_bypass; do a_args="b,$i $a_args"; done
+       for i in $src_ips_forward; do a_args="g,$i $a_args"; done
+       for i in $src_ips_checkdst; do a_args="n,$i $a_args"; done
+
+       "$bin" \
+                       -s "$ss_rules_servers" \
+                       -l "$local_port_tcp" \
+                       -S "$server_udp" \
+                       -L "$local_port_udp" \
+                       -B "$dst_ips_bypass_file" \
+                       -W "$dst_ips_forward_file" \
+                       -b "$dst_ips_bypass" \
+                       -w "$dst_ips_forward" \
+                       -e "$ipt_args" \
+                       -a "$a_args" \
+                       -d "$d_args" \
+                       $args \
+               || "$bin" -f
+}
+
+start_service() {
+       local cfgtype="$1"
+
+       mkdir -p "$ss_confdir"
+       config_load shadowsocks-libev
+       for cfgtype in ss_local ss_redir ss_server ss_tunnel; do
+               config_foreach ss_xxx "$cfgtype" "$cfgtype"
+       done
+       ss_rules
+}
+
+stop_service() {
+       local bin="$ss_bindir/ss-rules"
+
+       [ -x "$bin" ] && "$bin" -f
+       rm -rf "$ss_confdir"
+}
 
-       start_rules
+service_triggers() {
+       procd_add_reload_interface_trigger wan
+       procd_add_reload_trigger shadowsocks-libev
+       procd_open_validate
+       validate_server_section
+       validate_ss_local_section
+       validate_ss_redir_section
+       validate_ss_rules_section
+       validate_ss_server_section
+       validate_ss_tunnel_section
+       procd_close_validate
 }
 
-boot() {
-       until iptables-save -t nat | grep -q "^:zone_lan_prerouting"; do
-               sleep 1
+ss_validate_mklocal() {
+       local tuple opts
+
+       shift 2
+       for tuple in "$@"; do
+               opts="${tuple%%:*} $opts"
        done
-       start
+       [ -z "$opts" ] || echo "local $opts"
+}
+
+ss_validate() {
+       uci_validate_section shadowsocks-libev "$@"
+}
+
+validate_common_server_options_() {
+       local cfgtype="$1"; shift
+       local cfg="$1"; shift
+       local func="$1"; shift
+       local stream_methods='"table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20", "chacha20-ietf"'
+       local aead_methods='"aes-128-gcm", "aes-192-gcm", "aes-256-gcm"'
+
+       "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \
+               'disabled:bool:false' \
+               'server:host' \
+               'server_port:port' \
+               'password:string' \
+               'key:string' \
+               "method:or($stream_methods, $aead_methods)"
+}
+
+validate_common_client_options_() {
+       validate_common_options_ "$@" \
+               'server:uci("shadowsocks-libev", "@server")' \
+               'local_address:host:0.0.0.0' \
+               'local_port:port'
+}
+
+validate_common_options_() {
+       local cfgtype="$1"; shift
+       local cfg="$1"; shift
+       local func="$1"; shift
+
+       "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \
+               'disabled:bool:false' \
+               'verbose:bool:false' \
+               'fast_open:bool:false' \
+               'reuse_port:bool:false' \
+               'mode:or("tcp_only", "udp_only", "tcp_and_udp")' \
+               'mtu:uinteger' \
+               'timeout:uinteger' \
+               'user:string'
+}
+
+validate_server_section() {
+       validate_common_server_options_ server "$1" "${2}"
+}
+
+validate_ss_local_section() {
+       validate_common_client_options_ ss_local "$1" "${2}"
+}
+
+validate_ss_redir_section() {
+       validate_common_client_options_ ss_redir "$1" "${2}"
+}
+
+validate_ss_rules_section() {
+       "${2:-ss_validate}" ss_rules "$1" \
+               'disabled:bool:false' \
+               'redir_tcp:uci("shadowsocks-libev", "@ss_redir")' \
+               'redir_udp:uci("shadowsocks-libev", "@ss_redir")' \
+               'src_ips_bypass:list(ipaddr)' \
+               'src_ips_forward:list(ipaddr)' \
+               'src_ips_checkdst:list(ipaddr)' \
+               'dst_ips_bypass_file:file' \
+               'dst_ips_bypass:list(ipaddr)' \
+               'dst_ips_forward_file:file' \
+               'dst_ips_forward:list(ipaddr)' \
+               'src_default:or("bypass", "forward", "checkdst")' \
+               'local_default:or("bypass", "forward", "checkdst")' \
+               'ipt_args:string'
 }
 
-start() {
-       rules && start_redir
-       [ "$tunnel_enable" = 1 ] && start_tunnel
+validate_ss_server_section() {
+       validate_common_server_options_ ss_server "$1" \
+               validate_common_options_ \
+               "${2}" \
+               'bind_address:ipaddr' \
+               'manager_address:host'
 }
 
-stop() {
-       /usr/bin/ss-rules -f
-       killall -q -9 ss-redir
-       killall -q -9 ss-tunnel
+validate_ss_tunnel_section() {
+       validate_common_client_options_ ss_tunnel "$1" \
+               "${2}" \
+               'tunnel_address:regex(".+\:[0-9]+")'
 }
index 8ce1000cbcadad262d454a24ec4cef3fee21f728..8bd7264af1695fbbf76a9cb7f8f20a946c2234ba 100644 (file)
@@ -1,4 +1,10 @@
 #!/bin/sh
+#
+# Copyright (C) 2014-2017 Jian Chang <aa65535@live.com>
+#
+# This is free software, licensed under the GNU General Public License v3.
+# See /LICENSE for more information.
+#
 
 usage() {
        cat <<-EOF
@@ -6,20 +12,28 @@ usage() {
 
                Valid options are:
 
-                   -s <server_host>        hostname or ip of shadowsocks remote server
+                   -s <server_ips>         ip address of shadowsocks remote server
                    -l <local_port>         port number of shadowsocks local server
-                   -i <ip_list_file>       a file content is bypassed ip list
-                   -a <lan_ips>            lan ip of access control, need a prefix to
-                                           define access control mode
+                   -S <server_ips>         ip address of shadowsocks remote UDP server
+                   -L <local_port>         port number of shadowsocks local UDP server
+                   -B <ip_list_file>       a file whose content is bypassed ip list
                    -b <wan_ips>            wan ip of will be bypassed
+                   -W <ip_list_file>       a file whose content is forwarded ip list
                    -w <wan_ips>            wan ip of will be forwarded
-                   -e <extra_options>      extra options for iptables
+                   -I <interface>          proxy only for the given interface
+                   -d <target>             the default target of lan access control
+                   -a <lan_hosts>          lan ip of access control, need a prefix to
+                                           define proxy type
+                   -e <extra_args>         extra arguments for iptables
                    -o                      apply the rules to the OUTPUT chain
+                   -O                      apply the global rules to the OUTPUT chain
                    -u                      enable udprelay mode, TPROXY is required
                    -U                      enable udprelay mode, using different IP
                                            and ports for TCP and UDP
                    -f                      flush the rules
+                   -h                      show this help message and exit
 EOF
+       exit $1
 }
 
 loger() {
@@ -27,135 +41,192 @@ loger() {
        logger -st ss-rules[$$] -p$1 $2
 }
 
-ipt_n="iptables -t nat"
-ipt_m="iptables -t mangle"
-
-flush_r() {
-       local IPT
-
-       IPT=$(iptables-save -t nat)
-       eval $(echo "$IPT" | grep "_SS_SPEC_RULE_" | \
-               sed -e 's/^-A/$ipt_n -D/' -e 's/$/;/')
-
-       for chain in $(echo "$IPT" | awk '/^:SS_SPEC/{print $1}'); do
-               $ipt_n -F ${chain:1} 2>/dev/null && $ipt_n -X ${chain:1}
-       done
-
-       IPT=$(iptables-save -t mangle)
-       eval $(echo "$IPT" | grep "_SS_SPEC_RULE_" | \
-               sed -e 's/^-A/$ipt_m -D/' -e 's/$/;/')
-
-       for chain in $(echo "$IPT" | awk '/^:SS_SPEC/{print $1}'); do
-               $ipt_m -F ${chain:1} 2>/dev/null && $ipt_m -X ${chain:1}
+flush_rules() {
+       iptables-save -c | grep -v "SS_SPEC" | iptables-restore -c
+       if command -v ip >/dev/null 2>&1; then
+               ip rule del fwmark 1 lookup 100 2>/dev/null
+               ip route del local default dev lo table 100 2>/dev/null
+       fi
+       for setname in $(ipset -n list | grep "ss_spec"); do
+               ipset destroy $setname 2>/dev/null
        done
-
-       ip rule del fwmark 0x01/0x01 table 100 2>/dev/null
-       ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
-       ipset -X ss_spec_lan_ac 2>/dev/null
-       ipset -X ss_spec_wan_ac 2>/dev/null
+       FWI=$(uci get firewall.shadowsocks.path 2>/dev/null)
+       [ -n "$FWI" ] && echo '# firewall include file' >$FWI
        return 0
 }
 
-ipset_r() {
-       ipset -! -R <<-EOF || return 1
-               create ss_spec_wan_ac hash:net
-               $(echo -e "$IPLIST" | sed -e "s/^/add ss_spec_wan_ac /")
-               $(for ip in $WAN_FW_IP; do echo "add ss_spec_wan_ac $ip nomatch"; done)
+ipset_init() {
+       ipset -! restore <<-EOF || return 1
+               create ss_spec_src_ac hash:ip hashsize 64
+               create ss_spec_src_bp hash:ip hashsize 64
+               create ss_spec_src_fw hash:ip hashsize 64
+               create ss_spec_dst_sp hash:net hashsize 64
+               create ss_spec_dst_bp hash:net hashsize 64
+               create ss_spec_dst_fw hash:net hashsize 64
+               $(gen_lan_host_ipset_entry)
+               $(gen_special_purpose_ip | sed -e "s/^/add ss_spec_dst_sp /")
+               $(sed -e "s/^/add ss_spec_dst_bp /" ${WAN_BP_LIST:=/dev/null} 2>/dev/null)
+               $(for ip in $WAN_BP_IP; do echo "add ss_spec_dst_bp $ip"; done)
+               $(sed -e "s/^/add ss_spec_dst_fw /" ${WAN_FW_LIST:=/dev/null} 2>/dev/null)
+               $(for ip in $WAN_FW_IP; do echo "add ss_spec_dst_fw $ip"; done)
 EOF
-       $ipt_n -N SS_SPEC_WAN_AC && \
-       $ipt_n -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN && \
-       $ipt_n -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
-       return $?
+       return 0
 }
 
-fw_rule() {
-       $ipt_n -N SS_SPEC_WAN_FW && \
-       $ipt_n -A SS_SPEC_WAN_FW -p tcp \
-               -j REDIRECT --to-ports $local_port 2>/dev/null || {
-               loger 3 "Can't redirect, please check the iptables."
-               exit 1
-       }
+ipt_nat() {
+       include_ac_rules nat
+       ipt="iptables -t nat"
+       $ipt -A SS_SPEC_WAN_FW -p tcp \
+               -j REDIRECT --to-ports $local_port || return 1
+       if [ -n "$OUTPUT" ]; then
+               $ipt -N SS_SPEC_WAN_DG
+               $ipt -A SS_SPEC_WAN_DG -m set --match-set ss_spec_dst_sp dst -j RETURN
+               $ipt -A SS_SPEC_WAN_DG -p tcp $EXT_ARGS -j $OUTPUT
+               $ipt -I OUTPUT 1 -p tcp -j SS_SPEC_WAN_DG
+       fi
        return $?
 }
 
-ac_rule() {
-       local TAG ROUTECHAIN
-
-       if [ -n "$LAN_AC_IP" ]; then
-               if [ "${LAN_AC_IP:0:1}" = "w" ]; then
-                       TAG="nomatch"
-               else
-                       if [ "${LAN_AC_IP:0:1}" != "b" ]; then
-                               loger 3 "Bad argument \`-a $LAN_AC_IP\`."
-                               return 2
-                       fi
-               fi
+ipt_mangle() {
+       [ -n "$TPROXY" ] || return 0
+       if !(lsmod | grep -q TPROXY && command -v ip >/dev/null); then
+               loger 4 "TPROXY or ip not found."
+               return 0
        fi
+       ip rule add fwmark 1 lookup 100
+       ip route add local default dev lo table 100
+       include_ac_rules mangle
+       iptables -t mangle -A SS_SPEC_WAN_FW -p udp \
+               -j TPROXY --on-port $LOCAL_PORT --tproxy-mark 0x01/0x01
+       return $?
+}
 
-       ROUTECHAIN=PREROUTING
-       if iptables-save -t nat | grep -q "^:zone_lan_prerouting"; then
-               ROUTECHAIN=zone_lan_prerouting
-       fi
+export_ipt_rules() {
+       [ -n "$FWI" ] || return 0
+       cat <<-CAT >>$FWI
+       iptables-save -c | grep -v "SS_SPEC" | iptables-restore -c
+       iptables-restore -n <<-EOF
+       $(iptables-save | grep -E "SS_SPEC|^\*|^COMMIT" |\
+                       sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/")
+       EOF
+CAT
+       return $?
+}
+
+gen_lan_host_ipset_entry() {
+       for host in $LAN_HOSTS; do
+               case "${host:0:1}" in
+                       n|N)
+                               echo add ss_spec_src_ac ${host:2}
+                               ;;
+                       b|B)
+                               echo add ss_spec_src_bp ${host:2}
+                               ;;
+                       g|G)
+                               echo add ss_spec_src_fw ${host:2}
+                               ;;
+               esac
+       done
+}
 
-       ipset -! -R <<-EOF || return 1
-               create ss_spec_lan_ac hash:net
-               $(for ip in ${LAN_AC_IP:1}; do echo "add ss_spec_lan_ac $ip $TAG"; done)
+gen_special_purpose_ip() {
+       cat <<-EOF | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}"
+               0.0.0.0/8
+               10.0.0.0/8
+               100.64.0.0/10
+               127.0.0.0/8
+               169.254.0.0/16
+               172.16.0.0/12
+               192.0.0.0/24
+               192.0.2.0/24
+               192.31.196.0/24
+               192.52.193.0/24
+               192.88.99.0/24
+               192.168.0.0/16
+               192.175.48.0/24
+               198.18.0.0/15
+               198.51.100.0/24
+               203.0.113.0/24
+               224.0.0.0/4
+               240.0.0.0/4
+               255.255.255.255
+               $server
+               $SERVER
 EOF
-       $ipt_n -A $ROUTECHAIN -p tcp $EXT_ARGS \
-               -m set ! --match-set ss_spec_lan_ac src \
-               -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_WAN_AC
+}
 
-       if [ "$OUTPUT" = 1 ]; then
-               $ipt_n -A OUTPUT -p tcp $EXT_ARGS \
-                       -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_WAN_AC
-       fi
-       return $?
+include_ac_rules() {
+       local protocol=$([ "$1" = "mangle" ] && echo udp || echo tcp)
+       iptables-restore -n <<-EOF
+       *$1
+       :SS_SPEC_LAN_DG - [0:0]
+       :SS_SPEC_LAN_AC - [0:0]
+       :SS_SPEC_WAN_AC - [0:0]
+       :SS_SPEC_WAN_FW - [0:0]
+       -A SS_SPEC_LAN_DG -m set --match-set ss_spec_dst_sp dst -j RETURN
+       -A SS_SPEC_LAN_DG -p $protocol $EXT_ARGS -j SS_SPEC_LAN_AC
+       -A SS_SPEC_LAN_AC -m set --match-set ss_spec_src_bp src -j RETURN
+       -A SS_SPEC_LAN_AC -m set --match-set ss_spec_src_fw src -j SS_SPEC_WAN_FW
+       -A SS_SPEC_LAN_AC -m set --match-set ss_spec_src_ac src -j SS_SPEC_WAN_AC
+       -A SS_SPEC_LAN_AC -j ${LAN_TARGET:=SS_SPEC_WAN_AC}
+       -A SS_SPEC_WAN_AC -m set --match-set ss_spec_dst_fw dst -j SS_SPEC_WAN_FW
+       -A SS_SPEC_WAN_AC -m set --match-set ss_spec_dst_bp dst -j RETURN
+       -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
+       $(gen_prerouting_rules $protocol)
+       COMMIT
+EOF
 }
 
-tp_rule() {
-       [ -n "$TPROXY" ] || return 0
-       ip rule add fwmark 0x01/0x01 table 100
-       ip route add local 0.0.0.0/0 dev lo table 100
-       $ipt_m -N SS_SPEC_TPROXY
-       $ipt_m -A SS_SPEC_TPROXY -p udp -m set ! --match-set ss_spec_wan_ac dst \
-               -j TPROXY --on-port $LOCAL_PORT --tproxy-mark 0x01/0x01
-       $ipt_m -A PREROUTING -p udp $EXT_ARGS \
-               -m set ! --match-set ss_spec_lan_ac src \
-               -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_TPROXY
-       return $?
+gen_prerouting_rules() {
+       [ -z "$IFNAMES" ] && echo -I PREROUTING 1 -p $1 -j SS_SPEC_LAN_DG
+       for ifname in $IFNAMES; do
+               echo -I PREROUTING 1 -i $ifname -p $1 -j SS_SPEC_LAN_DG
+       done
 }
 
-while getopts ":s:l:S:L:i:e:a:b:w:ouUf" arg; do
-       case $arg in
+while getopts ":s:l:S:L:B:b:W:w:I:d:a:e:oOuUfh" arg; do
+       case "$arg" in
                s)
-                       server=$OPTARG
+                       server=$(for ip in $OPTARG; do echo $ip; done)
                        ;;
                l)
                        local_port=$OPTARG
                        ;;
                S)
-                       SERVER=$OPTARG
+                       SERVER=$(for ip in $OPTARG; do echo $ip; done)
                        ;;
                L)
                        LOCAL_PORT=$OPTARG
                        ;;
-               i)
-                       IGNORE=$OPTARG
-                       ;;
-               e)
-                       EXT_ARGS=$OPTARG
-                       ;;
-               a)
-                       LAN_AC_IP=$OPTARG
+               B)
+                       WAN_BP_LIST=$OPTARG
                        ;;
                b)
-                       WAN_BP_IP=$(for ip in $OPTARG; do echo $ip; done)
+                       WAN_BP_IP=$OPTARG
+                       ;;
+               W)
+                       WAN_FW_LIST=$OPTARG
                        ;;
                w)
                        WAN_FW_IP=$OPTARG
                        ;;
+               I)
+                       IFNAMES=$OPTARG
+                       ;;
+               d)
+                       LAN_TARGET=$OPTARG
+                       ;;
+               a)
+                       LAN_HOSTS=$OPTARG
+                       ;;
+               e)
+                       EXT_ARGS=$OPTARG
+                       ;;
                o)
-                       OUTPUT=1
+                       OUTPUT=SS_SPEC_WAN_AC
+                       ;;
+               O)
+                       OUTPUT=SS_SPEC_WAN_FW
                        ;;
                u)
                        TPROXY=1
@@ -164,56 +235,26 @@ while getopts ":s:l:S:L:i:e:a:b:w:ouUf" arg; do
                        TPROXY=2
                        ;;
                f)
-                       flush_r
+                       flush_rules
                        exit 0
                        ;;
+               h)
+                       usage 0
+                       ;;
        esac
 done
 
-if [ -z "$server" -o -z "$local_port" ]; then
-       usage
-       exit 2
-fi
+[ -z "$server" -o -z "$local_port" ] && usage 2
 
 if [ "$TPROXY" = 1 ]; then
-       SERVER=$server
+       unset SERVER
        LOCAL_PORT=$local_port
+elif [ "$TPROXY" = 2 ]; then
+       : ${SERVER:?"You must assign an ip for the udp relay server."}
+       : ${LOCAL_PORT:?"You must assign a port for the udp relay server."}
 fi
 
-if [ "$TPROXY" = 2 ]; then
-       if [ -z "$SERVER" -o -z "$LOCAL_PORT" ]; then
-               loger 3 "Please use -S and -L specifies IP and port for UDP."
-       fi
-fi
-
-if [ -f "$IGNORE" ]; then
-       IGNORE_IP=$(cat $IGNORE 2>/dev/null)
-fi
-
-IPLIST=$(cat <<-EOF | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}"
-       $server
-       $SERVER
-       0.0.0.0/8
-       10.0.0.0/8
-       100.64.0.0/10
-       127.0.0.0/8
-       169.254.0.0/16
-       172.16.0.0/12
-       192.0.0.0/24
-       192.0.2.0/24
-       192.88.99.0/24
-       192.168.0.0/16
-       198.18.0.0/15
-       198.51.100.0/24
-       203.0.113.0/24
-       224.0.0.0/4
-       240.0.0.0/4
-       255.255.255.255
-       $WAN_BP_IP
-       $IGNORE_IP
-EOF
-)
-
-flush_r && fw_rule && ipset_r && ac_rule && tp_rule
-
-exit $?
+flush_rules && ipset_init && ipt_nat && ipt_mangle && export_ipt_rules
+RET=$?
+[ "$RET" = 0 ] || loger 3 "Start failed!"
+exit $RET
diff --git a/net/shadowsocks-libev/files/ss-rules.defaults b/net/shadowsocks-libev/files/ss-rules.defaults
new file mode 100755 (executable)
index 0000000..c89e2d0
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+s=firewall.ss_rules
+uci get "$s" >/dev/null && exit 0
+uci batch <<-EOF
+       set $s=include
+       set $s.path=/etc/firewall.ss-rules
+       set $s.reload=1
+       commit firewall
+EOF
diff --git a/net/shadowsocks-libev/patches/0001-decouple-use_syslog-from-pid_flags.patch b/net/shadowsocks-libev/patches/0001-decouple-use_syslog-from-pid_flags.patch
new file mode 100644 (file)
index 0000000..875f105
--- /dev/null
@@ -0,0 +1,151 @@
+From ea18a4ffcd9a8de4c5b888d9dc58a2b173c5ff8e Mon Sep 17 00:00:00 2001
+From: Yousong Zhou <yszhou4tech@gmail.com>
+Date: Mon, 26 Jun 2017 14:49:36 +0800
+Subject: [PATCH] decouple use_syslog from pid_flags
+
+Sometimes we need processes to run in the foreground to be supervised
+and at the same time use syslog facility instead of logging its stdout,
+stderr output
+---
+ src/jconf.c   |  6 ++++++
+ src/local.c   |  2 +-
+ src/manager.c |  2 +-
+ src/redir.c   |  2 +-
+ src/server.c  |  2 +-
+ src/tunnel.c  |  2 +-
+ src/utils.h   | 18 +++++++++++-------
+ 7 files changed, 22 insertions(+), 12 deletions(-)
+
+diff --git a/src/jconf.c b/src/jconf.c
+index 3c58148..05445c3 100644
+--- a/src/jconf.c
++++ b/src/jconf.c
+@@ -313,6 +313,12 @@ read_jconf(const char *file)
+                 check_json_value_type(value, json_boolean,
+                     "invalid config file: option 'ipv6_first' must be a boolean");
+                 conf.ipv6_first = value->u.boolean;
++#ifdef HAS_SYSLOG
++            } else if (strcmp(name, "use_syslog") == 0) {
++                check_json_value_type(value, json_boolean,
++                    "invalid config file: option 'use_syslog' must be a boolean");
++                use_syslog = value->u.boolean;
++#endif
+             }
+         }
+     } else {
+diff --git a/src/local.c b/src/local.c
+index aa69205..d123516 100644
+--- a/src/local.c
++++ b/src/local.c
+@@ -1519,8 +1519,8 @@ main(int argc, char **argv)
+         local_addr = "127.0.0.1";
+     }
++    USE_SYSLOG(argv[0], pid_flags);
+     if (pid_flags) {
+-        USE_SYSLOG(argv[0]);
+         daemonize(pid_path);
+     }
+diff --git a/src/manager.c b/src/manager.c
+index 6e7197c..338ab85 100644
+--- a/src/manager.c
++++ b/src/manager.c
+@@ -1149,8 +1149,8 @@ main(int argc, char **argv)
+         timeout = "60";
+     }
++    USE_SYSLOG(argv[0], pid_flags);
+     if (pid_flags) {
+-        USE_SYSLOG(argv[0]);
+         daemonize(pid_path);
+     }
+diff --git a/src/redir.c b/src/redir.c
+index 4856007..88660f8 100644
+--- a/src/redir.c
++++ b/src/redir.c
+@@ -1137,8 +1137,8 @@ main(int argc, char **argv)
+ #endif
+     }
++    USE_SYSLOG(argv[0], pid_flags);
+     if (pid_flags) {
+-        USE_SYSLOG(argv[0]);
+         daemonize(pid_path);
+     }
+diff --git a/src/server.c b/src/server.c
+index 747f0e5..7e3df9e 100644
+--- a/src/server.c
++++ b/src/server.c
+@@ -1726,8 +1726,8 @@ main(int argc, char **argv)
+     }
+ #endif
++    USE_SYSLOG(argv[0], pid_flags);
+     if (pid_flags) {
+-        USE_SYSLOG(argv[0]);
+         daemonize(pid_path);
+     }
+diff --git a/src/tunnel.c b/src/tunnel.c
+index 77c7380..2419fa0 100644
+--- a/src/tunnel.c
++++ b/src/tunnel.c
+@@ -1022,8 +1022,8 @@ main(int argc, char **argv)
+         local_addr = "127.0.0.1";
+     }
++    USE_SYSLOG(argv[0], pid_flags);
+     if (pid_flags) {
+-        USE_SYSLOG(argv[0]);
+         daemonize(pid_path);
+     }
+diff --git a/src/utils.h b/src/utils.h
+index 2603e85..53f3983 100644
+--- a/src/utils.h
++++ b/src/utils.h
+@@ -35,7 +35,7 @@
+ #include <android/log.h>
+ #define USE_TTY()
+-#define USE_SYSLOG(ident)
++#define USE_SYSLOG(ident, _cond)
+ #define LOGI(...)                                                \
+     ((void)__android_log_print(ANDROID_LOG_DEBUG, "shadowsocks", \
+                                __VA_ARGS__))
+@@ -53,7 +53,7 @@
+ extern FILE *logfile;
+ #define TIME_FORMAT "%Y-%m-%d %H:%M:%S"
+ #define USE_TTY()
+-#define USE_SYSLOG(ident)
++#define USE_SYSLOG(ident, _cond)
+ #define USE_LOGFILE(ident)                                     \
+     do {                                                       \
+         if (ident != NULL) { logfile = fopen(ident, "w+"); } } \
+@@ -99,11 +99,15 @@ extern int use_syslog;
+         use_tty = isatty(STDERR_FILENO); \
+     } while (0)
+-#define USE_SYSLOG(ident)                          \
+-    do {                                           \
+-        use_syslog = 1;                            \
+-        openlog((ident), LOG_CONS | LOG_PID, 0); } \
+-    while (0)
++#define USE_SYSLOG(_ident, _cond)                       \
++    do {                                                \
++        if (!use_syslog && (_cond)) {                   \
++            use_syslog = 1;                             \
++        }                                               \
++        if (use_syslog) {                               \
++            openlog((_ident), LOG_CONS | LOG_PID, 0);   \
++        }                                               \
++    } while (0)
+ #define LOGI(format, ...)                                                        \
+     do {                                                                         \
+-- 
+2.12.2
+