mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-30 18:55:13 +08:00
🗽 Sync 2026-06-03 14:05:40
This commit is contained in:
1 parent
82314810a3
commit
50bf1ec00a
61 files changed
+6813
-15676
No files matched your search
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ "$ACTION" = "ifup" ] || exit 0
|
||||
[ "$INTERFACE" = "wan" ] || exit 0
|
||||
|
||||
WAN_PROTO="$(uci -q get network.wan.proto 2>/dev/null)"
|
||||
[ "$WAN_PROTO" = "pppoe" ] || exit 0
|
||||
|
||||
LOCK_FILE="/var/run/ssrplus-pppoe-hotplug.lock"
|
||||
|
||||
(
|
||||
if ! mkdir "$LOCK_FILE" 2>/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
trap 'rmdir "$LOCK_FILE" 2>/dev/null' EXIT INT TERM
|
||||
|
||||
sleep 10
|
||||
/etc/init.d/shadowsocksr restart >/dev/null 2>&1
|
||||
) >/dev/null 2>&1 &
|
||||
|
||||
exit 0
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,25 @@
|
||||
amazonaws.com
|
||||
aws.amazon.com
|
||||
awsstatic.com
|
||||
fast.com
|
||||
netflix.com
|
||||
netflix.net
|
||||
nflxext.com
|
||||
nflximg.net
|
||||
nflxso.net
|
||||
nflxvideo.net
|
||||
netflixdnstest0.com
|
||||
netflixdnstest1.com
|
||||
netflixdnstest2.com
|
||||
netflixdnstest3.com
|
||||
netflixdnstest4.com
|
||||
netflixdnstest5.com
|
||||
netflixdnstest6.com
|
||||
netflixdnstest7.com
|
||||
netflixdnstest8.com
|
||||
netflixdnstest9.com
|
||||
hulu.com
|
||||
huluim.com
|
||||
hbonow.com
|
||||
hbogo.com
|
||||
hbo.com
|
||||
@@ -0,0 +1,192 @@
|
||||
server=/v.youku.com/127.0.0.1#5335
|
||||
server=/api.youku.com/127.0.0.1#5335
|
||||
server=/v2.tudou.com/127.0.0.1#5335
|
||||
server=/www.tudou.com/127.0.0.1#5335
|
||||
server=/s.plcloud.music.qq.com/127.0.0.1#5335
|
||||
server=/i.y.qq.com/127.0.0.1#5335
|
||||
server=/hot.vrs.sohu.com/127.0.0.1#5335
|
||||
server=/live.tv.sohu.com/127.0.0.1#5335
|
||||
server=/pad.tv.sohu.com/127.0.0.1#5335
|
||||
server=/my.tv.sohu.com/127.0.0.1#5335
|
||||
server=/hot.vrs.letv.com/127.0.0.1#5335
|
||||
server=/data.video.qiyi.com/127.0.0.1#5335
|
||||
server=/cache.video.qiyi.com/127.0.0.1#5335
|
||||
server=/cache.vip.qiyi.com/127.0.0.1#5335
|
||||
server=/vv.video.qq.com/127.0.0.1#5335
|
||||
server=/tt.video.qq.com/127.0.0.1#5335
|
||||
server=/ice.video.qq.com/127.0.0.1#5335
|
||||
server=/tjsa.video.qq.com/127.0.0.1#5335
|
||||
server=/a10.video.qq.com/127.0.0.1#5335
|
||||
server=/xyy.video.qq.com/127.0.0.1#5335
|
||||
server=/vcq.video.qq.com/127.0.0.1#5335
|
||||
server=/vsh.video.qq.com/127.0.0.1#5335
|
||||
server=/vbj.video.qq.com/127.0.0.1#5335
|
||||
server=/bobo.video.qq.com/127.0.0.1#5335
|
||||
server=/flvs.video.qq.com/127.0.0.1#5335
|
||||
server=/bkvv.video.qq.com/127.0.0.1#5335
|
||||
server=/info.zb.qq.com/127.0.0.1#5335
|
||||
server=/geo.js.kankan.xunlei.com/127.0.0.1#5335
|
||||
server=/web-play.pptv.com/127.0.0.1#5335
|
||||
server=/web-play.pplive.cn/127.0.0.1#5335
|
||||
server=/dyn.ugc.pps.tv/127.0.0.1#5335
|
||||
server=/v.pps.tv/127.0.0.1#5335
|
||||
server=/inner.kandian.com/127.0.0.1#5335
|
||||
server=/ipservice.163.com/127.0.0.1#5335
|
||||
server=/so.open.163.com/127.0.0.1#5335
|
||||
server=/zb.s.qq.com/127.0.0.1#5335
|
||||
server=/ip.kankan.xunlei.com/127.0.0.1#5335
|
||||
server=/vxml.56.com/127.0.0.1#5335
|
||||
server=/music.sina.com.cn/127.0.0.1#5335
|
||||
server=/play.baidu.com/127.0.0.1#5335
|
||||
server=/v.iask.com/127.0.0.1#5335
|
||||
server=/tv.weibo.com/127.0.0.1#5335
|
||||
server=/wtv.v.iask.com/127.0.0.1#5335
|
||||
server=/video.sina.com.cn/127.0.0.1#5335
|
||||
server=/www.yinyuetai.com/127.0.0.1#5335
|
||||
server=/api.letv.com/127.0.0.1#5335
|
||||
server=/live.gslb.letv.com/127.0.0.1#5335
|
||||
server=/static.itv.letv.com/127.0.0.1#5335
|
||||
server=/ip.apps.cntv.cn/127.0.0.1#5335
|
||||
server=/vdn.apps.cntv.cn/127.0.0.1#5335
|
||||
server=/vdn.live.cntv.cn/127.0.0.1#5335
|
||||
server=/vip.sports.cntv.cn/127.0.0.1#5335
|
||||
server=/a.play.api.3g.youku.com/127.0.0.1#5335
|
||||
server=/i.play.api.3g.youku.com/127.0.0.1#5335
|
||||
server=/api.3g.youku.com/127.0.0.1#5335
|
||||
server=/tv.api.3g.youku.com/127.0.0.1#5335
|
||||
server=/play.api.3g.youku.com/127.0.0.1#5335
|
||||
server=/play.api.3g.tudou.com/127.0.0.1#5335
|
||||
server=/tv.api.3g.tudou.com/127.0.0.1#5335
|
||||
server=/api.3g.tudou.com/127.0.0.1#5335
|
||||
server=/api.tv.sohu.com/127.0.0.1#5335
|
||||
server=/access.tv.sohu.com/127.0.0.1#5335
|
||||
server=/iface.iqiyi.com/127.0.0.1#5335
|
||||
server=/iface2.iqiyi.com/127.0.0.1#5335
|
||||
server=/cache.m.iqiyi.com/127.0.0.1#5335
|
||||
server=/dynamic.app.m.letv.com/127.0.0.1#5335
|
||||
server=/dynamic.meizi.app.m.letv.com/127.0.0.1#5335
|
||||
server=/dynamic.search.app.m.letv.com/127.0.0.1#5335
|
||||
server=/dynamic.live.app.m.letv.com/127.0.0.1#5335
|
||||
server=/listso.m.areainfo.ppstream.com/127.0.0.1#5335
|
||||
server=/epg.api.pptv.com/127.0.0.1#5335
|
||||
server=/play.api.pptv.com/127.0.0.1#5335
|
||||
server=/m.letv.com/127.0.0.1#5335
|
||||
server=/interface.bilibili.com/127.0.0.1#5335
|
||||
server=/3g.music.qq.com/127.0.0.1#5335
|
||||
server=/mqqplayer.3g.qq.com/127.0.0.1#5335
|
||||
server=/proxy.music.qq.com/127.0.0.1#5335
|
||||
server=/proxymc.qq.com/127.0.0.1#5335
|
||||
server=/ip2.kugou.com/127.0.0.1#5335
|
||||
server=/ip.kugou.com/127.0.0.1#5335
|
||||
server=/client.api.ttpod.com/127.0.0.1#5335
|
||||
server=/mobi.kuwo.cn/127.0.0.1#5335
|
||||
server=/mobilefeedback.kugou.com/127.0.0.1#5335
|
||||
server=/tingapi.ting.baidu.com/127.0.0.1#5335
|
||||
server=/music.baidu.com/127.0.0.1#5335
|
||||
server=/serviceinfo.sdk.duomi.com/127.0.0.1#5335
|
||||
server=/music.163.com/127.0.0.1#5335
|
||||
server=/www.xiami.com/127.0.0.1#5335
|
||||
server=/spark.api.xiami.com/127.0.0.1#5335
|
||||
server=/iplocation.geo.qiyi.com/127.0.0.1#5335
|
||||
server=/sns.video.qq.com/127.0.0.1#5335
|
||||
server=/v5.pc.duomi.com/127.0.0.1#5335
|
||||
server=/tms.is.ysten.com/127.0.0.1#5335
|
||||
server=/internal.check.duokanbox.com/127.0.0.1#5335
|
||||
server=/openapi.youku.com/127.0.0.1#5335
|
||||
server=/y.qq.com/127.0.0.1#5335
|
||||
ipset=/v.youku.com/oversea
|
||||
ipset=/api.youku.com/oversea
|
||||
ipset=/v2.tudou.com/oversea
|
||||
ipset=/www.tudou.com/oversea
|
||||
ipset=/s.plcloud.music.qq.com/oversea
|
||||
ipset=/i.y.qq.com/oversea
|
||||
ipset=/hot.vrs.sohu.com/oversea
|
||||
ipset=/live.tv.sohu.com/oversea
|
||||
ipset=/pad.tv.sohu.com/oversea
|
||||
ipset=/my.tv.sohu.com/oversea
|
||||
ipset=/hot.vrs.letv.com/oversea
|
||||
ipset=/data.video.qiyi.com/oversea
|
||||
ipset=/cache.video.qiyi.com/oversea
|
||||
ipset=/cache.vip.qiyi.com/oversea
|
||||
ipset=/vv.video.qq.com/oversea
|
||||
ipset=/tt.video.qq.com/oversea
|
||||
ipset=/ice.video.qq.com/oversea
|
||||
ipset=/tjsa.video.qq.com/oversea
|
||||
ipset=/a10.video.qq.com/oversea
|
||||
ipset=/xyy.video.qq.com/oversea
|
||||
ipset=/vcq.video.qq.com/oversea
|
||||
ipset=/vsh.video.qq.com/oversea
|
||||
ipset=/vbj.video.qq.com/oversea
|
||||
ipset=/bobo.video.qq.com/oversea
|
||||
ipset=/flvs.video.qq.com/oversea
|
||||
ipset=/bkvv.video.qq.com/oversea
|
||||
ipset=/info.zb.qq.com/oversea
|
||||
ipset=/geo.js.kankan.xunlei.com/oversea
|
||||
ipset=/web-play.pptv.com/oversea
|
||||
ipset=/web-play.pplive.cn/oversea
|
||||
ipset=/dyn.ugc.pps.tv/oversea
|
||||
ipset=/v.pps.tv/oversea
|
||||
ipset=/inner.kandian.com/oversea
|
||||
ipset=/ipservice.163.com/oversea
|
||||
ipset=/so.open.163.com/oversea
|
||||
ipset=/zb.s.qq.com/oversea
|
||||
ipset=/ip.kankan.xunlei.com/oversea
|
||||
ipset=/vxml.56.com/oversea
|
||||
ipset=/music.sina.com.cn/oversea
|
||||
ipset=/play.baidu.com/oversea
|
||||
ipset=/v.iask.com/oversea
|
||||
ipset=/tv.weibo.com/oversea
|
||||
ipset=/wtv.v.iask.com/oversea
|
||||
ipset=/video.sina.com.cn/oversea
|
||||
ipset=/www.yinyuetai.com/oversea
|
||||
ipset=/api.letv.com/oversea
|
||||
ipset=/live.gslb.letv.com/oversea
|
||||
ipset=/static.itv.letv.com/oversea
|
||||
ipset=/ip.apps.cntv.cn/oversea
|
||||
ipset=/vdn.apps.cntv.cn/oversea
|
||||
ipset=/vdn.live.cntv.cn/oversea
|
||||
ipset=/vip.sports.cntv.cn/oversea
|
||||
ipset=/a.play.api.3g.youku.com/oversea
|
||||
ipset=/i.play.api.3g.youku.com/oversea
|
||||
ipset=/api.3g.youku.com/oversea
|
||||
ipset=/tv.api.3g.youku.com/oversea
|
||||
ipset=/play.api.3g.youku.com/oversea
|
||||
ipset=/play.api.3g.tudou.com/oversea
|
||||
ipset=/tv.api.3g.tudou.com/oversea
|
||||
ipset=/api.3g.tudou.com/oversea
|
||||
ipset=/api.tv.sohu.com/oversea
|
||||
ipset=/access.tv.sohu.com/oversea
|
||||
ipset=/iface.iqiyi.com/oversea
|
||||
ipset=/iface2.iqiyi.com/oversea
|
||||
ipset=/cache.m.iqiyi.com/oversea
|
||||
ipset=/dynamic.app.m.letv.com/oversea
|
||||
ipset=/dynamic.meizi.app.m.letv.com/oversea
|
||||
ipset=/dynamic.search.app.m.letv.com/oversea
|
||||
ipset=/dynamic.live.app.m.letv.com/oversea
|
||||
ipset=/listso.m.areainfo.ppstream.com/oversea
|
||||
ipset=/epg.api.pptv.com/oversea
|
||||
ipset=/play.api.pptv.com/oversea
|
||||
ipset=/m.letv.com/oversea
|
||||
ipset=/interface.bilibili.com/oversea
|
||||
ipset=/3g.music.qq.com/oversea
|
||||
ipset=/mqqplayer.3g.qq.com/oversea
|
||||
ipset=/proxy.music.qq.com/oversea
|
||||
ipset=/proxymc.qq.com/oversea
|
||||
ipset=/ip2.kugou.com/oversea
|
||||
ipset=/ip.kugou.com/oversea
|
||||
ipset=/client.api.ttpod.com/oversea
|
||||
ipset=/mobi.kuwo.cn/oversea
|
||||
ipset=/mobilefeedback.kugou.com/oversea
|
||||
ipset=/tingapi.ting.baidu.com/oversea
|
||||
ipset=/music.baidu.com/oversea
|
||||
ipset=/serviceinfo.sdk.duomi.com/oversea
|
||||
ipset=/music.163.com/oversea
|
||||
ipset=/www.xiami.com/oversea
|
||||
ipset=/spark.api.xiami.com/oversea
|
||||
ipset=/iplocation.geo.qiyi.com/oversea
|
||||
ipset=/sns.video.qq.com/oversea
|
||||
ipset=/v5.pc.duomi.com/oversea
|
||||
ipset=/tms.is.ysten.com/oversea
|
||||
ipset=/internal.check.duokanbox.com/oversea
|
||||
ipset=/openapi.youku.com/oversea
|
||||
ipset=/y.qq.com/oversea
|
||||
@@ -31,8 +31,11 @@ touch /etc/ssrplus/china_ssr.txt
|
||||
touch /etc/ssrplus/deny.list
|
||||
touch /etc/ssrplus/white.list
|
||||
touch /etc/ssrplus/black.list
|
||||
touch /etc/ssrplus/netflix.list
|
||||
touch /etc/ssrplus/netflixip.list
|
||||
touch /etc/ssrplus/gfw_base.conf
|
||||
touch /etc/ssrplus/gfw_list.conf
|
||||
touch /etc/ssrplus/oversea_list.conf
|
||||
touch /etc/ssrplus/ad.conf
|
||||
touch /etc/config/shadowsocksr
|
||||
|
||||
@@ -47,20 +50,16 @@ if [ -s "/etc/config/shadowsocksr" ]; then
|
||||
uci -q set shadowsocksr.@server_subscribe[0].auto_update_min_time='0'
|
||||
fi
|
||||
|
||||
if ! uci -q get shadowsocksr.@server_subscribe[0].config_auto_update_mode > /dev/null; then
|
||||
uci -q set shadowsocksr.@server_subscribe[0].config_auto_update_mode='0'
|
||||
fi
|
||||
|
||||
if ! uci -q get shadowsocksr.@server_subscribe[0].config_update_interval > /dev/null; then
|
||||
uci -q set shadowsocksr.@server_subscribe[0].config_update_interval='60'
|
||||
if ! uci -q get shadowsocksr.@server_subscribe[0].ss_type > /dev/null; then
|
||||
uci -q set shadowsocksr.@server_subscribe[0].ss_type='ss-rust'
|
||||
fi
|
||||
|
||||
if ! uci -q get shadowsocksr.@server_subscribe[0].user_agent > /dev/null; then
|
||||
uci -q set shadowsocksr.@server_subscribe[0].user_agent='v2rayN/9.99'
|
||||
fi
|
||||
|
||||
if ! uci -q get shadowsocksr.@server_subscribe[0].proxy > /dev/null; then
|
||||
uci -q set shadowsocksr.@server_subscribe[0].proxy='1'
|
||||
if ! uci -q get shadowsocksr.@server_subscribe[0].xray_hy2_type > /dev/null; then
|
||||
uci -q set shadowsocksr.@server_subscribe[0].xray_hy2_type='hysteria2'
|
||||
fi
|
||||
|
||||
if ! uci -q get shadowsocksr.@global_xray_fragment[0] > /dev/null; then
|
||||
@@ -69,61 +68,8 @@ if [ -s "/etc/config/shadowsocksr" ]; then
|
||||
uci -q set shadowsocksr.@global_xray_fragment[0].noise='0'
|
||||
fi
|
||||
|
||||
if ! uci -q get shadowsocksr.@global[0].component_mirror > /dev/null; then
|
||||
uci -q set shadowsocksr.@global[0].component_mirror='direct'
|
||||
fi
|
||||
|
||||
if ! uci -q get shadowsocksr.@global[0].filter_aaaa > /dev/null; then
|
||||
legacy_filter_aaaa="$(uci -q get shadowsocksr.@global[0].mosdns_ipv6)"
|
||||
uci -q set shadowsocksr.@global[0].filter_aaaa="${legacy_filter_aaaa:-1}"
|
||||
fi
|
||||
|
||||
legacy_dns_mode="$(uci -q get shadowsocksr.@global[0].pdnsd_enable)"
|
||||
if [ "$legacy_dns_mode" = "2" ] || [ "$legacy_dns_mode" = "3" ]; then
|
||||
uci -q set shadowsocksr.@global[0].pdnsd_enable='1'
|
||||
fi
|
||||
|
||||
if uci -q get shadowsocksr.@global[0].mosdns_ipv6 > /dev/null; then
|
||||
uci -q delete shadowsocksr.@global[0].mosdns_ipv6
|
||||
fi
|
||||
|
||||
if ! uci -q get shadowsocksr.@http_proxy[0] > /dev/null; then
|
||||
uci -q add shadowsocksr http_proxy
|
||||
uci -q set shadowsocksr.@http_proxy[0].server='nil'
|
||||
uci -q set shadowsocksr.@http_proxy[0].local_port='3128'
|
||||
uci -q set shadowsocksr.@http_proxy[0].http_auth='none'
|
||||
fi
|
||||
|
||||
ss_rust_bin="$(command -v ssserver 2>/dev/null)"
|
||||
[ -n "$ss_rust_bin" ] || [ ! -x /usr/libexec/ssserver ] || ss_rust_bin="/usr/libexec/ssserver"
|
||||
mihomo_bin="$(command -v mihomo 2>/dev/null)"
|
||||
[ -n "$mihomo_bin" ] || [ ! -x /usr/libexec/mihomo ] || mihomo_bin="/usr/libexec/mihomo"
|
||||
xray_bin="$(command -v xray 2>/dev/null)"
|
||||
[ -n "$xray_bin" ] || [ ! -x /usr/libexec/xray ] || xray_bin="/usr/libexec/xray"
|
||||
for section in $(uci -q show shadowsocksr | sed -n "s/^shadowsocksr\\.\\([^.=][^.=]*\\)=servers$/\\1/p"); do
|
||||
node_type="$(uci -q get shadowsocksr.${section}.type)"
|
||||
case "$node_type" in
|
||||
ss|ss-libev)
|
||||
if [ -n "$mihomo_bin" ]; then
|
||||
uci -q set shadowsocksr.${section}.type='ss'
|
||||
elif [ -n "$ss_rust_bin" ]; then
|
||||
uci -q set shadowsocksr.${section}.type='ss-rust'
|
||||
elif [ -n "$xray_bin" ]; then
|
||||
uci -q set shadowsocksr.${section}.type='v2ray'
|
||||
uci -q set shadowsocksr.${section}.v2ray_protocol='shadowsocks'
|
||||
fi
|
||||
;;
|
||||
v2ray)
|
||||
if [ "$(uci -q get shadowsocksr.${section}.v2ray_protocol)" = "shadowsocks" ] && [ -n "$mihomo_bin" ]; then
|
||||
uci -q set shadowsocksr.${section}.type='ss'
|
||||
uci -q delete shadowsocksr.${section}.v2ray_protocol
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
uci -q commit shadowsocksr
|
||||
fi
|
||||
uci -q commit shadowsocksr
|
||||
fi
|
||||
|
||||
[ -s "/etc/config/shadowsocksr" ] || /etc/init.d/shadowsocksr reset
|
||||
|
||||
|
||||
@@ -10,58 +10,6 @@
|
||||
LOCK_FILE="/var/lock/ssr-monitor.lock"
|
||||
[ -f "$LOCK_FILE" ] && exit 2
|
||||
touch "$LOCK_FILE"
|
||||
|
||||
ps_list() {
|
||||
if busybox ps -w >/dev/null 2>&1; then
|
||||
busybox ps -w
|
||||
else
|
||||
busybox ps
|
||||
fi
|
||||
}
|
||||
|
||||
match_proc_count() {
|
||||
local pattern="$1"
|
||||
ps_list | grep -E "$pattern" | grep -v grep | wc -l
|
||||
}
|
||||
|
||||
redir_tcp_running() {
|
||||
local ssr_count
|
||||
local ipt_count
|
||||
ssr_count=$(match_proc_count 'ssr-retcp')
|
||||
ipt_count=$(ps_list | grep 'ipt2socks' | grep -v grep | grep -E ' -T( |$)| --tcp-only( |$)' | wc -l)
|
||||
echo $((ssr_count + ipt_count))
|
||||
}
|
||||
|
||||
redir_udp_running() {
|
||||
local ssr_count
|
||||
local ipt_count
|
||||
ssr_count=$(match_proc_count 'ssr-reudp')
|
||||
ipt_count=$(ps_list | grep 'ipt2socks' | grep -v grep | grep -E ' -U( |$)| --udp-only( |$)' | wc -l)
|
||||
echo $((ssr_count + ipt_count))
|
||||
}
|
||||
|
||||
kill_matched_procs() {
|
||||
local pattern="$1"
|
||||
ps_list | grep -E "$pattern" | grep -v grep | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1
|
||||
}
|
||||
|
||||
get_expected_threads() {
|
||||
local threads
|
||||
threads=$(uci_get_by_type global threads 0)
|
||||
if [ "$threads" = "0" ] || [ -z "$threads" ]; then
|
||||
threads=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null)
|
||||
fi
|
||||
|
||||
case "$threads" in
|
||||
''|*[!0-9]*)
|
||||
threads=1
|
||||
;;
|
||||
esac
|
||||
|
||||
[ "$threads" -lt 1 ] && threads=1
|
||||
echo "$threads"
|
||||
}
|
||||
|
||||
server_process_count=$1
|
||||
redir_tcp_process=$2
|
||||
redir_udp_process=$3
|
||||
@@ -73,8 +21,6 @@ if [ -z "$pdnsd_process" ]; then
|
||||
fi
|
||||
i=0
|
||||
GLOBAL_SERVER=$(uci_get_by_type global global_server)
|
||||
GLOBAL_TYPE=$(uci_get_by_name $GLOBAL_SERVER type)
|
||||
EXPECTED_THREADS=$(get_expected_threads)
|
||||
server=$(uci_get_by_name $GLOBAL_SERVER server)
|
||||
kcp_port=$(uci_get_by_name $GLOBAL_SERVER kcp_port)
|
||||
server_port=$(uci_get_by_name $GLOBAL_SERVER server_port)
|
||||
@@ -86,12 +32,8 @@ while [ "1" == "1" ]; do #死循环
|
||||
sleep 000030s
|
||||
#redir tcp
|
||||
if [ "$redir_tcp_process" -gt 0 ]; then
|
||||
icount=$(redir_tcp_running)
|
||||
local tcp_expected="$redir_tcp_process"
|
||||
if [ "$GLOBAL_TYPE" = "socks5" ]; then
|
||||
tcp_expected="$EXPECTED_THREADS"
|
||||
fi
|
||||
if [ "$icount" -lt "$tcp_expected" ]; then
|
||||
icount=$(busybox ps -w | grep ssr-retcp | grep -v grep | wc -l)
|
||||
if [ "$icount" == 0 ]; then
|
||||
logger -t "$NAME" "ssrplus redir tcp error.restart!"
|
||||
echolog "ssrplus redir tcp error.restart!"
|
||||
/etc/init.d/shadowsocksr restart
|
||||
@@ -100,12 +42,8 @@ while [ "1" == "1" ]; do #死循环
|
||||
fi
|
||||
#redir udp
|
||||
if [ "$redir_udp_process" -gt 0 ]; then
|
||||
icount=$(redir_udp_running)
|
||||
local udp_expected="$redir_udp_process"
|
||||
if [ "$GLOBAL_TYPE" = "socks5" ]; then
|
||||
udp_expected="$EXPECTED_THREADS"
|
||||
fi
|
||||
if [ "$icount" -lt "$udp_expected" ]; then
|
||||
icount=$(busybox ps -w | grep ssr-reudp | grep -v grep | wc -l)
|
||||
if [ "$icount" == 0 ]; then
|
||||
logger -t "$NAME" "ssrplus redir udp error.restart!"
|
||||
echolog "ssrplus redir udp error.restart!"
|
||||
/etc/init.d/shadowsocksr restart
|
||||
@@ -114,18 +52,18 @@ while [ "1" == "1" ]; do #死循环
|
||||
fi
|
||||
#server
|
||||
if [ "$server_process_count" -gt 0 ]; then
|
||||
icount=$(match_proc_count 'ssr-server|ss-server-|xray-server-')
|
||||
icount=$(busybox ps -w | grep ssr-server | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt "$server_process_count" ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "ssrplus server error.restart!"
|
||||
echolog "ssrplus server error.restart!"
|
||||
kill_matched_procs 'ssr-server|ss-server-'
|
||||
kill -9 $(busybox ps -w | grep ssr-server | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
/etc/init.d/shadowsocksr restart
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
#kcptun
|
||||
if [ "$kcp_process" -gt 0 ]; then
|
||||
icount=$(ps_list | grep kcptun-client | grep -v grep | wc -l)
|
||||
icount=$(busybox ps -w | grep kcptun-client | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt "$kcp_process" ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "ssrplus kcptun error.restart!"
|
||||
echolog "ssrplus kcptun error.restart!"
|
||||
@@ -135,43 +73,132 @@ while [ "1" == "1" ]; do #死循环
|
||||
fi
|
||||
#localsocks
|
||||
if [ "$local_process" -gt 0 ]; then
|
||||
icount=$(match_proc_count 'ssr-local|ss-local|tuic-local')
|
||||
icount=$(busybox ps -w | grep ssr-local | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt "$local_process" ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "global socks server error.restart!"
|
||||
echolog "global socks server error.restart!"
|
||||
kill_matched_procs 'ssr-local|ss-local|tuic-local'
|
||||
kill -9 $(busybox ps -w | grep ssr-local | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
/etc/init.d/shadowsocksr restart
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
#dns2tcp
|
||||
if [ "$pdnsd_process" -eq 1 ]; then
|
||||
icount=$(ps_list | grep $TMP_BIN_PATH/dns2tcp | grep -v grep | wc -l)
|
||||
icount=$(busybox ps -w | grep $TMP_BIN_PATH/dns2tcp | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt 1 ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "dns2tcp tunnel error.restart!"
|
||||
echolog "dns2tcp tunnel error.restart!"
|
||||
dnsserver=$(uci_get_by_type global tunnel_forward 8.8.4.4:53)
|
||||
kill -9 $(ps_list | grep $TMP_BIN_PATH/dns2tcp | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
start_dns2tcp "$dnsserver"
|
||||
kill -9 $(busybox ps -w | grep $TMP_BIN_PATH/dns2tcp | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
ln_start_bin $(first_type dns2tcp) dns2tcp -L "127.0.0.1#$dns_port" -R "${dnsserver/:/#}"
|
||||
fi
|
||||
#dns2socks
|
||||
elif [ "$pdnsd_process" -eq 2 ]; then
|
||||
icount=$(busybox ps -w | grep -e ssrplus-dns -e "dns2socks 127.0.0.1 $tmp_dns_port" | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt 1 ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "dns2socks $dnsserver tunnel error.restart!"
|
||||
echolog "dns2socks $dnsserver tunnel error.restart!"
|
||||
dnsserver=$(uci_get_by_type global tunnel_forward 8.8.4.4:53)
|
||||
kill -9 $(busybox ps -w | grep ssrplus-dns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
kill -9 $(busybox ps -w | grep "dns2socks 127.0.0.1 $tmp_dns_port" | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
ln_start_bin $(first_type microsocks) microsocks -i 127.0.0.1 -p $tmp_dns_port ssrplus-dns
|
||||
ln_start_bin $(first_type dns2socks) dns2socks 127.0.0.1:$tmp_dns_port $dnsserver 127.0.0.1:$dns_port -q
|
||||
fi
|
||||
#dns2socks-rust
|
||||
elif [ "$pdnsd_process" -eq 3 ]; then
|
||||
icount=$(busybox ps -w | grep -e ssrplus-dns -e "dns2socks-rust -s socks5://127.0.0.1 $tmp_dns_port" | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt 1 ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "dns2socks-rust $dnsserver tunnel error.restart!"
|
||||
echolog "dns2socks-rust $dnsserver tunnel error.restart!"
|
||||
dnsserver=$(uci_get_by_type global tunnel_forward 8.8.4.4:53)
|
||||
kill -9 $(busybox ps -w | grep ssrplus-dns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
kill -9 $(busybox ps -w | grep "dns2socks-rust -s socks5://127.0.0.1 $tmp_dns_port" | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
ln_start_bin $(first_type microsocks) microsocks -i 127.0.0.1 -p $tmp_dns_port ssrplus-dns
|
||||
ln_start_bin $(first_type dns2socks) dns2socks-rust -s socks5://127.0.0.1:$tmp_dns_port -d $dnsserver -l 127.0.0.1:$dns_port -f -c
|
||||
fi
|
||||
#mosdns
|
||||
elif [ "$pdnsd_process" -eq 4 ]; then
|
||||
icount=$(ps_list | grep $TMP_BIN_PATH/mosdns | grep -v grep | wc -l)
|
||||
icount=$(busybox ps -w | grep $TMP_BIN_PATH/mosdns | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt 1 ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "mosdns tunnel error.restart!"
|
||||
echolog "mosdns tunnel error.restart!"
|
||||
dnsserver=$(uci_get_by_type global tunnel_forward 8.8.4.4:53)
|
||||
kill -9 $(ps_list | grep $TMP_BIN_PATH/mosdns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
kill -9 $(busybox ps -w | grep $TMP_BIN_PATH/mosdns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
ln_start_bin $(first_type mosdns) mosdns start -c /etc/mosdns/config.yaml
|
||||
#dnsproxy
|
||||
elif [ "$pdnsd_process" -eq 5 ]; then
|
||||
icount=$(busybox ps -w | grep -e ssrplus-dns -e "dnsproxy -l 127.0.0.1 -p $tmp_dns_port" | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt 1 ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "dnsproxy $dnsserver tunnel error.restart!"
|
||||
echolog "dnsproxy $dnsserver tunnel error.restart!"
|
||||
local dnsproxy_dnsserver="$(uci_get_by_type global parse_method)"
|
||||
if [ -n "$dnsproxy_dnsserver" ] && [ "$dnsproxy_dnsserver" != "parse_file" ]; then
|
||||
dnsserver="$(uci_get_by_type global dnsproxy_tunnel_forward 8.8.4.4:53)"
|
||||
fi
|
||||
kill -9 $(busybox ps -w | grep "dnsproxy -l 127.0.0.1 -p $tmp_dns_port" | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
dnsproxy_ipv6="$(uci_get_by_type global dnsproxy_ipv6)"
|
||||
disabled_ipv6="--ipv6-disabled"
|
||||
fi
|
||||
if [ "$dnsproxy_dnsserver" != "parse_file" ]; then
|
||||
ln_start_bin $(first_type dnsproxy) dnsproxy -l 127.0.0.1 -p $tmp_dns_port -p $dns_port -u $dnsserver $disabled_ipv6 --cache --cache-min-ttl=3600
|
||||
else
|
||||
dnsproxy_dnsserver_file="$TMP_PATH/dnsproxy_dns.list"
|
||||
cleaned_file="$TMP_PATH/cleaned_dns.list"
|
||||
temp_file="$TMP_PATH/temp_dns.list"
|
||||
> "$cleaned_file"
|
||||
# 清理输入文件并去重
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
|
||||
[ -z "$line" ] && continue
|
||||
echo "$line" | grep -qE '^#' && continue
|
||||
echo "$line" >> "$cleaned_file"
|
||||
done < "/etc/ssrplus/dnsproxy_dns.list"
|
||||
# 获取清理后文件的MD5
|
||||
cleaned_md5=$(md5sum "$cleaned_file" | awk '{print $1}')
|
||||
if [ ! -f "$dnsproxy_dnsserver_file" ]; then
|
||||
cp "$cleaned_file" "$dnsproxy_dnsserver_file"
|
||||
else
|
||||
target_md5=$(md5sum "$dnsproxy_dnsserver_file" | awk '{print $1}')
|
||||
if [ "$cleaned_md5" != "$target_md5" ]; then
|
||||
> "$temp_file"
|
||||
# 保留目标文件中也存在于清理文件的记录(去重)
|
||||
while IFS= read -r line; do
|
||||
line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
|
||||
if grep -qixF "$line" "$cleaned_file" && ! grep -qixF "$line" "$temp_file"; then
|
||||
echo "$line" >> "$temp_file"
|
||||
fi
|
||||
done < "$dnsproxy_dnsserver_file"
|
||||
# 添加清理文件中有但目标文件没有的记录(去重)
|
||||
while IFS= read -r line; do
|
||||
line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
|
||||
if ! grep -qixF "$line" "$temp_file"; then
|
||||
echo "$line" >> "$temp_file"
|
||||
fi
|
||||
done < "$cleaned_file"
|
||||
temp_md5=$(md5sum "$temp_file" | awk '{print $1}')
|
||||
if [ "$temp_md5" != "$target_md5" ]; then
|
||||
mv "$temp_file" "$dnsproxy_dnsserver_file"
|
||||
else
|
||||
rm -f "$temp_file"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
rm -f "$cleaned_file"
|
||||
|
||||
if [ -n "$dnsproxy_dnsserver_file" ] && [ -s "$dnsproxy_dnsserver_file" ]; then
|
||||
local upstreams_logic_mode="$(uci_get_by_type global upstreams_logic_mode)"
|
||||
ln_start_bin $(first_type dnsproxy) dnsproxy -l 127.0.0.1 -p $tmp_dns_port -p $dns_port -u $dnsproxy_dnsserver_file $disabled_ipv6 --cache --cache-min-ttl=3600 --upstream-mode=$upstreams_logic_mode
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
#chinadns-ng(proxy)
|
||||
elif [ "$pdnsd_process" -eq 6 ]; then
|
||||
icount=$(ps_list | grep -e ssrplus-dns -e "chinadns-ng -b 127.0.0.1 -l $tmp_dns_port" | grep -v grep | wc -l)
|
||||
icount=$(busybox ps -w | grep -e ssrplus-dns -e "chinadns-ng -b 127.0.0.1 -l $tmp_dns_port" | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt 1 ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "chinadns-ng $dnsserver tunnel error.restart!"
|
||||
echolog "chinadns-ng $dnsserver tunnel error.restart!"
|
||||
dnsserver=$(uci_get_by_type global chinadns_ng_tunnel_forward 8.8.4.4:53)
|
||||
kill -9 $(ps_list | grep "chinadns-ng -b 127.0.0.1 -l $tmp_dns_port" | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
kill -9 $(busybox ps -w | grep "chinadns-ng -b 127.0.0.1 -l $tmp_dns_port" | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
local chinadns_ng_proto="$(uci_get_by_type global chinadns_ng_proto)"
|
||||
local chinadns_ng_dns=""
|
||||
IFS=','
|
||||
@@ -197,18 +224,10 @@ while [ "1" == "1" ]; do #死循环
|
||||
dnsserver="$chinadns_ng_dns"
|
||||
ln_start_bin $(first_type chinadns-ng) chinadns-ng -b 127.0.0.1 -l $tmp_dns_port -l $dns_port -p 3 -d gfw $dnsserver -N --filter-qtype 64,65 -f -r --cache 4096 --cache-stale 86400 --cache-refresh 20
|
||||
fi
|
||||
#built-in dns
|
||||
elif [ "$pdnsd_process" -eq 7 ]; then
|
||||
if ! is_builtin_dns_active; then
|
||||
logger -t "$NAME" "builtin dns error.restart!"
|
||||
echolog "builtin dns error.restart!"
|
||||
/etc/init.d/shadowsocksr restart
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
#chinadns-ng(china)
|
||||
if [ "$(uci -q get "dhcp.@dnsmasq[0]._unused_ssrp_changed")" = "1" ]; then
|
||||
icount=$(ps_list | grep $TMP_BIN_PATH/chinadns-ng | grep -v grep | wc -l)
|
||||
icount=$(busybox ps -w | grep $TMP_BIN_PATH/chinadns-ng | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt 1 ]; then #如果进程挂掉就重启它
|
||||
logger -t "$NAME" "chinadns-ng tunnel error.restart!"
|
||||
echolog "chinadns-ng tunnel error.restart!"
|
||||
@@ -218,7 +237,7 @@ while [ "1" == "1" ]; do #死循环
|
||||
"wan") chinadns="$wandns" ;;
|
||||
""|"wan_114") chinadns="$wandns,114.114.114.114" ;;
|
||||
esac
|
||||
kill -9 $(ps_list | grep $TMP_BIN_PATH/chinadns-ng | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
kill -9 $(busybox ps -w | grep $TMP_BIN_PATH/chinadns-ng | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
ln_start_bin $(first_type chinadns-ng) chinadns-ng -l $china_dns_port -4 china -p 3 -c ${chinadns/:/#} -t 127.0.0.1#$dns_port -N -f -r
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -85,14 +85,13 @@ usage() {
|
||||
-e <extra_options> extra options 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
|
||||
-y disable all auxiliary UDP rules when UDP
|
||||
transparent proxy is unavailable
|
||||
-f flush the rules
|
||||
-u enable udprelay mode, TPROXY is required
|
||||
-U enable udprelay mode, using different IP
|
||||
and ports for TCP and UDP
|
||||
-f flush the rules
|
||||
-g gfwlist mode
|
||||
-r router mode
|
||||
-c oversea mode
|
||||
-z all mode
|
||||
|
||||
# New persistence management options (use different letters to avoid conflicts)
|
||||
@@ -275,7 +274,7 @@ flush_nftables() {
|
||||
|
||||
# Optional: force delete all ss_spec related sets (even if table was accidentally deleted)
|
||||
for setname in ss_spec_lan_ac ss_spec_wan_ac ssr_gen_router \
|
||||
china fplan bplan gmlan whitelist blacklist gfwlist music; do
|
||||
china fplan bplan gmlan oversea whitelist blacklist netflix gfwlist music; do
|
||||
$NFT delete set inet ss_spec $setname 2>/dev/null
|
||||
$NFT delete set ip ss_spec_mangle $setname 2>/dev/null
|
||||
done
|
||||
@@ -311,7 +310,7 @@ flush_iptables_legacy() {
|
||||
ip route del local 0.0.0.0/0 dev lo table 999 2>/dev/null
|
||||
fi
|
||||
for setname in ss_spec_lan_ac ss_spec_wan_ac ssr_gen_router \
|
||||
china fplan bplan gmlan whitelist blacklist gfwlist music; do
|
||||
china fplan bplan gmlan oversea whitelist blacklist netflix gfwlist music; do
|
||||
ipset -X $setname 2>/dev/null
|
||||
done
|
||||
[ -n "$FWI" ] && echo '#!/bin/sh' >$FWI
|
||||
@@ -334,7 +333,7 @@ ipset_nft() {
|
||||
fi
|
||||
|
||||
# Create necessary collections
|
||||
for setname in china gmlan fplan bplan whitelist blacklist music; do
|
||||
for setname in china gmlan fplan bplan whitelist blacklist netflix music; do
|
||||
if ! $NFT list set inet ss_spec $setname >/dev/null 2>&1; then
|
||||
$NFT add set inet ss_spec $setname '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
||||
else
|
||||
@@ -393,6 +392,13 @@ ipset_nft() {
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac meta l4proto tcp ip daddr @music return
|
||||
fi
|
||||
|
||||
# Shunt/Netflix rules
|
||||
if [ -f "$SHUNT_LIST" ]; then
|
||||
for ip in $(cat "$SHUNT_LIST" 2>/dev/null); do
|
||||
[ -n "$ip" ] && $NFT add element inet ss_spec netflix "{ $ip }" 2>/dev/null
|
||||
done
|
||||
fi
|
||||
|
||||
# Set up mode-specific rules
|
||||
case "$RUNMODE" in
|
||||
router)
|
||||
@@ -421,6 +427,14 @@ ipset_nft() {
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @gfwlist jump ss_spec_wan_fw
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan ip daddr != @china jump ss_spec_wan_fw
|
||||
;;
|
||||
oversea)
|
||||
if ! $NFT list set inet ss_spec oversea >/dev/null 2>&1; then
|
||||
$NFT add set inet ss_spec oversea '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
||||
fi
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @oversea jump ss_spec_wan_fw
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan jump ss_spec_wan_fw
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @china jump ss_spec_wan_fw
|
||||
;;
|
||||
all)
|
||||
if $NFT list chain inet ss_spec ss_spec_wan_fw >/dev/null 2>&1; then
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac jump ss_spec_wan_fw
|
||||
@@ -436,10 +450,9 @@ ipset_iptables() {
|
||||
|
||||
$IPT -N SS_SPEC_WAN_AC 2>/dev/null
|
||||
$IPT -F SS_SPEC_WAN_AC
|
||||
$IPT -I SS_SPEC_WAN_AC -m mark --mark 255 -j RETURN
|
||||
|
||||
$IPT -I SS_SPEC_WAN_AC -p tcp --dport 53 -d 127.0.0.0/8 -j RETURN
|
||||
[ -n "$server" ] && $IPT -I SS_SPEC_WAN_AC -p tcp ! --dport 53 -d "$server" -j RETURN
|
||||
$IPT -I SS_SPEC_WAN_AC -p tcp ! --dport 53 -d "$server" -j RETURN
|
||||
|
||||
ipset -N gmlan hash:net 2>/dev/null
|
||||
for ip in $LAN_GM_IP; do ipset -! add gmlan "$ip"; done
|
||||
@@ -461,6 +474,12 @@ ipset_iptables() {
|
||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set gfwlist dst -j SS_SPEC_WAN_FW
|
||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
|
||||
;;
|
||||
oversea)
|
||||
ipset -N oversea hash:net 2>/dev/null
|
||||
$IPT -I SS_SPEC_WAN_AC -m set --match-set oversea dst -j SS_SPEC_WAN_FW
|
||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -j SS_SPEC_WAN_FW
|
||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j SS_SPEC_WAN_FW
|
||||
;;
|
||||
all)
|
||||
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
|
||||
;;
|
||||
@@ -491,6 +510,25 @@ ipset_iptables() {
|
||||
|
||||
for ip in $WAN_BP_IP; do ipset -! add whitelist "$ip"; done
|
||||
for ip in $WAN_FW_IP; do ipset -! add blacklist "$ip"; done
|
||||
|
||||
if [ "$SHUNT_PORT" != "0" ]; then
|
||||
ipset -N netflix hash:net 2>/dev/null
|
||||
for ip in $(cat "${SHUNT_LIST:=/dev/null}" 2>/dev/null); do ipset -! add netflix "$ip"; done
|
||||
case "$SHUNT_PORT" in
|
||||
0) ;;
|
||||
1)
|
||||
$IPT -I SS_SPEC_WAN_AC -p tcp -m set --match-set netflix dst -j REDIRECT --to-ports "$local_port"
|
||||
;;
|
||||
*)
|
||||
$IPT -I SS_SPEC_WAN_AC -p tcp -m set --match-set netflix dst -j REDIRECT --to-ports "$SHUNT_PORT"
|
||||
if [ "$SHUNT_PROXY" = "1" ]; then
|
||||
$IPT -I SS_SPEC_WAN_AC -p tcp -d "$SHUNT_IP" -j REDIRECT --to-ports "$local_port"
|
||||
else
|
||||
ipset -! add whitelist "$SHUNT_IP"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
return $?
|
||||
}
|
||||
|
||||
@@ -524,6 +562,22 @@ fw_rule_nft() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$SHUNT_PORT" != "0" ] && [ -f "$SHUNT_LIST" ]; then
|
||||
case "$SHUNT_PORT" in
|
||||
1)
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac $TCP_EXT_ARGS ip daddr @netflix counter redirect to :$local_port
|
||||
;;
|
||||
*)
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac $TCP_EXT_ARGS ip daddr @netflix counter redirect to :$SHUNT_PORT
|
||||
if [ "$SHUNT_PROXY" = "1" ]; then
|
||||
$NFT add rule inet ss_spec ss_spec_wan_ac $TCP_EXT_ARGS ip daddr $SHUNT_IP counter redirect to :$local_port
|
||||
else
|
||||
[ -n "$SHUNT_IP" ] && $NFT add element inet ss_spec whitelist "{ $SHUNT_IP }" 2>/dev/null
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
return $?
|
||||
}
|
||||
|
||||
@@ -616,7 +670,7 @@ ac_rule_nft() {
|
||||
fi
|
||||
|
||||
# Block UDP port 443 when TPROXY not Enable
|
||||
if [ -z "$TPROXY" ] && [ "$DISABLE_UDP_RULES" != "1" ]; then
|
||||
if [ -z "$TPROXY" ]; then
|
||||
# Add UDP 443 block rule
|
||||
if [ -z "$Interface" ]; then
|
||||
if [ -n "$MATCH_SET" ]; then
|
||||
@@ -663,11 +717,10 @@ ac_rule_nft() {
|
||||
case "$OUTPUT" in
|
||||
1)
|
||||
# Create ss_spec_output tcp chain
|
||||
if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
||||
$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }'
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_output meta mark 255 return 2>/dev/null
|
||||
if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
||||
$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }'
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
|
||||
|
||||
# Exclude special local addresses
|
||||
if $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
||||
@@ -688,11 +741,10 @@ ac_rule_nft() {
|
||||
;;
|
||||
2)
|
||||
# Create ss_spec_output tcp chain
|
||||
if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
||||
$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }'
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_output meta mark 255 return 2>/dev/null
|
||||
if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
||||
$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }'
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
|
||||
|
||||
# Exclude special local addresses
|
||||
if $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
||||
@@ -706,12 +758,11 @@ ac_rule_nft() {
|
||||
for ip in $(gen_spec_iplist); do
|
||||
[ -n "$ip" ] && $NFT add element inet ss_spec ssr_gen_router "{ $ip }" 2>/dev/null
|
||||
done
|
||||
if ! $NFT list chain inet ss_spec ss_spec_router >/dev/null 2>&1; then
|
||||
$NFT add chain inet ss_spec ss_spec_router 2>/dev/null
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_router 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_router meta mark 255 return 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_router ip daddr @ssr_gen_router return 2>/dev/null
|
||||
if ! $NFT list chain inet ss_spec ss_spec_router >/dev/null 2>&1; then
|
||||
$NFT add chain inet ss_spec ss_spec_router 2>/dev/null
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_router 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_router ip daddr @ssr_gen_router return 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_router jump ss_spec_wan_fw 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_output $TCP_EXT_ARGS jump ss_spec_router comment "\"$TAG\"" 2>/dev/null
|
||||
;;
|
||||
@@ -741,7 +792,7 @@ ac_rule_iptables() {
|
||||
EOF
|
||||
|
||||
# Block UDP port 443 when TPROXY not Enable
|
||||
if [ -z "$TPROXY" ] && [ "$DISABLE_UDP_RULES" != "1" ]; then
|
||||
if [ -z "$TPROXY" ]; then
|
||||
# Add UDP 443 block rule
|
||||
if [ -z "$Interface" ]; then
|
||||
# Global rules
|
||||
@@ -794,11 +845,10 @@ ac_rule_iptables() {
|
||||
create ssr_gen_router hash:net
|
||||
$(gen_spec_iplist | sed -e "s/^/add ssr_gen_router /")
|
||||
EOF
|
||||
$IPT -N SS_SPEC_ROUTER 2>/dev/null
|
||||
$IPT -F SS_SPEC_ROUTER 2>/dev/null
|
||||
$IPT -A SS_SPEC_ROUTER -m mark --mark 255 -j RETURN && \
|
||||
$IPT -A SS_SPEC_ROUTER -m set --match-set ssr_gen_router dst -j RETURN && \
|
||||
$IPT -A SS_SPEC_ROUTER -j SS_SPEC_WAN_FW
|
||||
$IPT -N SS_SPEC_ROUTER 2>/dev/null
|
||||
$IPT -F SS_SPEC_ROUTER 2>/dev/null
|
||||
$IPT -A SS_SPEC_ROUTER -m set --match-set ssr_gen_router dst -j RETURN && \
|
||||
$IPT -A SS_SPEC_ROUTER -j SS_SPEC_WAN_FW
|
||||
$IPT -I OUTPUT 1 -p tcp -m comment --comment "$TAG" -j SS_SPEC_ROUTER
|
||||
;;
|
||||
esac
|
||||
@@ -956,6 +1006,14 @@ tp_rule_nft() {
|
||||
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip daddr @gfwlist counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null
|
||||
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan ip daddr != @china counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null
|
||||
;;
|
||||
oversea)
|
||||
if ! $NFT list set ip ss_spec_mangle oversea >/dev/null 2>&1; then
|
||||
$NFT add set ip ss_spec_mangle oversea '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
||||
fi
|
||||
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip saddr @oversea counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null
|
||||
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip daddr @china counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null
|
||||
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null
|
||||
;;
|
||||
all)
|
||||
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null
|
||||
;;
|
||||
@@ -1032,8 +1090,8 @@ tp_rule_iptables() {
|
||||
do
|
||||
$ipt -A SS_SPEC_TPROXY -p udp -d "$net" -j RETURN
|
||||
done
|
||||
[ -n "$SERVER" ] && $ipt -A SS_SPEC_TPROXY -p udp ! --dport 53 -d "$SERVER" -j RETURN
|
||||
[ -n "$SERVER" ] && [ "$server" != "$SERVER" ] && ipset -! add whitelist "$SERVER"
|
||||
$ipt -A SS_SPEC_TPROXY -p udp ! --dport 53 -d "$SERVER" -j RETURN
|
||||
[ "$server" != "$SERVER" ] && ipset -! add whitelist "$SERVER"
|
||||
if [ -f "${xhttp_ip:=/etc/ssrplus/xhttp_address.txt}" ]; then
|
||||
while IFS= read -r ip; do
|
||||
[ -n "$ip" ] && ipset add whitelist "$ip" -exist
|
||||
@@ -1061,6 +1119,11 @@ tp_rule_iptables() {
|
||||
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set gfwlist dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK}
|
||||
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK}
|
||||
;;
|
||||
oversea)
|
||||
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set oversea src -m dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK}
|
||||
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK}
|
||||
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK}
|
||||
;;
|
||||
all)
|
||||
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK}
|
||||
;;
|
||||
@@ -1432,7 +1495,7 @@ restore_from_persistence() {
|
||||
fi
|
||||
}
|
||||
|
||||
while getopts ":m:s:l:S:L:i:e:a:B:b:w:p:G:D:F:N:M:I:oOuUyfgrzAKPCRXh" arg; do
|
||||
while getopts ":m:s:l:S:L:i:e:a:B:b:w:p:G:D:F:N:M:I:oOuUfgrczAKPCRXh" arg; do
|
||||
case "$arg" in
|
||||
m)
|
||||
Interface=$OPTARG
|
||||
@@ -1500,15 +1563,15 @@ while getopts ":m:s:l:S:L:i:e:a:B:b:w:p:G:D:F:N:M:I:oOuUyfgrzAKPCRXh" arg; do
|
||||
U)
|
||||
TPROXY=2
|
||||
;;
|
||||
y)
|
||||
DISABLE_UDP_RULES=1
|
||||
;;
|
||||
g)
|
||||
RUNMODE=gfw
|
||||
;;
|
||||
r)
|
||||
RUNMODE=router
|
||||
;;
|
||||
c)
|
||||
RUNMODE=oversea
|
||||
;;
|
||||
z)
|
||||
RUNMODE=all
|
||||
;;
|
||||
@@ -1640,7 +1703,7 @@ runmode_change() {
|
||||
}
|
||||
|
||||
# Main process
|
||||
if [ -n "$local_port" ]; then
|
||||
if [ -n "$server" ] && [ -n "$local_port" ]; then
|
||||
if ! echo "$local_port" | grep -qE '^[0-9]+$'; then
|
||||
loger 3 "Invalid local port: $local_port"
|
||||
exit 1
|
||||
@@ -1652,6 +1715,7 @@ if [ -n "$local_port" ]; then
|
||||
LOCAL_PORT=$local_port
|
||||
;;
|
||||
2)
|
||||
: ${SERVER:?"You must assign an ip for the udp relay server."}
|
||||
: ${LOCAL_PORT:?"You must assign a port for the udp relay server."}
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/sh /etc/rc.common
|
||||
#
|
||||
# Copyright (C) 2017 openwrt-ssr
|
||||
# Copyright (C) 2017 yushi studio <ywb94@qq.com>
|
||||
@@ -7,276 +7,149 @@
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
ORIG_ARG1="${1:-}"
|
||||
ORIG_ARG2="${2:-}"
|
||||
ORIG_ARG3="${3:-}"
|
||||
[ -f /lib/functions.sh ] && . /lib/functions.sh
|
||||
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
|
||||
LOCK_FILE="/var/lock/ssr-switch.lock"
|
||||
PROBE_SID=""
|
||||
[ -f "$LOCK_FILE" ] && exit 2
|
||||
touch "$LOCK_FILE"
|
||||
LOG_FILE=/var/log/ssrplus.log
|
||||
|
||||
cycle_time=60
|
||||
switch_time=3
|
||||
probe_socks_port=10800
|
||||
direct_fail_count=0
|
||||
direct_fail_limit=3
|
||||
probe_url="$(uci_get_by_type server_subscribe url_test_url)"
|
||||
[ -n "$probe_url" ] || probe_url="https://www.google.com/generate_204"
|
||||
PROBE_INSTANCE_KEY="ssr-switch-probe-local"
|
||||
normal_flag=0
|
||||
server_locate=0
|
||||
server_count=0
|
||||
ENABLE_SERVER=nil
|
||||
[ -n "$1" ] && cycle_time=$1
|
||||
[ -n "$2" ] && switch_time=$2
|
||||
DEFAULT_SERVER=$(uci_get_by_type global global_server)
|
||||
CURRENT_SERVER=$DEFAULT_SERVER
|
||||
|
||||
DEFAULT_SERVER="$(uci_get_by_type global global_server)"
|
||||
CURRENT_SERVER="$DEFAULT_SERVER"
|
||||
ENABLE_SERVER="nil"
|
||||
|
||||
cleanup_probe_socks() {
|
||||
ps_list | grep -v "grep" | grep -E "tcp-http-ssr-switch-local\\.json|ss-${PROBE_INSTANCE_KEY}/config\\.yaml|tuic-${PROBE_INSTANCE_KEY}/config\\.yaml" | awk '{print $1}' | xargs kill >/dev/null 2>&1
|
||||
sleep 1
|
||||
ps_list | grep -v "grep" | grep -E "tcp-http-ssr-switch-local\\.json|ss-${PROBE_INSTANCE_KEY}/config\\.yaml|tuic-${PROBE_INSTANCE_KEY}/config\\.yaml" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1
|
||||
rm -f "$TMP_PATH/tcp-http-ssr-switch-local.json" 2>/dev/null
|
||||
rm -rf "$TMP_PATH/ss-${PROBE_INSTANCE_KEY}" "$TMP_PATH/tuic-${PROBE_INSTANCE_KEY}" 2>/dev/null
|
||||
rm -f /tmp/ssr-switch-probe.log 2>/dev/null
|
||||
}
|
||||
|
||||
start_probe_socks() {
|
||||
local sid="$1"
|
||||
local server_type
|
||||
|
||||
server_type="$(uci_get_by_name "$sid" type)"
|
||||
[ "$server_type" = "clash" ] && return 1
|
||||
[ "$server_type" = "tuic" ] && return 1
|
||||
[ "$server_type" = "socks5" ] && return 1
|
||||
|
||||
cleanup_probe_socks
|
||||
LOCAL_SERVER="$sid"
|
||||
local_config_file="$TMP_PATH/tcp-http-ssr-switch-local.json"
|
||||
_local="2"
|
||||
local_enable=0
|
||||
tmp_local_port=
|
||||
mode="tcp,udp"
|
||||
|
||||
if [ "$server_type" = "v2ray" ]; then
|
||||
export SSR_SWITCH_PROBE=1
|
||||
gen_config_file "$LOCAL_SERVER" "$server_type" 4 0 "$probe_socks_port" >/tmp/ssr-switch-probe.log 2>&1 || return 1
|
||||
unset SSR_SWITCH_PROBE
|
||||
local xray_bin
|
||||
xray_bin="$(first_type xray)"
|
||||
[ -x "$xray_bin" ] || return 1
|
||||
"$xray_bin" run -c "$local_config_file" >>/tmp/ssr-switch-probe.log 2>&1 &
|
||||
else
|
||||
export SSR_SWITCH_PROBE=1
|
||||
export SSR_SWITCH_PROBE_INSTANCE_KEY="$PROBE_INSTANCE_KEY"
|
||||
start_local_with_port "$probe_socks_port" >/tmp/ssr-switch-probe.log 2>&1 || return 1
|
||||
unset SSR_SWITCH_PROBE_INSTANCE_KEY
|
||||
unset SSR_SWITCH_PROBE
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
return 0
|
||||
}
|
||||
|
||||
probe_http_ok() {
|
||||
case "$1" in
|
||||
[1-5][0-9][0-9])
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
check_direct_probe() {
|
||||
local code
|
||||
|
||||
code="$(curl -k -sS -L -o /dev/null \
|
||||
--connect-timeout "$switch_time" \
|
||||
--max-time "$switch_time" \
|
||||
-w '%{http_code}' \
|
||||
"$probe_url" 2>/dev/null || true)"
|
||||
probe_http_ok "$code"
|
||||
}
|
||||
|
||||
check_proxy_via_socks() {
|
||||
local try_count
|
||||
local code
|
||||
local proxy_host
|
||||
|
||||
try_count="$(uci_get_by_type global switch_try_count 3)"
|
||||
for i in $(seq 1 "$try_count"); do
|
||||
for proxy_host in "127.0.0.1:$probe_socks_port" "[::1]:$probe_socks_port"; do
|
||||
code="$(curl -k -sS -L -o /dev/null \
|
||||
--connect-timeout "$switch_time" \
|
||||
--max-time "$switch_time" \
|
||||
--socks5-hostname "$proxy_host" \
|
||||
-w '%{http_code}' \
|
||||
"$probe_url" 2>/dev/null || true)"
|
||||
probe_http_ok "$code" && return 0
|
||||
done
|
||||
#判断代理是否正常
|
||||
check_proxy() {
|
||||
local result=0
|
||||
local try_count=$(uci_get_by_type global switch_try_count 3)
|
||||
for i in $(seq 1 $try_count); do
|
||||
/usr/bin/ssr-check www.google.com 80 $switch_time 1
|
||||
if [ "$?" == "0" ]; then
|
||||
# echolog "Check Google Proxy Success, count=$i"
|
||||
result=0
|
||||
break
|
||||
else
|
||||
# echolog "Check Google Proxy Fail, count=$i"
|
||||
/usr/bin/ssr-check www.baidu.com 80 $switch_time 1
|
||||
if [ "$?" == "0" ]; then
|
||||
result=1
|
||||
else
|
||||
result=2
|
||||
fi
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
return 1
|
||||
return $result
|
||||
}
|
||||
|
||||
test_proxy() {
|
||||
local sid="$1"
|
||||
[ "$(uci_get_by_name "$sid" type)" = "clash" ] && return 1
|
||||
[ "$(uci_get_by_name "$sid" type)" = "tuic" ] && return 1
|
||||
start_probe_socks "$sid" || {
|
||||
cleanup_probe_socks
|
||||
local servername=$(uci_get_by_name $1 server)
|
||||
local serverport=$(uci_get_by_name $1 server_port)
|
||||
ipset add whitelist $servername 2>/dev/null
|
||||
tcping -q -c 3 -i 1 -t 2 -p $serverport $servername
|
||||
if [ "$?" -gt "0" ]; then
|
||||
ipset del whitelist $servername 2>/dev/null
|
||||
return 1
|
||||
}
|
||||
check_proxy_via_socks
|
||||
fi
|
||||
/usr/bin/ssr-check $servername $serverport $switch_time
|
||||
local ret=$?
|
||||
cleanup_probe_socks
|
||||
return $ret
|
||||
ipset del whitelist $servername 2>/dev/null
|
||||
if [ "$ret" == "0" ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
search_proxy() {
|
||||
local sid="$1"
|
||||
[ "$(uci_get_by_name "$sid" switch_enable 0)" = "1" ] || return 1
|
||||
[ "$(uci_get_by_name "$sid" type)" = "clash" ] && return 1
|
||||
[ "$(uci_get_by_name "$sid" type)" = "tuic" ] && return 1
|
||||
[ "$sid" = "$CURRENT_SERVER" ] && return 1
|
||||
[ "$ENABLE_SERVER" != "nil" ] && return 0
|
||||
|
||||
if test_proxy "$sid"; then
|
||||
ENABLE_SERVER="$sid"
|
||||
let server_count=server_count+1
|
||||
[ "$normal_flag" == "1" -a "$server_count" -le "$server_locate" ] && return 0
|
||||
[ "$(uci_get_by_name $1 switch_enable 0)" != "1" ] && return 1
|
||||
[ $ENABLE_SERVER != nil ] && return 0
|
||||
[ "$1" == "$CURRENT_SERVER" ] && return 0
|
||||
local servername=$(uci_get_by_name $1 server)
|
||||
local serverport=$(uci_get_by_name $1 server_port)
|
||||
ipset add whitelist $servername 2>/dev/null
|
||||
/usr/bin/ssr-check $servername $serverport $switch_time
|
||||
local ret=$?
|
||||
ipset del whitelist $servername 2>/dev/null
|
||||
if [ "$ret" == "0" ]; then
|
||||
server_locate=$server_count
|
||||
ENABLE_SERVER=$1
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#选择可用的代理
|
||||
select_proxy() {
|
||||
config_load "$NAME"
|
||||
ENABLE_SERVER="nil"
|
||||
config_load $NAME
|
||||
ENABLE_SERVER=nil
|
||||
mkdir -p /var/run /var/etc
|
||||
server_count=0
|
||||
config_foreach search_proxy servers
|
||||
}
|
||||
|
||||
set_main_server() {
|
||||
local sid="$1"
|
||||
[ -n "$sid" ] || return 1
|
||||
uci set shadowsocksr.@global[0].global_server="$sid"
|
||||
uci commit shadowsocksr
|
||||
return 0
|
||||
}
|
||||
|
||||
#切换代理
|
||||
switch_proxy() {
|
||||
set_main_server "$1" || return 1
|
||||
/etc/init.d/shadowsocksr restart "$1"
|
||||
/etc/init.d/shadowsocksr restart $1
|
||||
return 0
|
||||
}
|
||||
|
||||
start() {
|
||||
while [ "1" = "1" ]; do
|
||||
sleep "0000$cycle_time"
|
||||
run_once
|
||||
#不支持kcptun启用时的切换
|
||||
[ $(uci_get_by_name $DEFAULT_SERVER kcp_enable) = "1" ] && return 1
|
||||
while [ "1" == "1" ]; do #死循环
|
||||
sleep 0000$cycle_time
|
||||
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
|
||||
#判断当前代理是否为缺省服务器
|
||||
if [ "$CURRENT_SERVER" != "$DEFAULT_SERVER" ]; then
|
||||
#echo "not default proxy"
|
||||
echolog "Current server is not default Main server, try to switch back."
|
||||
#检查缺省服务器是否正常
|
||||
if test_proxy $DEFAULT_SERVER; then
|
||||
#echo "switch to default proxy"
|
||||
echolog "Main server is avilable."
|
||||
#缺省服务器正常,切换回来
|
||||
CURRENT_SERVER=$DEFAULT_SERVER
|
||||
switch_proxy $CURRENT_SERVER
|
||||
echolog "switch to default "$(uci_get_by_name $CURRENT_SERVER alias)" proxy!"
|
||||
else
|
||||
echolog "Main server is NOT avilable.Continue using current server."
|
||||
fi
|
||||
fi
|
||||
#判断当前代理是否正常
|
||||
#echolog "Start checking if the current server is available."
|
||||
check_proxy
|
||||
current_ret=$?
|
||||
if [ "$current_ret" == "1" ]; then
|
||||
#当前代理错误,判断有无可用的服务器
|
||||
#echo "current error"
|
||||
echolog "Current server error, try to switch another server."
|
||||
select_proxy
|
||||
if [ "$ENABLE_SERVER" != nil ]; then
|
||||
#有其他服务器可用,进行切换
|
||||
#echo $(uci_get_by_name $new_proxy server)
|
||||
echolog "Another server is avilable, now switching server."
|
||||
CURRENT_SERVER=$ENABLE_SERVER
|
||||
switch_proxy $CURRENT_SERVER
|
||||
normal_flag=1
|
||||
echolog "Switch to "$(uci_get_by_name $CURRENT_SERVER alias)" proxy!"
|
||||
else
|
||||
switch_proxy $CURRENT_SERVER
|
||||
normal_flag=1
|
||||
echolog "Try restart current server."
|
||||
fi
|
||||
else
|
||||
normal_flag=0
|
||||
# echolog "ShadowsocksR No Problem."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
run_once() {
|
||||
DEFAULT_SERVER="$(uci_get_by_type global global_server)"
|
||||
[ "$DEFAULT_SERVER" = "nil" ] && {
|
||||
direct_fail_count=0
|
||||
return 0
|
||||
}
|
||||
[ "$(uci_get_by_name "$DEFAULT_SERVER" type)" = "clash" ] && {
|
||||
direct_fail_count=0
|
||||
cleanup_probe_socks
|
||||
return 0
|
||||
}
|
||||
[ "$(uci_get_by_name "$DEFAULT_SERVER" type)" = "tuic" ] && {
|
||||
direct_fail_count=0
|
||||
cleanup_probe_socks
|
||||
return 0
|
||||
}
|
||||
[ "$(uci_get_by_name "$DEFAULT_SERVER" kcp_enable 0)" = "1" ] && {
|
||||
direct_fail_count=0
|
||||
cleanup_probe_socks
|
||||
return 1
|
||||
}
|
||||
|
||||
CURRENT_SERVER="$DEFAULT_SERVER"
|
||||
|
||||
if check_direct_probe; then
|
||||
direct_fail_count=0
|
||||
return 0
|
||||
fi
|
||||
|
||||
direct_fail_count=$((direct_fail_count + 1))
|
||||
[ "$direct_fail_count" -lt "$direct_fail_limit" ] && return 0
|
||||
direct_fail_count=0
|
||||
|
||||
if test_proxy "$CURRENT_SERVER"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echolog "Current server error, try to switch another server."
|
||||
select_proxy
|
||||
if [ "$ENABLE_SERVER" != "nil" ]; then
|
||||
CURRENT_SERVER="$ENABLE_SERVER"
|
||||
switch_proxy "$CURRENT_SERVER"
|
||||
echolog "Switch to $(uci_get_by_name "$CURRENT_SERVER" alias) proxy!"
|
||||
else
|
||||
switch_proxy "$CURRENT_SERVER"
|
||||
echolog "Try restart current server."
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
probe_once() {
|
||||
local sid="$1"
|
||||
[ -n "$sid" ] || {
|
||||
echo "missing_sid"
|
||||
return 1
|
||||
}
|
||||
cleanup_probe_socks
|
||||
if ! start_probe_socks "$sid"; then
|
||||
echo "probe_start:fail"
|
||||
return 1
|
||||
fi
|
||||
echo "probe_start:ok"
|
||||
echo "probe:curl_ipv4"
|
||||
curl -k -sS -L -o /dev/null \
|
||||
--connect-timeout "$switch_time" \
|
||||
--max-time "$switch_time" \
|
||||
--socks5-hostname "127.0.0.1:$probe_socks_port" \
|
||||
-w 'code=%{http_code}\n' \
|
||||
"$probe_url" 2>/dev/null || true
|
||||
echo "probe:curl_ipv6"
|
||||
curl -g -k -sS -L -o /dev/null \
|
||||
--connect-timeout "$switch_time" \
|
||||
--max-time "$switch_time" \
|
||||
--socks5-hostname "[::1]:$probe_socks_port" \
|
||||
-w 'code=%{http_code}\n' \
|
||||
"$probe_url" 2>/dev/null || true
|
||||
cleanup_probe_socks
|
||||
return 0
|
||||
}
|
||||
|
||||
main() {
|
||||
case "${ORIG_ARG1:-}" in
|
||||
probe)
|
||||
PROBE_SID="${ORIG_ARG2:-}"
|
||||
probe_once "$PROBE_SID"
|
||||
;;
|
||||
once)
|
||||
run_once
|
||||
;;
|
||||
start)
|
||||
[ -n "$ORIG_ARG2" ] && cycle_time="$ORIG_ARG2"
|
||||
[ -n "$ORIG_ARG3" ] && switch_time="$ORIG_ARG3"
|
||||
[ -f "$LOCK_FILE" ] && exit 2
|
||||
touch "$LOCK_FILE"
|
||||
start
|
||||
;;
|
||||
*)
|
||||
[ -n "$ORIG_ARG1" ] && cycle_time="$ORIG_ARG1"
|
||||
[ -n "$ORIG_ARG2" ] && switch_time="$ORIG_ARG2"
|
||||
[ -f "$LOCK_FILE" ] && exit 2
|
||||
touch "$LOCK_FILE"
|
||||
start
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ "${0##*/}" = "ssr-switch" ]; then
|
||||
main "$@"
|
||||
fi
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 215 KiB |
@@ -1,939 +0,0 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
require "nixio.fs"
|
||||
require "luci.model.uci"
|
||||
|
||||
local ok_lyaml, lyaml = pcall(require, "lyaml")
|
||||
if not ok_lyaml then
|
||||
io.stderr:write("lyaml_not_found\n")
|
||||
os.exit(2)
|
||||
end
|
||||
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local ok_jsonc, jsonc = pcall(require, "luci.jsonc")
|
||||
|
||||
local function read_file(path)
|
||||
local data = nixio.fs.readfile(path)
|
||||
if not data or data == "" then
|
||||
return nil
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
local function write_file(path, data)
|
||||
return nixio.fs.writefile(path, data)
|
||||
end
|
||||
|
||||
local function load_yaml(path)
|
||||
local raw = read_file(path)
|
||||
if not raw then
|
||||
return nil, "read_failed"
|
||||
end
|
||||
|
||||
local ok, parsed = pcall(lyaml.load, raw)
|
||||
if not ok or type(parsed) ~= "table" then
|
||||
return nil, "parse_failed"
|
||||
end
|
||||
|
||||
return parsed
|
||||
end
|
||||
|
||||
local function dump_yaml(path, data)
|
||||
local ok, rendered = pcall(lyaml.dump, { data })
|
||||
if not ok or not rendered then
|
||||
return nil, "dump_failed"
|
||||
end
|
||||
|
||||
write_file(path, rendered)
|
||||
return true
|
||||
end
|
||||
|
||||
local function split_filter_words(text)
|
||||
local items = {}
|
||||
for part in tostring(text or ""):gmatch("[^/]+") do
|
||||
if part ~= "" then
|
||||
items[#items + 1] = part
|
||||
end
|
||||
end
|
||||
return items
|
||||
end
|
||||
|
||||
local function trim(value)
|
||||
return tostring(value or ""):gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
|
||||
local function parse_csv_line(line)
|
||||
local cols = {}
|
||||
local cur = ""
|
||||
local in_quote = false
|
||||
local i = 1
|
||||
|
||||
while i <= #line do
|
||||
local ch = line:sub(i, i)
|
||||
if ch == '"' then
|
||||
if in_quote and line:sub(i + 1, i + 1) == '"' then
|
||||
cur = cur .. '"'
|
||||
i = i + 1
|
||||
else
|
||||
in_quote = not in_quote
|
||||
end
|
||||
elseif ch == "," and not in_quote then
|
||||
cols[#cols + 1] = cur
|
||||
cur = ""
|
||||
else
|
||||
cur = cur .. ch
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
cols[#cols + 1] = cur
|
||||
return cols
|
||||
end
|
||||
|
||||
local function read_clash_client_rules_csv(sid)
|
||||
local rows = {}
|
||||
sid = trim(sid)
|
||||
if sid == "" then
|
||||
return rows
|
||||
end
|
||||
|
||||
local csv_path = string.format("/etc/ssrplus/clash/%s.csv", sid)
|
||||
local raw = read_file(csv_path)
|
||||
if not raw or raw == "" then
|
||||
return rows
|
||||
end
|
||||
|
||||
local first = true
|
||||
for line in tostring(raw):gsub("\r", ""):gmatch("[^\n]+") do
|
||||
local text = trim(line)
|
||||
if text ~= "" then
|
||||
if first and text:lower() == "enabled,client,policy,remarks,client_mac" then
|
||||
first = false
|
||||
else
|
||||
local cols = parse_csv_line(line)
|
||||
if #cols >= 4 then
|
||||
rows[#rows + 1] = {
|
||||
enabled = cols[1],
|
||||
ip_addr = trim(cols[2] or ""),
|
||||
policy_group = trim(cols[3] or ""),
|
||||
remarks = trim(cols[4] or ""),
|
||||
client_mac = trim(cols[5] or "")
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return rows
|
||||
end
|
||||
|
||||
local function has_proxy_sections(doc)
|
||||
return type(doc.proxies) == "table" or type(doc["proxy-providers"]) == "table"
|
||||
end
|
||||
|
||||
local function validate(path)
|
||||
local doc = load_yaml(path)
|
||||
if not doc then
|
||||
return false
|
||||
end
|
||||
return has_proxy_sections(doc)
|
||||
end
|
||||
|
||||
local function filter(path, filter_words)
|
||||
local doc, err = load_yaml(path)
|
||||
if not doc then
|
||||
io.stderr:write(err or "parse_failed", "\n")
|
||||
return false
|
||||
end
|
||||
|
||||
local words = split_filter_words(filter_words)
|
||||
if #words == 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
local removed = {}
|
||||
local proxies = {}
|
||||
for _, proxy in ipairs(doc.proxies or {}) do
|
||||
local name = tostring(proxy.name or "")
|
||||
local matched = false
|
||||
for _, word in ipairs(words) do
|
||||
if name:find(word, 1, true) then
|
||||
matched = true
|
||||
removed[name] = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not matched then
|
||||
proxies[#proxies + 1] = proxy
|
||||
end
|
||||
end
|
||||
doc.proxies = proxies
|
||||
|
||||
for _, group in ipairs(doc["proxy-groups"] or {}) do
|
||||
if type(group.proxies) == "table" then
|
||||
local kept = {}
|
||||
for _, name in ipairs(group.proxies) do
|
||||
if not removed[tostring(name)] then
|
||||
kept[#kept + 1] = name
|
||||
end
|
||||
end
|
||||
group.proxies = kept
|
||||
end
|
||||
end
|
||||
|
||||
local count = 0
|
||||
for _ in pairs(removed) do
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
dump_yaml(path, doc)
|
||||
io.stdout:write(tostring(count), "\n")
|
||||
return true
|
||||
end
|
||||
|
||||
local function deep_merge(dst, src)
|
||||
if type(dst) ~= "table" or type(src) ~= "table" then
|
||||
return src
|
||||
end
|
||||
|
||||
for k, v in pairs(src) do
|
||||
if type(v) == "table" and type(dst[k]) == "table" then
|
||||
dst[k] = deep_merge(dst[k], v)
|
||||
else
|
||||
dst[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
return dst
|
||||
end
|
||||
|
||||
local function strip_runtime_conflicts(doc)
|
||||
doc.tun = nil
|
||||
doc.listeners = nil
|
||||
doc["redir-port"] = nil
|
||||
doc["tproxy-port"] = nil
|
||||
doc["socks-port"] = nil
|
||||
doc["mixed-port"] = nil
|
||||
doc.port = nil
|
||||
doc["external-controller"] = nil
|
||||
doc.secret = nil
|
||||
doc["allow-lan"] = nil
|
||||
if type(doc.dns) == "table" then
|
||||
doc.dns["fake-ip-range"] = nil
|
||||
doc.dns["fake-ip-filter"] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function group_requires_candidates(group)
|
||||
local gtype = tostring(group and group.type or ""):lower()
|
||||
return gtype == "select"
|
||||
or gtype == "fallback"
|
||||
or gtype == "load-balance"
|
||||
or gtype == "url-test"
|
||||
or gtype == "relay"
|
||||
end
|
||||
|
||||
local function has_nonempty_sequence(value)
|
||||
return type(value) == "table" and next(value) ~= nil
|
||||
end
|
||||
|
||||
local function fill_empty_proxy_groups(doc)
|
||||
local changed = 0
|
||||
for _, group in ipairs(doc["proxy-groups"] or {}) do
|
||||
if type(group) == "table"
|
||||
and group_requires_candidates(group)
|
||||
and not has_nonempty_sequence(group.proxies)
|
||||
and not has_nonempty_sequence(group.use)
|
||||
then
|
||||
group.proxies = { "DIRECT" }
|
||||
changed = changed + 1
|
||||
end
|
||||
end
|
||||
return changed
|
||||
end
|
||||
|
||||
local function strip_incompatible_script_rules(doc)
|
||||
local kept = {}
|
||||
local removed = 0
|
||||
local has_script_rule = false
|
||||
|
||||
for _, rule in ipairs(doc.rules or {}) do
|
||||
local text = tostring(rule or "")
|
||||
if text:match("^SCRIPT,") then
|
||||
removed = removed + 1
|
||||
else
|
||||
kept[#kept + 1] = rule
|
||||
if text:match("^SCRIPT,") then
|
||||
has_script_rule = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if removed > 0 then
|
||||
doc.rules = kept
|
||||
end
|
||||
|
||||
if not has_script_rule then
|
||||
doc.script = nil
|
||||
end
|
||||
|
||||
return removed
|
||||
end
|
||||
|
||||
local function prepare(input_path, output_path)
|
||||
local doc, err = load_yaml(input_path)
|
||||
if not doc then
|
||||
io.stderr:write(err or "parse_failed", "\n")
|
||||
return false
|
||||
end
|
||||
if not has_proxy_sections(doc) then
|
||||
io.stderr:write("missing_proxy_sections\n")
|
||||
return false
|
||||
end
|
||||
|
||||
strip_runtime_conflicts(doc)
|
||||
local filled_groups = fill_empty_proxy_groups(doc)
|
||||
local stripped_rules = strip_incompatible_script_rules(doc)
|
||||
local ok, rendered = pcall(lyaml.dump, { doc })
|
||||
if not ok or not rendered then
|
||||
io.stderr:write("dump_failed\n")
|
||||
return false
|
||||
end
|
||||
|
||||
write_file(output_path, rendered)
|
||||
io.stdout:write(string.format("filled_groups=%d stripped_script_rules=%d\n", filled_groups, stripped_rules))
|
||||
return true
|
||||
end
|
||||
|
||||
local function merge(raw_path, overlay_path, output_path)
|
||||
local raw_doc, raw_err = load_yaml(raw_path)
|
||||
if not raw_doc then
|
||||
io.stderr:write(raw_err or "parse_failed", "\n")
|
||||
return false
|
||||
end
|
||||
|
||||
local overlay_doc, overlay_err = load_yaml(overlay_path)
|
||||
if not overlay_doc then
|
||||
io.stderr:write(overlay_err or "parse_failed", "\n")
|
||||
return false
|
||||
end
|
||||
|
||||
strip_runtime_conflicts(raw_doc)
|
||||
local filled_groups = fill_empty_proxy_groups(raw_doc)
|
||||
local stripped_rules = strip_incompatible_script_rules(raw_doc)
|
||||
local merged = deep_merge(raw_doc, overlay_doc)
|
||||
local ok, rendered = pcall(lyaml.dump, { merged })
|
||||
if not ok or not rendered then
|
||||
io.stderr:write("dump_failed\n")
|
||||
return false
|
||||
end
|
||||
|
||||
write_file(output_path, rendered)
|
||||
io.stdout:write(string.format("filled_groups=%d stripped_script_rules=%d\n", filled_groups, stripped_rules))
|
||||
return true
|
||||
end
|
||||
|
||||
local function append_client_policy_rules(runtime_path, sid)
|
||||
local doc, err = load_yaml(runtime_path)
|
||||
if not doc then
|
||||
io.stderr:write(err or "parse_failed", "\n")
|
||||
return false
|
||||
end
|
||||
|
||||
local valid_policies = {}
|
||||
for _, proxy in ipairs(doc.proxies or {}) do
|
||||
if type(proxy) == "table" and proxy.name and proxy.name ~= "" then
|
||||
valid_policies[tostring(proxy.name)] = true
|
||||
end
|
||||
end
|
||||
for _, group in ipairs(doc["proxy-groups"] or {}) do
|
||||
if type(group) == "table" and group.name and group.name ~= "" then
|
||||
valid_policies[tostring(group.name)] = true
|
||||
end
|
||||
end
|
||||
|
||||
local custom_rules = {}
|
||||
for _, section in ipairs(read_clash_client_rules_csv(sid)) do
|
||||
if tostring(section.enabled or "0") == "1" then
|
||||
local ip_addr = tostring(section.ip_addr or "")
|
||||
local policy_group = tostring(section.policy_group or "")
|
||||
if ip_addr ~= "" and policy_group ~= "" and valid_policies[policy_group] then
|
||||
if not ip_addr:find("/", 1, true) then
|
||||
ip_addr = ip_addr .. "/32"
|
||||
end
|
||||
custom_rules[#custom_rules + 1] = string.format("SRC-IP-CIDR,%s,%s", ip_addr, policy_group)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #custom_rules == 0 then
|
||||
io.stdout:write("client_rules=0\n")
|
||||
return true
|
||||
end
|
||||
|
||||
local existing_rules = {}
|
||||
for _, rule in ipairs(doc.rules or {}) do
|
||||
local text = tostring(rule or "")
|
||||
if not text:match("^SRC%-IP%-CIDR,") then
|
||||
existing_rules[#existing_rules + 1] = rule
|
||||
end
|
||||
end
|
||||
|
||||
doc.rules = {}
|
||||
for _, rule in ipairs(custom_rules) do
|
||||
doc.rules[#doc.rules + 1] = rule
|
||||
end
|
||||
for _, rule in ipairs(existing_rules) do
|
||||
doc.rules[#doc.rules + 1] = rule
|
||||
end
|
||||
|
||||
local ok, rendered = pcall(lyaml.dump, { doc })
|
||||
if not ok or not rendered then
|
||||
io.stderr:write("dump_failed\n")
|
||||
return false
|
||||
end
|
||||
|
||||
write_file(runtime_path, rendered)
|
||||
io.stdout:write(string.format("client_rules=%d\n", #custom_rules))
|
||||
return true
|
||||
end
|
||||
|
||||
local function bool_enabled(value)
|
||||
return value == "1" or value == 1 or value == true or value == "true"
|
||||
end
|
||||
|
||||
local function split_csv(value)
|
||||
local items = {}
|
||||
for part in tostring(value or ""):gmatch("[^,%s]+") do
|
||||
items[#items + 1] = part
|
||||
end
|
||||
return items
|
||||
end
|
||||
|
||||
local function get_server_field(sid, option, default)
|
||||
local value = uci:get("shadowsocksr", sid, option)
|
||||
if value == nil or value == "" then
|
||||
return default
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function get_filter_aaaa()
|
||||
local value = uci:get_first("shadowsocksr", "global", "filter_aaaa", "1")
|
||||
if value == nil or value == "" then
|
||||
value = uci:get_first("shadowsocksr", "global", "mosdns_ipv6", "1")
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function build_tuic_runtime_doc(sid, local_port, socks_port, mode)
|
||||
local server = get_server_field(sid, "server", "")
|
||||
local server_port = tonumber(get_server_field(sid, "server_port", "0")) or 0
|
||||
local tuic_ip = get_server_field(sid, "tuic_ip", "")
|
||||
local tls_host = get_server_field(sid, "tls_host", "")
|
||||
local ipstack_prefer = get_server_field(sid, "ipstack_prefer", "")
|
||||
local dns_mode = uci:get_first("shadowsocksr", "global", "pdnsd_enable", "0")
|
||||
|
||||
local proxy = {
|
||||
name = sid,
|
||||
type = "tuic",
|
||||
server = server,
|
||||
port = server_port,
|
||||
uuid = get_server_field(sid, "tuic_uuid", ""),
|
||||
password = get_server_field(sid, "tuic_passwd", ""),
|
||||
["udp-relay-mode"] = get_server_field(sid, "udp_relay_mode", "native"),
|
||||
["congestion-controller"] = get_server_field(sid, "congestion_control", "cubic"),
|
||||
["skip-cert-verify"] = bool_enabled(get_server_field(sid, "insecure", "0")),
|
||||
["disable-sni"] = bool_enabled(get_server_field(sid, "disable_sni", "0")),
|
||||
["reduce-rtt"] = bool_enabled(get_server_field(sid, "zero_rtt_handshake", "0"))
|
||||
}
|
||||
|
||||
if tuic_ip ~= "" then
|
||||
proxy.ip = tuic_ip
|
||||
end
|
||||
if tls_host ~= "" then
|
||||
proxy.sni = tls_host
|
||||
end
|
||||
|
||||
local alpn = split_csv(get_server_field(sid, "tuic_alpn", ""))
|
||||
if #alpn > 0 then
|
||||
proxy.alpn = alpn
|
||||
end
|
||||
|
||||
local heartbeat = tonumber(get_server_field(sid, "heartbeat", "0"))
|
||||
if heartbeat and heartbeat > 0 then
|
||||
proxy["heartbeat-interval"] = heartbeat * 1000
|
||||
end
|
||||
|
||||
local timeout = tonumber(get_server_field(sid, "timeout", "0"))
|
||||
if timeout and timeout > 0 then
|
||||
proxy["request-timeout"] = timeout * 1000
|
||||
end
|
||||
|
||||
local max_udp_packet_size = tonumber(get_server_field(sid, "tuic_max_package_size", "0"))
|
||||
if max_udp_packet_size and max_udp_packet_size > 0 then
|
||||
proxy["max-udp-relay-packet-size"] = max_udp_packet_size
|
||||
end
|
||||
|
||||
if ipstack_prefer ~= "" then
|
||||
proxy["ip-version"] = ipstack_prefer == "v6first" and "ipv6-prefer" or "ipv4-prefer"
|
||||
end
|
||||
|
||||
local listen_port = tonumber(local_port)
|
||||
local socks_listen = tonumber(socks_port)
|
||||
|
||||
local doc = {
|
||||
["allow-lan"] = true,
|
||||
["bind-address"] = "0.0.0.0",
|
||||
mode = "rule",
|
||||
["log-level"] = "silent",
|
||||
["find-process-mode"] = "off",
|
||||
["unified-delay"] = true,
|
||||
["tcp-concurrent"] = true,
|
||||
["routing-mark"] = 255,
|
||||
proxies = { proxy },
|
||||
["proxy-groups"] = {
|
||||
{
|
||||
name = "PROXY",
|
||||
type = "select",
|
||||
proxies = { sid }
|
||||
}
|
||||
},
|
||||
rules = { "MATCH,PROXY" },
|
||||
tun = { enable = false },
|
||||
profile = { ["store-selected"] = true },
|
||||
dns = {
|
||||
enable = dns_mode == "7",
|
||||
["enhanced-mode"] = "redir-host",
|
||||
listen = "127.0.0.1:5335",
|
||||
ipv6 = get_filter_aaaa() ~= "1"
|
||||
}
|
||||
}
|
||||
|
||||
if mode == "socks" then
|
||||
doc["socks-port"] = listen_port
|
||||
else
|
||||
doc["redir-port"] = listen_port
|
||||
doc["tproxy-port"] = listen_port
|
||||
if socks_listen and socks_listen > 0 then
|
||||
doc["socks-port"] = socks_listen
|
||||
end
|
||||
end
|
||||
|
||||
return doc
|
||||
end
|
||||
|
||||
local function generate_tuic_runtime(sid, output_path, local_port, socks_port, mode)
|
||||
local doc = build_tuic_runtime_doc(sid, local_port, socks_port, mode)
|
||||
local ok, rendered = pcall(lyaml.dump, { doc })
|
||||
if not ok or not rendered then
|
||||
io.stderr:write("dump_failed\n")
|
||||
return false
|
||||
end
|
||||
write_file(output_path, rendered)
|
||||
return true
|
||||
end
|
||||
|
||||
local function parse_plugin_opts(value)
|
||||
local result = {}
|
||||
for part in tostring(value or ""):gmatch("[^;]+") do
|
||||
local key, val = part:match("^%s*([^=]+)=?(.*)%s*$")
|
||||
if key and key ~= "" then
|
||||
result[key] = val or ""
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function split_host_port(value)
|
||||
local text = tostring(value or "")
|
||||
if text == "" then
|
||||
return "", ""
|
||||
end
|
||||
local host, port = text:match("^%[(.-)%]:(%d+)$")
|
||||
if host and port then
|
||||
return host, port
|
||||
end
|
||||
host, port = text:match("^(.-):(%d+)$")
|
||||
if host and port then
|
||||
return host, port
|
||||
end
|
||||
return text, ""
|
||||
end
|
||||
|
||||
local function bool_default(value, default)
|
||||
if value == nil or value == "" then
|
||||
return default
|
||||
end
|
||||
return bool_enabled(value)
|
||||
end
|
||||
|
||||
local function number_or_nil(value)
|
||||
if value == nil or value == "" then
|
||||
return nil
|
||||
end
|
||||
return tonumber(value)
|
||||
end
|
||||
|
||||
local function string_or_nil(value)
|
||||
if value == nil or value == "" then
|
||||
return nil
|
||||
end
|
||||
return tostring(value)
|
||||
end
|
||||
|
||||
local function pick_plugin_opt(plugin_opts, ...)
|
||||
for i = 1, select("#", ...) do
|
||||
local key = select(i, ...)
|
||||
local value = plugin_opts[key]
|
||||
if value ~= nil and value ~= "" then
|
||||
return value
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function parse_plugin_headers(plugin_opts)
|
||||
local headers = {}
|
||||
local raw_headers = pick_plugin_opt(plugin_opts, "headers", "header")
|
||||
|
||||
if raw_headers and ok_jsonc and jsonc then
|
||||
local decoded = jsonc.parse(raw_headers)
|
||||
if type(decoded) == "table" then
|
||||
for key, value in pairs(decoded) do
|
||||
headers[tostring(key)] = tostring(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if raw_headers and next(headers) == nil then
|
||||
for part in tostring(raw_headers):gmatch("[^|,]+") do
|
||||
local key, value = part:match("^%s*([^=:]+)%s*[:=]%s*(.-)%s*$")
|
||||
if key and key ~= "" and value and value ~= "" then
|
||||
headers[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for key, value in pairs(plugin_opts) do
|
||||
local header_name = key:match("^headers[%.:](.+)$")
|
||||
or key:match("^header[%.:](.+)$")
|
||||
or key:match("^header_(.+)$")
|
||||
if header_name and header_name ~= "" and value ~= "" then
|
||||
headers[header_name] = value
|
||||
end
|
||||
end
|
||||
|
||||
return next(headers) and headers or nil
|
||||
end
|
||||
|
||||
local function get_plugin_client_fingerprint(sid, plugin_opts)
|
||||
return string_or_nil(
|
||||
pick_plugin_opt(
|
||||
plugin_opts,
|
||||
"client-fingerprint",
|
||||
"client_fingerprint",
|
||||
"fingerprint"
|
||||
) or get_server_field(sid, "fingerprint", "")
|
||||
)
|
||||
end
|
||||
|
||||
local function normalize_plugin_name(plugin)
|
||||
local value = tostring(plugin or ""):lower()
|
||||
if value == "" or value == "none" then
|
||||
return ""
|
||||
end
|
||||
if value == "simple-obfs" then
|
||||
return "obfs-local"
|
||||
end
|
||||
if value == "obfs" then
|
||||
return "obfs-local"
|
||||
end
|
||||
if value == "shadowtls" then
|
||||
return "shadow-tls"
|
||||
end
|
||||
if value == "gost" then
|
||||
return "gost-plugin"
|
||||
end
|
||||
if value == "kcp-tun" then
|
||||
return "kcptun"
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function build_shadowsocks_plugin(proxy, sid)
|
||||
local plugin = normalize_plugin_name(get_server_field(sid, "plugin", ""))
|
||||
local plugin_opts = parse_plugin_opts(get_server_field(sid, "plugin_opts", ""))
|
||||
|
||||
if plugin == "" then
|
||||
return
|
||||
end
|
||||
|
||||
if plugin == "obfs-local" then
|
||||
proxy.plugin = "obfs"
|
||||
proxy["plugin-opts"] = {
|
||||
mode = pick_plugin_opt(plugin_opts, "obfs", "mode") or "http",
|
||||
host = string_or_nil(pick_plugin_opt(plugin_opts, "obfs-host", "obfs_host", "host"))
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
if plugin == "v2ray-plugin" or plugin == "xray-plugin" then
|
||||
proxy.plugin = "v2ray-plugin"
|
||||
proxy["plugin-opts"] = {
|
||||
mode = pick_plugin_opt(plugin_opts, "mode") or "websocket",
|
||||
tls = bool_default(pick_plugin_opt(plugin_opts, "tls"), false),
|
||||
fingerprint = string_or_nil(pick_plugin_opt(plugin_opts, "fingerprint")),
|
||||
["skip-cert-verify"] = bool_default(pick_plugin_opt(plugin_opts, "skip-cert-verify", "skip_cert_verify", "insecure"), false),
|
||||
host = string_or_nil(pick_plugin_opt(plugin_opts, "host")),
|
||||
path = string_or_nil(pick_plugin_opt(plugin_opts, "path")),
|
||||
mux = bool_default(pick_plugin_opt(plugin_opts, "mux"), false),
|
||||
headers = parse_plugin_headers(plugin_opts),
|
||||
["v2ray-http-upgrade"] = bool_default(pick_plugin_opt(plugin_opts, "v2ray-http-upgrade", "v2ray_http_upgrade"), false)
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
if plugin == "gost-plugin" then
|
||||
proxy.plugin = "gost-plugin"
|
||||
proxy["plugin-opts"] = {
|
||||
mode = pick_plugin_opt(plugin_opts, "mode") or "websocket",
|
||||
tls = bool_default(pick_plugin_opt(plugin_opts, "tls"), false),
|
||||
fingerprint = string_or_nil(pick_plugin_opt(plugin_opts, "fingerprint")),
|
||||
["skip-cert-verify"] = bool_default(pick_plugin_opt(plugin_opts, "skip-cert-verify", "skip_cert_verify", "insecure"), false),
|
||||
host = string_or_nil(pick_plugin_opt(plugin_opts, "host")),
|
||||
path = string_or_nil(pick_plugin_opt(plugin_opts, "path")),
|
||||
mux = bool_default(pick_plugin_opt(plugin_opts, "mux"), false),
|
||||
headers = parse_plugin_headers(plugin_opts)
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
if plugin == "shadow-tls" then
|
||||
local host, port = split_host_port(pick_plugin_opt(plugin_opts, "host") or "")
|
||||
local version
|
||||
if plugin_opts.v3 == "1" or plugin_opts.version == "3" then
|
||||
version = 3
|
||||
elseif plugin_opts.v2 == "1" or plugin_opts.version == "2" then
|
||||
version = 2
|
||||
elseif plugin_opts.v1 == "1" or plugin_opts.version == "1" then
|
||||
version = 1
|
||||
end
|
||||
proxy.plugin = "shadow-tls"
|
||||
proxy["client-fingerprint"] = get_plugin_client_fingerprint(sid, plugin_opts)
|
||||
proxy["plugin-opts"] = {
|
||||
host = host ~= "" and host or nil,
|
||||
port = number_or_nil(port),
|
||||
password = string_or_nil(pick_plugin_opt(plugin_opts, "passwd", "password")),
|
||||
version = version
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
if plugin == "restls" then
|
||||
proxy.plugin = "restls"
|
||||
proxy["client-fingerprint"] = get_plugin_client_fingerprint(sid, plugin_opts)
|
||||
proxy["plugin-opts"] = {
|
||||
host = string_or_nil(pick_plugin_opt(plugin_opts, "host")),
|
||||
password = string_or_nil(pick_plugin_opt(plugin_opts, "passwd", "password")),
|
||||
["version-hint"] = string_or_nil(pick_plugin_opt(plugin_opts, "version-hint", "version_hint")),
|
||||
["restls-script"] = string_or_nil(pick_plugin_opt(plugin_opts, "restls-script", "restls_script"))
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
if plugin == "kcptun" then
|
||||
proxy.plugin = "kcptun"
|
||||
proxy["plugin-opts"] = {
|
||||
key = string_or_nil(pick_plugin_opt(plugin_opts, "key", "passwd", "password")),
|
||||
crypt = string_or_nil(pick_plugin_opt(plugin_opts, "crypt")),
|
||||
mode = string_or_nil(pick_plugin_opt(plugin_opts, "mode")),
|
||||
conn = number_or_nil(pick_plugin_opt(plugin_opts, "conn")),
|
||||
autoexpire = number_or_nil(pick_plugin_opt(plugin_opts, "autoexpire")),
|
||||
scavengettl = number_or_nil(pick_plugin_opt(plugin_opts, "scavengettl")),
|
||||
mtu = number_or_nil(pick_plugin_opt(plugin_opts, "mtu")),
|
||||
ratelimit = number_or_nil(pick_plugin_opt(plugin_opts, "ratelimit")),
|
||||
sndwnd = number_or_nil(pick_plugin_opt(plugin_opts, "sndwnd")),
|
||||
rcvwnd = number_or_nil(pick_plugin_opt(plugin_opts, "rcvwnd")),
|
||||
datashard = number_or_nil(pick_plugin_opt(plugin_opts, "datashard")),
|
||||
parityshard = number_or_nil(pick_plugin_opt(plugin_opts, "parityshard")),
|
||||
dscp = number_or_nil(pick_plugin_opt(plugin_opts, "dscp")),
|
||||
nocomp = bool_default(pick_plugin_opt(plugin_opts, "nocomp"), false),
|
||||
acknodelay = bool_default(pick_plugin_opt(plugin_opts, "acknodelay"), false),
|
||||
nodelay = number_or_nil(pick_plugin_opt(plugin_opts, "nodelay")),
|
||||
interval = number_or_nil(pick_plugin_opt(plugin_opts, "interval")),
|
||||
resend = number_or_nil(pick_plugin_opt(plugin_opts, "resend")),
|
||||
sockbuf = number_or_nil(pick_plugin_opt(plugin_opts, "sockbuf")),
|
||||
smuxver = number_or_nil(pick_plugin_opt(plugin_opts, "smuxver")),
|
||||
smuxbuf = number_or_nil(pick_plugin_opt(plugin_opts, "smuxbuf")),
|
||||
framesize = number_or_nil(pick_plugin_opt(plugin_opts, "framesize")),
|
||||
streambuf = number_or_nil(pick_plugin_opt(plugin_opts, "streambuf")),
|
||||
keepalive = number_or_nil(pick_plugin_opt(plugin_opts, "keepalive"))
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
proxy.plugin = plugin
|
||||
if next(plugin_opts) then
|
||||
proxy["plugin-opts"] = plugin_opts
|
||||
end
|
||||
end
|
||||
|
||||
local function build_kcptun_plugin(proxy, sid)
|
||||
if not bool_enabled(get_server_field(sid, "kcp_enable", "0")) then
|
||||
return
|
||||
end
|
||||
|
||||
proxy.plugin = "kcptun"
|
||||
proxy.port = tonumber(get_server_field(sid, "kcp_port", "0")) or proxy.port
|
||||
proxy["plugin-opts"] = {
|
||||
key = get_server_field(sid, "kcp_password", ""),
|
||||
mode = "fast",
|
||||
mtu = 1350
|
||||
}
|
||||
end
|
||||
|
||||
local function build_shadowsocks_runtime_doc(sid, local_port, socks_port, mode)
|
||||
local dns_mode = uci:get_first("shadowsocksr", "global", "pdnsd_enable", "0")
|
||||
local server = get_server_field(sid, "server", "")
|
||||
local server_port = tonumber(get_server_field(sid, "server_port", "0")) or 0
|
||||
local method = get_server_field(sid, "encrypt_method_ss", "none")
|
||||
local password = get_server_field(sid, "password", "")
|
||||
local proxy = {
|
||||
name = sid,
|
||||
type = "ss",
|
||||
server = server,
|
||||
port = server_port,
|
||||
cipher = method,
|
||||
password = password,
|
||||
udp = true,
|
||||
tfo = bool_enabled(get_server_field(sid, "fast_open", "0"))
|
||||
}
|
||||
|
||||
if get_server_field(sid, "type", "") == "ss" then
|
||||
build_kcptun_plugin(proxy, sid)
|
||||
end
|
||||
if proxy.plugin == nil then
|
||||
build_shadowsocks_plugin(proxy, sid)
|
||||
end
|
||||
|
||||
local doc = {
|
||||
["allow-lan"] = true,
|
||||
["bind-address"] = "0.0.0.0",
|
||||
mode = "rule",
|
||||
["log-level"] = "silent",
|
||||
["find-process-mode"] = "off",
|
||||
["unified-delay"] = true,
|
||||
["tcp-concurrent"] = true,
|
||||
["routing-mark"] = 255,
|
||||
proxies = { proxy },
|
||||
["proxy-groups"] = {
|
||||
{
|
||||
name = "PROXY",
|
||||
type = "select",
|
||||
proxies = { sid }
|
||||
}
|
||||
},
|
||||
rules = { "MATCH,PROXY" },
|
||||
tun = { enable = false },
|
||||
profile = { ["store-selected"] = true },
|
||||
dns = {
|
||||
enable = dns_mode == "7",
|
||||
["enhanced-mode"] = "redir-host",
|
||||
listen = "127.0.0.1:5335",
|
||||
ipv6 = get_filter_aaaa() ~= "1"
|
||||
}
|
||||
}
|
||||
|
||||
local listen_port = tonumber(local_port)
|
||||
local socks_listen = tonumber(socks_port)
|
||||
if mode == "socks" then
|
||||
doc["socks-port"] = listen_port
|
||||
else
|
||||
doc["redir-port"] = listen_port
|
||||
doc["tproxy-port"] = listen_port
|
||||
if socks_listen and socks_listen > 0 then
|
||||
doc["socks-port"] = socks_listen
|
||||
end
|
||||
end
|
||||
|
||||
return doc
|
||||
end
|
||||
|
||||
local function generate_shadowsocks_runtime(sid, output_path, local_port, socks_port, mode)
|
||||
local doc = build_shadowsocks_runtime_doc(sid, local_port, socks_port, mode)
|
||||
local ok, rendered = pcall(lyaml.dump, { doc })
|
||||
if not ok or not rendered then
|
||||
io.stderr:write("dump_failed\n")
|
||||
return false
|
||||
end
|
||||
write_file(output_path, rendered)
|
||||
return true
|
||||
end
|
||||
|
||||
local function build_shadowsocks_server_doc(sid)
|
||||
local server_port = tonumber(get_server_field(sid, "server_port", "0")) or 0
|
||||
local method = get_server_field(sid, "encrypt_method_ss", "aes-128-gcm")
|
||||
local password = get_server_field(sid, "password", "")
|
||||
local listener = {
|
||||
name = sid,
|
||||
type = "shadowsocks",
|
||||
listen = "::",
|
||||
port = server_port,
|
||||
cipher = method,
|
||||
password = password,
|
||||
udp = true,
|
||||
tfo = bool_enabled(get_server_field(sid, "fast_open", "0"))
|
||||
}
|
||||
|
||||
local plugin = normalize_plugin_name(get_server_field(sid, "plugin", ""))
|
||||
if plugin == "obfs-local" then
|
||||
local plugin_opts = parse_plugin_opts(get_server_field(sid, "plugin_opts", ""))
|
||||
listener.obfs = plugin_opts.obfs or plugin_opts.mode or "http"
|
||||
listener.obfs_opts = {
|
||||
mode = plugin_opts.obfs or plugin_opts.mode or "http",
|
||||
host = plugin_opts["obfs-host"] or plugin_opts.obfs_host or plugin_opts.host or nil
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
["allow-lan"] = true,
|
||||
["bind-address"] = "*",
|
||||
["log-level"] = "silent",
|
||||
["find-process-mode"] = "off",
|
||||
listeners = { listener }
|
||||
}
|
||||
end
|
||||
|
||||
local function generate_shadowsocks_server(sid, output_path)
|
||||
local doc = build_shadowsocks_server_doc(sid)
|
||||
local ok, rendered = pcall(lyaml.dump, { doc })
|
||||
if not ok or not rendered then
|
||||
io.stderr:write("dump_failed\n")
|
||||
return false
|
||||
end
|
||||
write_file(output_path, rendered)
|
||||
return true
|
||||
end
|
||||
|
||||
local action = arg[1]
|
||||
if action == "validate" then
|
||||
os.exit(validate(arg[2]) and 0 or 1)
|
||||
elseif action == "filter" then
|
||||
os.exit(filter(arg[2], arg[3]) and 0 or 1)
|
||||
elseif action == "prepare" then
|
||||
os.exit(prepare(arg[2], arg[3]) and 0 or 1)
|
||||
elseif action == "merge" then
|
||||
os.exit(merge(arg[2], arg[3], arg[4]) and 0 or 1)
|
||||
elseif action == "append_client_policy_rules" then
|
||||
os.exit(append_client_policy_rules(arg[2], arg[3]) and 0 or 1)
|
||||
elseif action == "tuic" then
|
||||
os.exit(generate_tuic_runtime(arg[2], arg[3], arg[4], arg[5], arg[6]) and 0 or 1)
|
||||
elseif action == "ss" then
|
||||
os.exit(generate_shadowsocks_runtime(arg[2], arg[3], arg[4], arg[5], arg[6]) and 0 or 1)
|
||||
elseif action == "ss_server" then
|
||||
os.exit(generate_shadowsocks_server(arg[2], arg[3]) and 0 or 1)
|
||||
else
|
||||
io.stderr:write("usage: clash_yaml.lua validate <yaml> | filter <yaml> <words> | prepare <input> <output> | merge <raw> <overlay> <output> | append_client_policy_rules <runtime_yaml> <sid> | tuic <sid> <output> <local_port> [socks_port] [mode] | ss <sid> <output> <local_port> [socks_port] [mode] | ss_server <sid> <output>\n")
|
||||
os.exit(1)
|
||||
end
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
require "luci.sys"
|
||||
local ucursor = require "luci.model.uci".cursor()
|
||||
local datatypes = require "luci.cbi.datatypes"
|
||||
local json = require "luci.jsonc"
|
||||
|
||||
local server_section = arg[1]
|
||||
@@ -11,6 +12,12 @@ local socks_port = arg[4] or "0"
|
||||
|
||||
local chain = arg[5] or "0"
|
||||
|
||||
local GLOBAL = {
|
||||
DNS_SERVER = {},
|
||||
DNS_HOSTNAME = {},
|
||||
VPS_EXCLUDE = {}
|
||||
}
|
||||
|
||||
-- 辅助函数:拆分字符串(若 luci.util 未加载则定义)
|
||||
local function split(str, pat)
|
||||
local t = {}
|
||||
@@ -31,52 +38,27 @@ local server = ucursor:get_all("shadowsocksr", server_section)
|
||||
local socks_server = ucursor:get_all("shadowsocksr", "@socks5_proxy[0]") or {}
|
||||
local xray_fragment = ucursor:get_all("shadowsocksr", "@global_xray_fragment[0]") or {}
|
||||
local xray_noise = ucursor:get_all("shadowsocksr", "@xray_noise_packets[0]") or {}
|
||||
local default_node_local_port = ucursor:get_first("shadowsocksr", "global", "default_node_local_port", "1234")
|
||||
local dns_mode = ucursor:get_first("shadowsocksr", "global", "pdnsd_enable", "0")
|
||||
local dns_ipv4_only = ucursor:get_first("shadowsocksr", "global", "filter_aaaa")
|
||||
if not dns_ipv4_only or dns_ipv4_only == "" then
|
||||
dns_ipv4_only = ucursor:get_first("shadowsocksr", "global", "mosdns_ipv6", "1")
|
||||
end
|
||||
local builtin_dns_server = ucursor:get_first("shadowsocksr", "global", "tunnel_forward", "8.8.4.4:53")
|
||||
local outbound_settings = nil
|
||||
local xray_version = nil
|
||||
local xray_version_val = 0
|
||||
local xray_builtin_dns = nil
|
||||
|
||||
local node_id = server_section
|
||||
local remarks = server.alias or ""
|
||||
local b64decode = nixio.bin.b64decode
|
||||
local b64encode = nixio.bin.b64encode
|
||||
local effective_node_local_port = tonumber(server.local_port) or tonumber(default_node_local_port) or 1234
|
||||
|
||||
if server.type == "ss-rust" then
|
||||
server.type = "ss"
|
||||
-- 解析 URL(简单实现,仅用于 DoH)
|
||||
local function parseURL(url)
|
||||
if not url then return nil end
|
||||
local schema, rest = url:match("^(https?)://(.*)$")
|
||||
if not schema then return nil end
|
||||
local host, port_str = rest:match("^([^:]+):?(%d*)/?.*$")
|
||||
local port = tonumber(port_str) or (schema == "https" and 443 or 80)
|
||||
return { host = host, port = port, schema = schema }
|
||||
end
|
||||
|
||||
local function parse_realm_uri(uri)
|
||||
if type(uri) ~= "string" then return nil end
|
||||
-- realm://token@server/realm_id?query
|
||||
local token, server_url, realm_id, query = trim(uri):match("^realm://([^@]+)@([^/]+)/([^?]*)%??(.*)$")
|
||||
if not token or not server_url or not realm_id then return nil end
|
||||
realm_id = realm_id:gsub("/+$", "")
|
||||
local realm = {
|
||||
token = token,
|
||||
server_url = server_url,
|
||||
realm_id = realm_id
|
||||
}
|
||||
-- 解析 query 中的 stun=
|
||||
if query and query ~= "" then
|
||||
local stun_servers = {}
|
||||
for key, value in query:gmatch("([^&=?]+)=([^&]+)") do
|
||||
if key == "stun" and value ~= "" then
|
||||
stun_servers[#stun_servers + 1] = value
|
||||
end
|
||||
end
|
||||
if #stun_servers > 0 then
|
||||
realm.stun_servers = stun_servers
|
||||
end
|
||||
end
|
||||
return realm
|
||||
if server.type == "ss-rust" or server.type == "ss-libev" then
|
||||
server.type = "ss"
|
||||
end
|
||||
|
||||
-- base64 解码
|
||||
@@ -138,25 +120,9 @@ local function cleanEmptyTables(t)
|
||||
return next(t) and t or nil
|
||||
end
|
||||
|
||||
local function format_host(host)
|
||||
host = tostring(host or "")
|
||||
if host ~= "" and host:find(":", 1, true) and not host:match("^%[.*%]$") then
|
||||
return "[" .. host .. "]"
|
||||
end
|
||||
return host
|
||||
end
|
||||
|
||||
local function format_host_port(host, port)
|
||||
host = format_host(host)
|
||||
if port == nil or port == "" then
|
||||
return host
|
||||
end
|
||||
return host .. ":" .. tostring(port)
|
||||
end
|
||||
|
||||
-- 确保正确判断程序是否存在
|
||||
local function is_finded(e)
|
||||
return luci.sys.exec(string.format('type -t -p "%s" -p "/usr/libexec/%s" 2>/dev/null', e, e)) ~= ""
|
||||
return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
|
||||
end
|
||||
|
||||
-- 获取 Xray 版本号
|
||||
@@ -237,33 +203,20 @@ function wireguard()
|
||||
-- 处理 reserved 字段,支持逗号分隔的数字或 Base64 编码
|
||||
local reserved = nil
|
||||
if server.reserved then
|
||||
local all_bytes = {}
|
||||
local reserved_values = server.reserved
|
||||
|
||||
-- 确保是 table 类型
|
||||
if type(reserved_values) ~= "table" then
|
||||
reserved_values = {reserved_values}
|
||||
end
|
||||
|
||||
for _, reserved_str in ipairs(reserved_values) do
|
||||
if type(reserved_str) == "string" then
|
||||
if not reserved_str:match("[^%d,]+") then
|
||||
-- 数字和逗号格式
|
||||
reserved_str:gsub("%d+", function(b)
|
||||
all_bytes[#all_bytes + 1] = tonumber(b)
|
||||
end)
|
||||
else
|
||||
-- Base64 格式
|
||||
local result = base64Decode(reserved_str)
|
||||
if result then
|
||||
for i = 1, #result do
|
||||
all_bytes[#all_bytes + 1] = result:byte(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
local bytes = {}
|
||||
if not server.reserved:match("[^%d,]+") then
|
||||
-- 纯数字和逗号,解析为数字列表
|
||||
server.reserved:gsub("%d+", function(b)
|
||||
bytes[#bytes + 1] = tonumber(b)
|
||||
end)
|
||||
else
|
||||
-- Base64 编码的二进制数据
|
||||
local result = base64Decode(server.reserved)
|
||||
for i = 1, #result do
|
||||
bytes[i] = result:byte(i)
|
||||
end
|
||||
end
|
||||
reserved = #all_bytes > 0 and all_bytes or nil
|
||||
reserved = #bytes > 0 and bytes or nil
|
||||
end
|
||||
|
||||
outbound_settings = {
|
||||
@@ -273,22 +226,15 @@ function wireguard()
|
||||
{
|
||||
publicKey = server.peer_pubkey,
|
||||
preSharedKey = server.preshared_key,
|
||||
endpoint = format_host_port(server.server, server.server_port),
|
||||
endpoint = server.server .. ":" .. server.server_port,
|
||||
keepAlive = tonumber(server.keepaliveperiod),
|
||||
allowedIPs = (server.allowedips) or nil,
|
||||
}
|
||||
},
|
||||
kernelMode = (server.kernelmode == "1") and true or false,
|
||||
noKernelTun = (server.kernelmode == "1") and true or false,
|
||||
reserved = reserved,
|
||||
mtu = tonumber(server.mtu)
|
||||
}
|
||||
if server.finalmask and server.finalmask ~= "" then
|
||||
local ok, fm = pcall(json.parse, base64Decode(server.finalmask))
|
||||
if ok and type(fm) == "table" then
|
||||
outbound_settings.streamSettings = outbound_settings.streamSettings or {}
|
||||
outbound_settings.streamSettings.finalmask = fm
|
||||
end
|
||||
end
|
||||
end
|
||||
function xray_hysteria2()
|
||||
outbound_settings = {
|
||||
@@ -353,35 +299,6 @@ local Xray = {
|
||||
-- 初始化 outbounds 表
|
||||
outbounds = {},
|
||||
}
|
||||
|
||||
if server.type == "v2ray" and dns_mode == "7" and os.getenv("SSR_SWITCH_PROBE") ~= "1" then
|
||||
local dns_host = builtin_dns_server:match("^([^:]+)") or "8.8.4.4"
|
||||
local dns_port = tonumber(builtin_dns_server:match(":(%d+)$")) or 53
|
||||
|
||||
Xray.dns = {
|
||||
queryStrategy = (dns_ipv4_only == "1") and "UseIPv4" or "UseIP",
|
||||
servers = {
|
||||
string.format("tcp://%s:%d", dns_host, dns_port)
|
||||
}
|
||||
}
|
||||
|
||||
table.insert(Xray.inbounds, {
|
||||
listen = "127.0.0.1",
|
||||
port = 5335,
|
||||
protocol = "dokodemo-door",
|
||||
settings = {
|
||||
address = dns_host,
|
||||
port = dns_port,
|
||||
network = "tcp,udp"
|
||||
},
|
||||
tag = "builtin-dns-in"
|
||||
})
|
||||
|
||||
xray_builtin_dns = {
|
||||
address = dns_host,
|
||||
port = dns_port
|
||||
}
|
||||
end
|
||||
-- 传入连接
|
||||
-- 添加 dokodemo-door 配置,如果 local_port 不为 0
|
||||
if local_port ~= "0" then
|
||||
@@ -432,11 +349,11 @@ if proto and proto:find("tcp") and socks_port ~= "0" then
|
||||
-- socks
|
||||
protocol = "socks",
|
||||
port = tonumber(socks_port),
|
||||
settings = {
|
||||
settings = {
|
||||
auth = socks_server.socks5_auth or "noauth",
|
||||
udp = true,
|
||||
mixed = ((socks_server.socks5_mixed == '1') and true or false) or nil,
|
||||
accounts = (socks_server.socks5_auth and socks_server.socks5_auth ~= "noauth") and {
|
||||
mixed = ((socks_server.socks5_mixed == '1') and true or false) or (socks_server.server == 'same') and nil,
|
||||
accounts = (socks_server.server ~= "same" and (socks_server.socks5_auth and socks_server.socks5_auth ~= "noauth")) and {
|
||||
{
|
||||
user = socks_server.socks5_user,
|
||||
pass = socks_server.socks5_pass
|
||||
@@ -615,7 +532,7 @@ Xray.outbounds = {
|
||||
local finalmask = {}
|
||||
local PT = server.v2ray_protocol
|
||||
local TP = server.transport
|
||||
if TP == "kcp" then
|
||||
if server.transport == "kcp" then
|
||||
local map = {none = "none", srtp = "header-srtp", utp = "header-utp", ["wechat-video"] = "header-wechat",
|
||||
dtls = "header-dtls", wireguard = "header-wireguard", dns = "header-dns"}
|
||||
local udp = {}
|
||||
@@ -633,36 +550,14 @@ Xray.outbounds = {
|
||||
udp[#udp+1] = c
|
||||
finalmask.udp = udp
|
||||
elseif PT == "hysteria2" then
|
||||
local udp = {}
|
||||
if (server.flag_obfs == "1" and (server.obfs_type and server.obfs_type ~= "")) then
|
||||
local o = {
|
||||
type = "salamander",
|
||||
finalmask.udp = {{
|
||||
type = server.obfs_type,
|
||||
settings = server.salamander and {
|
||||
password = server.salamander,
|
||||
packetSize = server.obfs_type == "gecko" and "512-1200" or nil
|
||||
password = server.salamander
|
||||
} or nil
|
||||
}
|
||||
udp[#udp+1] = o
|
||||
}}
|
||||
end
|
||||
if server.hysteria2_realms then
|
||||
local realm = parse_realm_uri(server.hysteria2_realm_url)
|
||||
local url, stun
|
||||
if realm then
|
||||
if realm.token and realm.server_url and realm.realm_id then
|
||||
url = "realm://" .. realm.token .. "@" .. realm.server_url .. "/" .. realm.realm_id
|
||||
end
|
||||
stun = realm.stun_servers or server.hysteria2_realm_stun
|
||||
end
|
||||
local r = {
|
||||
type = "realm",
|
||||
settings = {
|
||||
url = url,
|
||||
stunServers = stun
|
||||
}
|
||||
}
|
||||
udp[#udp+1] = r
|
||||
end
|
||||
finalmask.udp = udp
|
||||
local up = tonumber(server.uplink_capacity) or 0
|
||||
local down = tonumber(server.downlink_capacity) or 0
|
||||
finalmask.quicParams = {
|
||||
@@ -750,6 +645,7 @@ Xray.outbounds = {
|
||||
end)(),
|
||||
sockopt = {
|
||||
mark = 255,
|
||||
domainStrategy = server.domain_strategy or "UseIP",
|
||||
tcpFastOpen = (function()
|
||||
if server.transport == "xhttp" then
|
||||
return (server.fast_open == "1") and true or false
|
||||
@@ -777,24 +673,18 @@ Xray.outbounds = {
|
||||
}
|
||||
}
|
||||
|
||||
if xray_builtin_dns then
|
||||
table.insert(Xray.outbounds, {
|
||||
protocol = "dns",
|
||||
tag = "builtin-dns-out",
|
||||
settings = {
|
||||
network = "tcp",
|
||||
address = xray_builtin_dns.address,
|
||||
port = xray_builtin_dns.port
|
||||
table.insert(Xray.outbounds, {
|
||||
protocol = "freedom",
|
||||
tag = "direct",
|
||||
settings = {
|
||||
domainStrategy = server.domain_strategy or "UseIP" -- 可根据需要改为 direct_dns_query_strategy
|
||||
},
|
||||
streamSettings = {
|
||||
sockopt = {
|
||||
mark = 255
|
||||
}
|
||||
})
|
||||
Xray.routing = Xray.routing or {}
|
||||
Xray.routing.rules = Xray.routing.rules or {}
|
||||
table.insert(Xray.routing.rules, {
|
||||
type = "field",
|
||||
inboundTag = { "builtin-dns-in" },
|
||||
outboundTag = "builtin-dns-out"
|
||||
})
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
-- 添加带有 fragment 设置的 dialerproxy 配置
|
||||
if xray_fragment.fragment ~= "0" or (xray_fragment.noise ~= "0" and xray_noise.enabled ~= "0") then
|
||||
@@ -825,6 +715,121 @@ if xray_fragment.fragment ~= "0" or (xray_fragment.noise ~= "0" and xray_noise.e
|
||||
})
|
||||
end
|
||||
|
||||
-- Xray DNS 解析配置
|
||||
if datatypes.hostname(server.server) and server.domain_resolver and (server.domain_resolver_dns or server.domain_resolver_dns_https) then
|
||||
-- 解析 DNS 服务器配置
|
||||
local dns_proto = server.domain_resolver
|
||||
local config_address
|
||||
local config_port
|
||||
if dns_proto == "https" then
|
||||
local _a = parseURL(server.domain_resolver_dns_https)
|
||||
if _a then
|
||||
config_address = server.domain_resolver_dns_https
|
||||
config_port = _a.port or 443
|
||||
if _a.hostname and datatypes.hostname(_a.hostname) then
|
||||
GLOBAL.DNS_HOSTNAME[_a.hostname] = true
|
||||
end
|
||||
end
|
||||
else
|
||||
local server_address = server.domain_resolver_dns
|
||||
config_port = 53
|
||||
local parts = split(server_address, ":")
|
||||
if #parts > 1 then
|
||||
server_address = parts[1]
|
||||
config_port = tonumber(parts[#parts])
|
||||
end
|
||||
config_address = server_address
|
||||
if dns_proto == "tcp" then
|
||||
config_address = dns_proto .. "://" .. server_address .. ":" .. config_port
|
||||
end
|
||||
end
|
||||
|
||||
-- 存入 GLOBAL.DNS_SERVER(去重)
|
||||
local dns_key = dns_proto .. "|" .. config_address .. "|" .. tostring(config_port)
|
||||
if not GLOBAL.DNS_SERVER[dns_key] then
|
||||
GLOBAL.DNS_SERVER[dns_key] = {
|
||||
tag = "dns-node-" .. node_id,
|
||||
address = config_address,
|
||||
port = config_port,
|
||||
finalQuery = true,
|
||||
disableCache = false,
|
||||
serveStale = true,
|
||||
serveExpiredTTL = 30,
|
||||
domains = {}
|
||||
}
|
||||
end
|
||||
|
||||
-- 添加当前节点域名到该 DNS 服务器的 domains 列表
|
||||
local domain = "full:" .. server.server
|
||||
local exists
|
||||
for _, d in ipairs(GLOBAL.DNS_SERVER[dns_key].domains) do
|
||||
if d == domain then exists = true; break end
|
||||
end
|
||||
if not exists then
|
||||
table.insert(GLOBAL.DNS_SERVER[dns_key].domains, domain)
|
||||
end
|
||||
GLOBAL.VPS_EXCLUDE[server.server] = true
|
||||
|
||||
-- 构建 Xray.dns
|
||||
local dns_servers = { "localhost" }
|
||||
for key, dns_server in pairs(GLOBAL.DNS_SERVER) do
|
||||
table.insert(dns_servers, {
|
||||
tag = dns_server.tag,
|
||||
address = dns_server.address,
|
||||
port = dns_server.port,
|
||||
domains = dns_server.domains,
|
||||
finalQuery = dns_server.finalQuery,
|
||||
serveStale = dns_server.serveStale,
|
||||
serveExpiredTTL = dns_server.serveExpiredTTL,
|
||||
disableCache = dns_server.disableCache
|
||||
})
|
||||
end
|
||||
Xray.dns = {
|
||||
servers = dns_servers,
|
||||
disableFallback = true,
|
||||
disableFallbackIfMatch = true,
|
||||
useSystemHosts = true,
|
||||
queryStrategy = "UseIP",
|
||||
disableCache = false,
|
||||
tag = "dns-global" -- 用于 routing
|
||||
}
|
||||
end
|
||||
|
||||
-- 代理出站的 tag(与 outbound 中的 tag 保持一致)
|
||||
local proxy_tag = (remarks ~= nil and remarks ~= "") and (node_id .. ":" .. remarks) or node_id
|
||||
-- 构建 routing 规则表
|
||||
local routing_rules = {}
|
||||
-- 1. 为每个自定义 DNS 服务器添加直连规则(inboundTag 匹配 dns-node-xxx)
|
||||
if GLOBAL.DNS_SERVER and next(GLOBAL.DNS_SERVER) then
|
||||
for _, dns_server in pairs(GLOBAL.DNS_SERVER) do
|
||||
table.insert(routing_rules, {
|
||||
inboundTag = { dns_server.tag },
|
||||
outboundTag = "direct"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- 2. 添加全局 DNS 直连规则(需要 Xray.dns 中已设置 tag = "dns-global")
|
||||
if Xray.dns and Xray.dns.tag then
|
||||
table.insert(routing_rules, {
|
||||
inboundTag = { "dns-global" },
|
||||
outboundTag = "direct"
|
||||
})
|
||||
end
|
||||
|
||||
-- 3. 添加默认规则:所有 TCP/UDP 流量走代理
|
||||
table.insert(routing_rules, {
|
||||
network = "tcp,udp",
|
||||
ruleTag = "default",
|
||||
outboundTag = proxy_tag
|
||||
})
|
||||
|
||||
-- 构建 routing 对象
|
||||
Xray.routing = {
|
||||
rules = routing_rules,
|
||||
domainStrategy = "AsIs"
|
||||
}
|
||||
|
||||
local cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA"
|
||||
local cipher13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384"
|
||||
local trojan = {
|
||||
@@ -872,7 +877,7 @@ local trojan = {
|
||||
}
|
||||
}
|
||||
local naiveproxy = {
|
||||
proxy = (server.username and server.password and server.server and server.server_port) and "https://" .. server.username .. ":" .. server.password .. "@" .. format_host_port(server.server, server.server_port),
|
||||
proxy = (server.username and server.password and server.server and server.server_port) and "https://" .. server.username .. ":" .. server.password .. "@" .. server.server .. ":" .. server.server_port,
|
||||
listen = (proto == "redir") and "redir" .. "://0.0.0.0:" .. tonumber(local_port) or "socks" .. "://0.0.0.0:" .. tonumber(local_port),
|
||||
["insecure-concurrency"] = tonumber(server.concurrency) or 1
|
||||
}
|
||||
@@ -893,16 +898,16 @@ local hysteria2 = {
|
||||
server.server_port and
|
||||
(
|
||||
server.port_range and
|
||||
(format_host_port(server.server, server.server_port) .. "," .. string.gsub(server.port_range, ":", "-"))
|
||||
(server.server .. ":" .. server.server_port .. "," .. string.gsub(server.port_range, ":", "-"))
|
||||
or
|
||||
(format_host_port(server.server, server.server_port))
|
||||
(server.server .. ":" .. server.server_port)
|
||||
)
|
||||
or
|
||||
(
|
||||
server.port_range and
|
||||
format_host(server.server) .. ":" .. string.gsub(server.port_range, ":", "-")
|
||||
server.server .. ":" .. string.gsub(server.port_range, ":", "-")
|
||||
or
|
||||
server.server and format_host_port(server.server, "443")
|
||||
server.server and server.server .. ":443"
|
||||
)
|
||||
),
|
||||
bandwidth = (server.uplink_capacity or server.downlink_capacity) and {
|
||||
@@ -1001,7 +1006,7 @@ local hysteria2 = {
|
||||
}
|
||||
local shadowtls = {
|
||||
client = {
|
||||
server_addr = server.server_port and format_host_port(server.server, server.server_port) or nil,
|
||||
server_addr = server.server_port and server.server .. ":" .. server.server_port or nil,
|
||||
listen = "127.0.0.1:" .. tonumber(local_port),
|
||||
tls_names = server.shadowtls_sni,
|
||||
password = server.password
|
||||
@@ -1015,7 +1020,7 @@ local chain_sslocal = {
|
||||
locals = local_port ~= "0" and {
|
||||
{
|
||||
local_address = "0.0.0.0",
|
||||
local_port = (chain_local_port == "0" and effective_node_local_port or tonumber(chain_local_port)),
|
||||
local_port = (chain_local_port == "0" and tonumber(server.local_port) or tonumber(chain_local_port)),
|
||||
mode = (proto:find("tcp,udp") and "tcp_and_udp") or proto .. "_only",
|
||||
protocol = "redir",
|
||||
tcp_redir = "redirect",
|
||||
@@ -1044,7 +1049,7 @@ local chain_sslocal = {
|
||||
local chain_vmess = {
|
||||
inbounds = (local_port ~= "0") and {
|
||||
{
|
||||
port = (chain_local_port == "0" and effective_node_local_port or tonumber(chain_local_port)),
|
||||
port = (chain_local_port == "0" and tonumber(server.local_port) or tonumber(chain_local_port)),
|
||||
protocol = "dokodemo-door",
|
||||
settings = {
|
||||
network = proto,
|
||||
@@ -1080,7 +1085,7 @@ local chain_vmess = {
|
||||
}
|
||||
local tuic = {
|
||||
relay = {
|
||||
server = server.server_port and format_host_port(server.server, server.server_port),
|
||||
server = server.server_port and server.server .. ":" .. server.server_port,
|
||||
ip = server.tuic_ip,
|
||||
uuid = server.tuic_uuid,
|
||||
password = server.tuic_passwd,
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/bin/sh
|
||||
argv1=$1
|
||||
argv2=$2
|
||||
argv3=$3
|
||||
argv4=$4
|
||||
argv5=$5
|
||||
argv6=$6
|
||||
argv7=$7
|
||||
argv8=$8
|
||||
argv9=$9
|
||||
cat <<-EOF >$argv1
|
||||
base {
|
||||
log_debug = off;
|
||||
log_info = off;
|
||||
log = stderr;
|
||||
daemon = on;
|
||||
redirector = iptables;
|
||||
reuseport = on;
|
||||
}
|
||||
EOF
|
||||
tcp() {
|
||||
if [ "$argv7" == "0" ]; then
|
||||
cat <<-EOF >>$argv1
|
||||
redsocks {
|
||||
bind = "0.0.0.0:$argv4";
|
||||
relay = "$argv5:$argv6";
|
||||
type = socks5;
|
||||
autoproxy = 0;
|
||||
timeout = 10;
|
||||
}
|
||||
EOF
|
||||
else
|
||||
cat <<-EOF >>$argv1
|
||||
redsocks {
|
||||
bind = "0.0.0.0:$argv4";
|
||||
relay = "$argv5:$argv6";
|
||||
type = socks5;
|
||||
autoproxy = 0;
|
||||
timeout = 10;
|
||||
login = "$argv8";
|
||||
password = "$argv9";
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
udp() {
|
||||
if [ "$argv7" == "0" ]; then
|
||||
cat <<-EOF >>$argv1
|
||||
redudp {
|
||||
bind = "0.0.0.0:$argv4";
|
||||
relay = "$argv5:$argv6";
|
||||
type = socks5;
|
||||
udp_timeout = 10;
|
||||
}
|
||||
EOF
|
||||
else
|
||||
cat <<-EOF >>$argv1
|
||||
redudp {
|
||||
bind = "0.0.0.0:$argv4";
|
||||
relay = "$argv5:$argv6";
|
||||
type = socks5;
|
||||
udp_timeout = 10;
|
||||
login = "$argv8";
|
||||
password = "$argv9";
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
case "$argv2" in
|
||||
socks5)
|
||||
case "$argv3" in
|
||||
tcp)
|
||||
tcp
|
||||
;;
|
||||
udp)
|
||||
udp
|
||||
;;
|
||||
*)
|
||||
tcp
|
||||
udp
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
cat <<-EOF >>$argv1
|
||||
redsocks {
|
||||
bind = "0.0.0.0:$argv4";
|
||||
type = direct;
|
||||
interface = $argv3;
|
||||
autoproxy = 0;
|
||||
timeout = 10;
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
@@ -18,12 +18,38 @@ case "$USE_TABLES" in
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
netflix() {
|
||||
local port="$1"
|
||||
if [ -f "$TMP_DNSMASQ_PATH/gfw_list.conf" ] && [ -s /etc/ssrplus/netflix.list ]; then
|
||||
grep -vE '^\s*#|^\s*$' /etc/ssrplus/netflix.list > /tmp/ssrplus_netflix.list.clean
|
||||
if [ -s /tmp/ssrplus_netflix.list.clean ]; then
|
||||
grep -v -F -f /tmp/ssrplus_netflix.list.clean "$TMP_DNSMASQ_PATH/gfw_list.conf" > "$TMP_DNSMASQ_PATH/gfw_list.conf.tmp"
|
||||
mv "$TMP_DNSMASQ_PATH/gfw_list.conf.tmp" "$TMP_DNSMASQ_PATH/gfw_list.conf"
|
||||
if [ -f "$TMP_DNSMASQ_PATH/gfw_base.conf" ]; then
|
||||
grep -v -F -f /tmp/ssrplus_netflix.list.clean "$TMP_DNSMASQ_PATH/gfw_base.conf" > "$TMP_DNSMASQ_PATH/gfw_base.conf.tmp"
|
||||
mv "$TMP_DNSMASQ_PATH/gfw_base.conf.tmp" "$TMP_DNSMASQ_PATH/gfw_base.conf"
|
||||
fi
|
||||
fi
|
||||
rm -f /tmp/ssrplus_netflix.list.clean
|
||||
fi
|
||||
if [ "$nft_support" = "1" ]; then
|
||||
# 移除 ipset
|
||||
cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$port\nnftset=\/&\/inet#ss_spec#netflix/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
|
||||
elif [ "$nft_support" = "0" ]; then
|
||||
cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$port\nipset=\/&\/netflix/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
|
||||
fi
|
||||
}
|
||||
mkdir -p $TMP_DNSMASQ_PATH
|
||||
|
||||
run_mode=$(normalize_run_mode)
|
||||
run_mode=$(uci_get_by_type global run_mode router)
|
||||
|
||||
cp -rf /etc/ssrplus/gfw_list.conf $TMP_DNSMASQ_PATH/
|
||||
cp -rf /etc/ssrplus/gfw_base.conf $TMP_DNSMASQ_PATH/
|
||||
if [ "$run_mode" = "oversea" ]; then
|
||||
cp -rf /etc/ssrplus/oversea_list.conf $TMP_DNSMASQ_PATH/
|
||||
else
|
||||
cp -rf /etc/ssrplus/gfw_list.conf $TMP_DNSMASQ_PATH/
|
||||
cp -rf /etc/ssrplus/gfw_base.conf $TMP_DNSMASQ_PATH/
|
||||
fi
|
||||
|
||||
for conf_file in gfw_base.conf gfw_list.conf; do
|
||||
conf="$TMP_DNSMASQ_PATH/$conf_file"
|
||||
@@ -41,41 +67,36 @@ for conf_file in gfw_base.conf gfw_list.conf; do
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$(uci_get_by_type global netflix_enable 0)" == "1" ]; then
|
||||
# 只有开启 NetFlix分流 才需要取值
|
||||
SHUNT_SERVER=$(uci_get_by_type global netflix_server nil)
|
||||
else
|
||||
# 没有开启 设置为 nil
|
||||
SHUNT_SERVER=nil
|
||||
fi
|
||||
case "$SHUNT_SERVER" in
|
||||
nil)
|
||||
rm -f $TMP_DNSMASQ_PATH/netflix_forward.conf
|
||||
;;
|
||||
$(uci_get_by_type global global_server nil) | $switch_server | same)
|
||||
netflix $dns_port
|
||||
;;
|
||||
*)
|
||||
netflix $tmp_shunt_dns_port
|
||||
;;
|
||||
esac
|
||||
|
||||
# 此处使用 for 方式读取 防止 /etc/ssrplus/ 目录下的 black.list white.list deny.list 等2个或多个文件一行中存在空格 比如:# abc.com 而丢失:server
|
||||
# Optimize: Batch filter using grep
|
||||
for list_file in /etc/ssrplus/black.list /etc/ssrplus/white.list /etc/ssrplus/deny.list; do
|
||||
if [ -s "$list_file" ]; then
|
||||
# 清理注释和空行
|
||||
grep -vE '^\s*#|^\s*$' "$list_file" | sed 's/\r//g' > "${list_file}.clean"
|
||||
grep -vE '^\s*#|^\s*$' "$list_file" > "${list_file}.clean"
|
||||
if [ -s "${list_file}.clean" ]; then
|
||||
for target_file in "$TMP_DNSMASQ_PATH/gfw_list.conf" "$TMP_DNSMASQ_PATH/gfw_base.conf"; do
|
||||
[ -f "$target_file" ] || continue
|
||||
tmp_file="${target_file}.tmp"
|
||||
awk -v list="${list_file}.clean" '
|
||||
BEGIN {
|
||||
while ((getline line < list) > 0) {
|
||||
gsub(/\r/, "", line)
|
||||
if (line != "") {
|
||||
domain[line] = 1
|
||||
# 同时支持 *.domain
|
||||
domain["*." line] = 1
|
||||
}
|
||||
}
|
||||
close(list)
|
||||
}
|
||||
{
|
||||
# 提取 server=/domain/xxx
|
||||
if (match($0, /^server=\/([^\/]+)\//, m)) {
|
||||
if (m[1] in domain) next
|
||||
}
|
||||
# 提取 ipset=/domain/xxx
|
||||
if (match($0, /^ipset=\/([^\/]+)\//, m)) {
|
||||
if (m[1] in domain) next
|
||||
}
|
||||
print
|
||||
}
|
||||
' "$target_file" > "$tmp_file"
|
||||
mv "$tmp_file" "$target_file"
|
||||
if [ -f "$target_file" ]; then
|
||||
grep -v -F -f "${list_file}.clean" "$target_file" > "${target_file}.tmp"
|
||||
mv "${target_file}.tmp" "$target_file"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
rm -f "${list_file}.clean"
|
||||
@@ -93,45 +114,19 @@ fi
|
||||
cat /etc/ssrplus/deny.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/address=\/&\//" >$TMP_DNSMASQ_PATH/denylist.conf
|
||||
|
||||
if [ "$(uci_get_by_type global adblock 0)" == "1" ]; then
|
||||
cp -f /etc/ssrplus/ad.conf "$TMP_DNSMASQ_PATH/"
|
||||
cp -f /etc/ssrplus/ad.conf $TMP_DNSMASQ_PATH/
|
||||
if [ -f "$TMP_DNSMASQ_PATH/ad.conf" ]; then
|
||||
for list_file in /etc/ssrplus/black.list /etc/ssrplus/white.list /etc/ssrplus/deny.list; do
|
||||
for list_file in /etc/ssrplus/black.list /etc/ssrplus/white.list /etc/ssrplus/deny.list /etc/ssrplus/netflix.list; do
|
||||
if [ -s "$list_file" ]; then
|
||||
# 清理注释 & 空行
|
||||
grep -vE '^\s*#|^\s*$' "$list_file" | sed 's/\r//g' > "${list_file}.clean"
|
||||
grep -vE '^\s*#|^\s*$' "$list_file" > "${list_file}.clean"
|
||||
if [ -s "${list_file}.clean" ]; then
|
||||
tmp_file="$TMP_DNSMASQ_PATH/ad.conf.tmp"
|
||||
awk -v list="${list_file}.clean" '
|
||||
BEGIN {
|
||||
while ((getline line < list) > 0) {
|
||||
gsub(/\r/, "", line)
|
||||
if (line != "") {
|
||||
domain[line] = 1
|
||||
# 支持泛域名
|
||||
domain["*." line] = 1
|
||||
}
|
||||
}
|
||||
close(list)
|
||||
}
|
||||
{
|
||||
keep = 1
|
||||
# 精确匹配 server=/domain/
|
||||
if (match($0, /^server=\/([^\/]+)\//, m)) {
|
||||
if (m[1] in domain) keep = 0
|
||||
}
|
||||
# 精确匹配 ipset=/domain/
|
||||
if (match($0, /^ipset=\/([^\/]+)\//, m)) {
|
||||
if (m[1] in domain) keep = 0
|
||||
}
|
||||
if (keep) print
|
||||
}
|
||||
' "$TMP_DNSMASQ_PATH/ad.conf" > "$tmp_file"
|
||||
mv "$tmp_file" "$TMP_DNSMASQ_PATH/ad.conf"
|
||||
grep -v -F -f "${list_file}.clean" "$TMP_DNSMASQ_PATH/ad.conf" > "$TMP_DNSMASQ_PATH/ad.conf.tmp"
|
||||
mv "$TMP_DNSMASQ_PATH/ad.conf.tmp" "$TMP_DNSMASQ_PATH/ad.conf"
|
||||
fi
|
||||
rm -f "${list_file}.clean"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
rm -f "$TMP_DNSMASQ_PATH/ad.conf"
|
||||
rm -f $TMP_DNSMASQ_PATH/ad.conf
|
||||
fi
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
#!/bin/sh
|
||||
# /usr/share/shadowsocksr/test.sh
|
||||
|
||||
CONFIG="shadowsocksr"
|
||||
LOCK_PATH=/tmp/lock
|
||||
TMP_PATH=/var/etc/ssrplus
|
||||
|
||||
uci_get_by_name() {
|
||||
local ret=$(uci -q get $CONFIG.$1.$2 2>/dev/null)
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
uci_get_by_type() {
|
||||
local ret=$(uci -q get $CONFIG.@$1[0].$2 2>/dev/null)
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
check_port_exists() {
|
||||
local port=$1
|
||||
local protocol=$2
|
||||
[ -n "$protocol" ] || protocol="tcp,udp"
|
||||
local result=
|
||||
if [ "$protocol" = "tcp" ]; then
|
||||
result=$(netstat -tln | grep -c ":$port ")
|
||||
elif [ "$protocol" = "udp" ]; then
|
||||
result=$(netstat -uln | grep -c ":$port ")
|
||||
elif [ "$protocol" = "tcp,udp" ]; then
|
||||
result=$(netstat -tuln | grep -c ":$port ")
|
||||
fi
|
||||
echo "${result}"
|
||||
}
|
||||
|
||||
set_cache_var() {
|
||||
local key="${1}"
|
||||
shift 1
|
||||
local val="$@"
|
||||
[ -n "${key}" ] && [ -n "${val}" ] && {
|
||||
sed -i "/${key}=/d" $TMP_PATH/var >/dev/null 2>&1
|
||||
echo "${key}=\"${val}\"" >> $TMP_PATH/var
|
||||
eval ${key}=\"${val}\"
|
||||
}
|
||||
}
|
||||
|
||||
get_cache_var() {
|
||||
local key="${1}"
|
||||
[ -n "${key}" ] && [ -s "$TMP_PATH/var" ] && {
|
||||
echo $(cat $TMP_PATH/var | grep "^${key}=" | awk -F '=' '{print $2}' | tail -n 1 | awk -F'"' '{print $2}')
|
||||
}
|
||||
}
|
||||
|
||||
#uci_get_by_port() {
|
||||
# local port=$1
|
||||
# while netstat -tuln 2>/dev/null | grep -q ":${port} "; do
|
||||
# port=$((port + 1))
|
||||
# done
|
||||
# echo $port
|
||||
|
||||
uci_get_by_port() {
|
||||
local default_start_port=2001
|
||||
local min_port=1025
|
||||
local max_port=49151
|
||||
local port="$1"
|
||||
local protocol=$(echo "$2" | tr 'A-Z' 'a-z')
|
||||
local LOCK_FILE="${LOCK_PATH}/${CONFIG}_get_prot.lock"
|
||||
while ! mkdir "$LOCK_FILE" 2>/dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
if [ "$port" = "auto" ]; then
|
||||
local now last_time diff last_port
|
||||
now=$(date +%s 2>/dev/null)
|
||||
last_time=$(get_cache_var "last_get_new_port_time")
|
||||
if [ -n "$now" ] && [ -n "$last_time" ]; then
|
||||
diff=$(expr "$now" - "$last_time")
|
||||
[ "$diff" -lt 0 ] && diff=$(expr 0 - "$diff")
|
||||
else
|
||||
diff=999
|
||||
fi
|
||||
if [ "$diff" -gt 10 ]; then
|
||||
port=$default_start_port
|
||||
else
|
||||
last_port=$(get_cache_var "last_get_new_port_auto")
|
||||
if [ -n "$last_port" ]; then
|
||||
port=$(expr "$last_port" + 1)
|
||||
else
|
||||
port=$default_start_port
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
[ "$port" -lt $min_port -o "$port" -gt $max_port ] && port=$default_start_port
|
||||
local start_port="$port"
|
||||
while :; do
|
||||
if [ "$(check_port_exists "$port" "$protocol")" = 0 ]; then
|
||||
break
|
||||
fi
|
||||
port=$(expr "$port" + 1)
|
||||
if [ "$port" -gt $max_port ]; then
|
||||
port=$min_port
|
||||
fi
|
||||
[ "$port" = "$start_port" ] && {
|
||||
rmdir "$LOCK_FILE" 2>/dev/null
|
||||
return 1
|
||||
}
|
||||
done
|
||||
if [ "$1" = "auto" ]; then
|
||||
set_cache_var "last_get_new_port_auto" "$port"
|
||||
[ -n "$now" ] && set_cache_var "last_get_new_port_time" "$now"
|
||||
fi
|
||||
rmdir "$LOCK_FILE" 2>/dev/null
|
||||
echo "$port"
|
||||
}
|
||||
|
||||
url_test_hy2() {
|
||||
local node_id=$1
|
||||
|
||||
# 读取配置
|
||||
local server=$(uci_get_by_name ${node_id} server)
|
||||
local port=$(uci_get_by_name ${node_id} server_port)
|
||||
local auth=$(uci_get_by_name ${node_id} hy2_auth)
|
||||
local tls=$(uci_get_by_name ${node_id} tls)
|
||||
local insecure=$(uci_get_by_name ${node_id} insecure)
|
||||
local tls_host=$(uci_get_by_name ${node_id} tls_host)
|
||||
|
||||
# 获取本地端口
|
||||
# local tmp_port=$(uci_get_by_port 48900 tcp,udp)
|
||||
local tmp_port=$(uci_get_by_port auto tcp,udp)
|
||||
|
||||
# 生成Hysteria2配置文件
|
||||
local config_file="/tmp/hy2_test_${node_id}.yaml"
|
||||
cat > "$config_file" <<-EOF
|
||||
server: ${server}:${port}
|
||||
auth: "${auth}"
|
||||
tls:
|
||||
insecure: true
|
||||
EOF
|
||||
|
||||
# 如果 tls_host 非空,动态添加 sni 行
|
||||
[ -n "$tls_host" ] && echo " sni: \"${tls_host}\"" >> "$config_file"
|
||||
|
||||
# 追加 socks5 监听配置
|
||||
cat >> "$config_file" <<-EOF
|
||||
socks5:
|
||||
listen: 127.0.0.1:${tmp_port}
|
||||
EOF
|
||||
|
||||
# echo "Debug: 配置文件已生成: $config_file" >&2
|
||||
|
||||
# 启动Hysteria2客户端
|
||||
hysteria client --disable-update-check -c "$config_file" >/dev/null 2>&1 &
|
||||
local pid=$!
|
||||
echo $pid > "/tmp/hy2_test_${node_id}.pid"
|
||||
|
||||
# 等待端口启动
|
||||
sleep 1
|
||||
|
||||
# 测试代理
|
||||
# local result=$(curl --connect-timeout 3 --max-time 3 -s -o /dev/null -I -w "%{http_code}:%{time_pretransfer}" --socks5 127.0.0.1:${tmp_port} "${probeUrl}" 2>/dev/null)
|
||||
local curlx="socks5h://127.0.0.1:${tmp_port}"
|
||||
local probeUrl=$(uci_get_by_type server_subscribe url_test_url https://www.google.com/generate_204)
|
||||
local result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}" 2>/dev/null)
|
||||
|
||||
# 清理
|
||||
# kill -9 $pid 2>/dev/null
|
||||
local pid_file="/tmp/hy2_test_${node_id}.pid"
|
||||
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
|
||||
pgrep -af "hysteria.*${config_file}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
|
||||
rm -f "$config_file" "$pid_file"
|
||||
|
||||
echo $result
|
||||
}
|
||||
|
||||
case $1 in
|
||||
url_test_hy2)
|
||||
url_test_hy2 $2
|
||||
;;
|
||||
esac
|
||||
@@ -1,35 +1,35 @@
|
||||
|
||||
config global
|
||||
option global_server 'nil'
|
||||
option netflix_server 'nil'
|
||||
option netflix_proxy '0'
|
||||
option threads '0'
|
||||
option run_mode 'router'
|
||||
option dports '2'
|
||||
option custom_ports '80,443'
|
||||
option pdnsd_enable '1'
|
||||
option filter_aaaa '1'
|
||||
option prefer_nft '1'
|
||||
option tunnel_forward '8.8.4.4:53'
|
||||
option monitor_enable '1'
|
||||
option enable_switch '1'
|
||||
option switch_time '667'
|
||||
option switch_timeout '5'
|
||||
option switch_try_count '3'
|
||||
option default_node_local_port '1234'
|
||||
option component_mirror 'direct'
|
||||
option shunt_dns '1'
|
||||
option gfwlist_url 'https://fastly.jsdelivr.net/gh/YW5vbnltb3Vz/domain-list-community@release/gfwlist.txt'
|
||||
option chnroute_url 'https://ispip.clang.cn/all_cn.txt'
|
||||
option nfip_url 'https://fastly.jsdelivr.net/gh/QiuSimons/Netflix_IP/NF_only.txt'
|
||||
option adblock_url 'https://anti-ad.net/anti-ad-for-dnsmasq.conf'
|
||||
|
||||
config server_subscribe
|
||||
option proxy '1'
|
||||
option proxy '0'
|
||||
option auto_update '1'
|
||||
option config_auto_update_mode '0'
|
||||
option config_update_interval '60'
|
||||
option auto_update_week_time '*'
|
||||
option auto_update_day_time '2'
|
||||
option auto_update_min_time '0'
|
||||
option url_test_url 'https://www.google.com/generate_204'
|
||||
option user_agent 'v2rayN/9.99'
|
||||
option filter_words '过期/套餐/剩余/网址/QQ群/官网/防失联/回国'
|
||||
option filter_words '过期/套餐/剩余/QQ群/官网/防失联/回国'
|
||||
|
||||
config access_control
|
||||
option lan_ac_mode '0'
|
||||
@@ -47,11 +47,6 @@ config socks5_proxy
|
||||
option server 'nil'
|
||||
option local_port '1080'
|
||||
|
||||
config http_proxy
|
||||
option server 'nil'
|
||||
option local_port '3128'
|
||||
option http_auth 'none'
|
||||
|
||||
config server_global
|
||||
option enable_server '0'
|
||||
|
||||
@@ -59,9 +54,3 @@ config global_xray_fragment
|
||||
option fragment '0'
|
||||
option noise '0'
|
||||
|
||||
config clash_client_group
|
||||
option enabled '0'
|
||||
option remarks ''
|
||||
option ip_addr ''
|
||||
option client_mac ''
|
||||
option policy_group ''
|
||||
@@ -1,43 +1,6 @@
|
||||
#!/bin/sh
|
||||
LOCK_DIR="/var/run/ssrplusupdate.lock"
|
||||
LOOP_STAMP="/var/run/ssrplusupdate.loop"
|
||||
MODE="$(uci -q get shadowsocksr.@server_subscribe[0].config_auto_update_mode 2>/dev/null || echo 0)"
|
||||
|
||||
mkdir "$LOCK_DIR" 2>/dev/null || exit 0
|
||||
trap 'rmdir "$LOCK_DIR" 2>/dev/null' EXIT INT TERM
|
||||
|
||||
if [ "$1" = "loop" ]; then
|
||||
[ "$(uci -q get shadowsocksr.@server_subscribe[0].auto_update 2>/dev/null || echo 0)" = "1" ] || exit 0
|
||||
[ "$MODE" = "1" ] || exit 0
|
||||
|
||||
INTERVAL="$(uci -q get shadowsocksr.@server_subscribe[0].config_update_interval 2>/dev/null || echo 60)"
|
||||
case "$INTERVAL" in
|
||||
''|*[!0-9]*)
|
||||
INTERVAL=60
|
||||
;;
|
||||
esac
|
||||
[ "$INTERVAL" -gt 0 ] 2>/dev/null || INTERVAL=60
|
||||
|
||||
NOW="$(date +%s)"
|
||||
LAST_RUN=0
|
||||
[ -f "$LOOP_STAMP" ] && LAST_RUN="$(cat "$LOOP_STAMP" 2>/dev/null || echo 0)"
|
||||
case "$LAST_RUN" in
|
||||
''|*[!0-9]*)
|
||||
LAST_RUN=0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $((NOW - LAST_RUN)) -lt $((INTERVAL * 60)) ]; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
/usr/bin/lua /usr/share/shadowsocksr/update.lua
|
||||
sleep 2s
|
||||
/usr/share/shadowsocksr/chinaipset.sh /var/etc/ssrplus/china_ssr.txt
|
||||
sleep 2s
|
||||
/usr/bin/lua /usr/share/shadowsocksr/subscribe.lua
|
||||
|
||||
if [ "$1" = "loop" ]; then
|
||||
date +%s > "$LOOP_STAMP"
|
||||
fi
|
||||
File diff suppressed because it is too large.
Load diff
@@ -250,6 +250,10 @@ if args then
|
||||
update(uci:get_first("shadowsocksr", "global", "adblock_url"), "/etc/ssrplus/ad.conf", args, TMP_DNSMASQ_PATH .. "/ad.conf")
|
||||
os.exit(0)
|
||||
end
|
||||
if args == "nfip_data" then
|
||||
update(uci:get_first("shadowsocksr", "global", "nfip_url"), "/etc/ssrplus/netflixip.list", args, TMP_DNSMASQ_PATH .. "/netflixip.list")
|
||||
os.exit(0)
|
||||
end
|
||||
else
|
||||
log("正在更新【GFW列表】数据库")
|
||||
update(uci:get_first("shadowsocksr", "global", "gfwlist_url"), "/etc/ssrplus/gfw_list.conf", "gfw_data", TMP_DNSMASQ_PATH .. "/gfw_list.conf")
|
||||
@@ -263,4 +267,10 @@ else
|
||||
log("正在更新【广告屏蔽】数据库")
|
||||
update(uci:get_first("shadowsocksr", "global", "adblock_url"), "/etc/ssrplus/ad.conf", "ad_data", TMP_DNSMASQ_PATH .. "/ad.conf")
|
||||
end
|
||||
if uci:get_first("shadowsocksr", "global", "netflix_enable", "0") == "1" then
|
||||
log("正在更新【Netflix IP段】数据库")
|
||||
update(uci:get_first("shadowsocksr", "global", "nfip_url"), "/etc/ssrplus/netflixip.list", "nfip_data", TMP_DNSMASQ_PATH .. "/netflixip.list")
|
||||
end
|
||||
-- log("正在更新【Netflix IP段】数据库")
|
||||
-- update(uci:get_first("shadowsocksr", "global", "nfip_url"), "/etc/ssrplus/netflixip.list", "nfip_data")
|
||||
end
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user