| #!/bin/sh /etc/rc.common | 
 | # | 
 | # Copyright (C) 2017-2019 Yousong Zhou <yszhou4tech@gmail.com> | 
 | # | 
 | # This is free software, licensed under the GNU General Public License v3. | 
 | # See /LICENSE for more information. | 
 | # | 
 |  | 
 | USE_PROCD=1 | 
 | START=99 | 
 |  | 
 | ss_confdir=/var/etc/shadowsocks-libev | 
 | ss_bindir=/usr/bin | 
 |  | 
 | 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 | 
 | 	[ -z "$server" ] || json_add_string server "$server" | 
 | 	json_add_int server_port "$server_port" | 
 | 	[ -z "$method" ] || json_add_string method "$method" | 
 | 	[ -z "$key" ] || json_add_string key "$key" | 
 | 	[ -z "$password" ] || json_add_string password "$password" | 
 | 	[ -z "$plugin" ] || json_add_string plugin "$plugin" | 
 | 	[ -z "$plugin_opts" ] || json_add_string plugin_opts "$plugin_opts" | 
 | } | 
 |  | 
 | 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 | 
 | 	json_add_string tunnel_address "$tunnel_address" | 
 | } | 
 |  | 
 | 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" || return | 
 | 	[ "$disabled" = 0 ] || return | 
 |  | 
 | 	json_init | 
 | 	ss_mkjson_${cfgtype}_conf || return | 
 | 	json_add_boolean use_syslog 1 | 
 | 	json_add_boolean ipv6_first "$ipv6_first" | 
 | 	json_add_boolean fast_open "$fast_open" | 
 | 	json_add_boolean reuse_port "$reuse_port" | 
 | 	json_add_boolean no_delay "$no_delay" | 
 | 	[ -z "$local_address" ] || json_add_string local_address "$local_address" | 
 | 	[ -z "$local_port" ] || json_add_int local_port "$local_port" | 
 | 	[ -z "$local_ipv4_address" ] || json_add_string local_ipv4_address "$local_ipv4_address" | 
 | 	[ -z "$local_ipv6_address" ] || json_add_string local_ipv6_address "$local_ipv6_address" | 
 | 	[ -z "$mode" ] || json_add_string mode "$mode" | 
 | 	[ -z "$mtu" ] || json_add_int mtu "$mtu" | 
 | 	[ -z "$timeout" ] || json_add_int timeout "$timeout" | 
 | 	[ -z "$user" ] || json_add_string user "$user" | 
 | 	json_dump -i >"$confjson" | 
 |  | 
 | 	procd_open_instance "$cfgtype.$cfg" | 
 | 	procd_set_param command "$bin" -c "$confjson" | 
 | 	[ "$verbose" = 0 ] || procd_append_param command -v | 
 | 	if [ -n "$bind_address" ]; then | 
 | 		echo "$cfgtype $cfg: uci option bind_address deprecated, please switch to local_address" >&2 | 
 | 		procd_append_param command -b "$bind_address" | 
 | 	fi | 
 | 	procd_set_param file "$confjson" | 
 | 	procd_set_param respawn | 
 | 	procd_close_instance | 
 | 	ss_rules_cb | 
 | } | 
 |  | 
 | ss_rules_cb() { | 
 | 	local cfgserver server | 
 |  | 
 | 	if [ "$cfgtype" = ss_redir ]; then | 
 | 		config_get cfgserver "$cfg" server | 
 | 		config_get server "$cfgserver" server | 
 | 		ss_redir_servers="$ss_redir_servers $server" | 
 | 		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" | 
 | 		fi | 
 | 	fi | 
 | } | 
 |  | 
 | ss_rules() { | 
 | 	local cfg="ss_rules" | 
 | 	local bin="$ss_bindir/ss-rules" | 
 | 	local cfgtype | 
 | 	local local_port_tcp local_port_udp | 
 | 	local args | 
 |  | 
 | 	[ -x "$bin" ] || return 1 | 
 | 	"$bin" -f | 
 | 	"$bin" -6 -f | 
 |  | 
 | 	config_get cfgtype "$cfg" TYPE | 
 | 	[ "$cfgtype" = ss_rules ] || return 1 | 
 |  | 
 | 	eval "$(validate_ss_rules_section "$cfg" ss_validate_mklocal)" | 
 | 	validate_ss_rules_section "$cfg" || return 1 | 
 | 	[ "$disabled" = 0 ] || return 0 | 
 |  | 
 | 	eval local_port_tcp="\$ss_rules_redir_tcp_$redir_tcp" | 
 | 	eval local_port_udp="\$ss_rules_redir_udp_$redir_udp" | 
 | 	[ -n "$local_port_tcp" -o -n "$local_port_udp" ] || return 1 | 
 | 	ss_redir_servers="$(echo "$ss_redir_servers" | tr ' ' '\n' | sort -u)" | 
 | 	[ "$dst_forward_recentrst" = 0 ] || args="$args --dst-forward-recentrst" | 
 |  | 
 | 	ss_rules_call | 
 | 	ss_rules_call -6 | 
 | } | 
 |  | 
 | ss_rules_call() { | 
 | 	"$bin" "$@" \ | 
 | 			-s "$ss_redir_servers" \ | 
 | 			-l "$local_port_tcp" \ | 
 | 			-L "$local_port_udp" \ | 
 | 			--src-default "$src_default" \ | 
 | 			--dst-default "$dst_default" \ | 
 | 			--local-default "$local_default" \ | 
 | 			--dst-bypass-file "$dst_ips_bypass_file" \ | 
 | 			--dst-forward-file "$dst_ips_forward_file" \ | 
 | 			--dst-bypass "$dst_ips_bypass" \ | 
 | 			--dst-forward "$dst_ips_forward" \ | 
 | 			--src-bypass "$src_ips_bypass" \ | 
 | 			--src-forward "$src_ips_forward" \ | 
 | 			--src-checkdst "$src_ips_checkdst" \ | 
 | 			--ifnames "$ifnames" \ | 
 | 			--ipt-extra "$ipt_args" \ | 
 | 			$args \ | 
 | 		|| "$bin" "$@" -f | 
 | } | 
 |  | 
 | start_service() { | 
 | 	local cfgtype | 
 |  | 
 | 	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 | 
 | 		"$bin" -6 -f | 
 | 	} | 
 | 	rm -rf "$ss_confdir" | 
 | } | 
 |  | 
 | 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 | 
 | } | 
 |  | 
 | ss_validate_mklocal() { | 
 | 	local tuple opts | 
 |  | 
 | 	shift 2 | 
 | 	for tuple in "$@"; do | 
 | 		opts="${tuple%%:*} $opts" | 
 | 	done | 
 | 	[ -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", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305"' | 
 |  | 
 | 	"${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ | 
 | 		'disabled:bool:0' \ | 
 | 		'server:host' \ | 
 | 		'server_port:port' \ | 
 | 		'password:string' \ | 
 | 		'key:string' \ | 
 | 		"method:or($stream_methods, $aead_methods)" \ | 
 | 		'plugin:string' \ | 
 | 		'plugin_opts:string' | 
 | } | 
 |  | 
 | validate_common_client_options_() { | 
 | 	validate_common_options_ "$@" \ | 
 | 		'server:uci("shadowsocks-libev", "@server")' \ | 
 | 		'local_address:ipaddr: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:0' \ | 
 | 		'fast_open:bool:0' \ | 
 | 		'ipv6_first:bool:0' \ | 
 | 		'no_delay:bool:0' \ | 
 | 		'reuse_port:bool:0' \ | 
 | 		'verbose:bool:0' \ | 
 | 		'mode:or("tcp_only", "udp_only", "tcp_and_udp"):tcp_only' \ | 
 | 		'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:0' \ | 
 | 		'redir_tcp:uci("shadowsocks-libev", "@ss_redir")' \ | 
 | 		'redir_udp:uci("shadowsocks-libev", "@ss_redir")' \ | 
 | 		'src_ips_bypass:or(ipaddr,cidr)' \ | 
 | 		'src_ips_forward:or(ipaddr,cidr)' \ | 
 | 		'src_ips_checkdst:or(ipaddr,cidr)' \ | 
 | 		'dst_ips_bypass_file:file' \ | 
 | 		'dst_ips_bypass:or(ipaddr,cidr)' \ | 
 | 		'dst_ips_forward_file:file' \ | 
 | 		'dst_ips_forward:or(ipaddr,cidr)' \ | 
 | 		'src_default:or("bypass", "forward", "checkdst"):checkdst' \ | 
 | 		'dst_default:or("bypass", "forward"):bypass' \ | 
 | 		'local_default:or("bypass", "forward", "checkdst"):bypass' \ | 
 | 		'dst_forward_recentrst:bool:0' \ | 
 | 		'ifnames:maxlength(15)' \ | 
 | 		'ipt_args:string' | 
 | } | 
 |  | 
 | validate_ss_server_section() { | 
 | 	validate_common_server_options_ ss_server "$1" \ | 
 | 		validate_common_options_ \ | 
 | 		"$2" \ | 
 | 		'local_address:ipaddr' \ | 
 | 		'local_ipv4_address:ip4addr' \ | 
 | 		'local_ipv6_address:ip6addr' \ | 
 | 		'bind_address:ipaddr' | 
 | } | 
 |  | 
 | validate_ss_tunnel_section() { | 
 | 	validate_common_client_options_ ss_tunnel "$1" \ | 
 | 		"$2" \ | 
 | 		'tunnel_address:regex(".+\:[0-9]+")' | 
 | } |