update 2026-07-28 01:38:11

This commit is contained in:
action committed 2026-07-28 01:38:11 +08:00
1 parent 20b786ab77
commit 25d570431d
6243 files changed
+2850542

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

@@ -0,0 +1,53 @@
#!/bin/sh
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
check_run_environment
# 设置 china_ip 变量并检查文件是否存在
china_ip="${1:-${china_ip:-/etc/ssrplus/china_ssr.txt}}"
[ -f "$china_ip" ] || exit 1
case "$USE_TABLES" in
nftables)
skip_inet="${SKIP_INET:-0}"
case "$skip_inet" in
1)
{
# ss_spec / inet (仅在表和 set 存在时添加)
if nft list set inet ss_spec china >/dev/null 2>&1; then
echo "add element inet ss_spec china {"
grep -vE '^\s*#|^\s*$' "$china_ip" | sed 's/^/ /;s/$/,/'
echo "}"
fi
} | nft -f - || exit 1
;;
2)
{
# ss_spec_mangle / ip (仅在表和 set 存在时添加)
if nft list set ip ss_spec_mangle china >/dev/null 2>&1; then
echo "add element ip ss_spec_mangle china {"
grep -vE '^\s*#|^\s*$' "$china_ip" | sed 's/^/ /;s/$/,/'
echo "}"
fi
} | nft -f - || exit 1
;;
*)
echolog "chinaipset: invalid SKIP_INET=$skip_inet"
exit 1
;;
esac
;;
iptables)
ipset -! flush china 2>/dev/null
ipset -! -R <<-EOF || exit 1
create china hash:net
$(grep -vE '^\s*#|^\s*$' "$china_ip" | sed 's/^/add china /')
EOF
;;
*)
echolog "ERROR: No supported firewall backend detected"
exit 1
;;
esac
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+138
View File
@@ -0,0 +1,138 @@
#!/bin/sh
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
check_run_environment
case "$USE_TABLES" in
nftables)
nft_support=1
echolog "gfw2ipset: Using nftables"
;;
iptables)
nft_support=0
echolog "gfw2ipset: Using iptables"
;;
*)
echolog "ERROR: No supported firewall backend detected"
exit 1
;;
esac
mkdir -p $TMP_DNSMASQ_PATH
run_mode=$(normalize_run_mode)
cp -rf /etc/ssrplus/gfw_list.conf $TMP_DNSMASQ_PATH/
cp -rf /etc/ssrplus/gfw_base.conf $TMP_DNSMASQ_PATH/
for conf_file in gfw_base.conf gfw_list.conf; do
conf="$TMP_DNSMASQ_PATH/$conf_file"
[ -f "$conf" ] || continue
if [ "$run_mode" = "gfw" ]; then
if [ "$nft_support" = "1" ]; then
# gfw + nft:ipset → nftset
sed -i 's|ipset=/\([^/]*\)/\([^[:space:]]*\)|nftset=/\1/inet#ss_spec#\2|g' "$conf"
fi
else
# 非 gfw:无条件清理所有分流引用
# sed -i '/^[[:space:]]*\(ipset=\|nftset=\)/d' "$conf"
sed -i '/^[[:space:]]*ipset=/d' "$conf"
fi
done
# 此处使用 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"
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"
done
fi
rm -f "${list_file}.clean"
fi
done
# 此处直接使用 cat 因为有 sed '/#/d' 删除了 数据
if [ "$nft_support" = "1" ]; then
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/server=\/&\/127.0.0.1#$dns_port\nnftset=\/&\/inet#ss_spec#blacklist/" /etc/ssrplus/black.list > "$TMP_DNSMASQ_PATH/blacklist_forward.conf"
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/server=\/&\/127.0.0.1\nnftset=\/&\/inet#ss_spec#whitelist/" /etc/ssrplus/white.list > "$TMP_DNSMASQ_PATH/whitelist_forward.conf"
elif [ "$nft_support" = "0" ]; then
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/server=\/&\/127.0.0.1#$dns_port\nipset=\/&\/blacklist/" /etc/ssrplus/black.list > "$TMP_DNSMASQ_PATH/blacklist_forward.conf"
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/server=\/&\/127.0.0.1\nipset=\/&\/whitelist/" /etc/ssrplus/white.list > "$TMP_DNSMASQ_PATH/whitelist_forward.conf"
fi
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/address=\/&\//" /etc/ssrplus/deny.list > "$TMP_DNSMASQ_PATH/denylist.conf"
if [ "$(uci_get_by_type global adblock 0)" == "1" ]; then
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
if [ -s "$list_file" ]; then
# 清理注释 & 空行
grep -vE '^\s*#|^\s*$' "$list_file" | sed 's/\r//g' > "${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"
fi
rm -f "${list_file}.clean"
fi
done
fi
else
rm -f "$TMP_DNSMASQ_PATH/ad.conf"
fi
@@ -0,0 +1,67 @@
config global
option global_server 'nil'
option threads '0'
option run_mode 'router'
option dports '2'
option custom_ports '80,443'
option pdnsd_enable '1'
option filter_aaaa '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 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 adblock_url 'https://anti-ad.net/anti-ad-for-dnsmasq.conf'
config server_subscribe
option proxy '1'
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群/官网/防失联/回国'
config access_control
option lan_ac_mode '0'
option router_proxy '1'
list wan_fw_ips '149.154.160.0/20'
list wan_fw_ips '67.198.55.0/24'
list wan_fw_ips '91.108.4.0/22'
list wan_fw_ips '91.108.56.0/22'
list wan_fw_ips '109.239.140.0/24'
list wan_fw_ips '8.8.8.8'
list wan_fw_ips '1.1.1.1'
list Interface 'lan'
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'
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 ''
@@ -0,0 +1,43 @@
#!/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
+266
View File
@@ -0,0 +1,266 @@
#!/usr/bin/lua
------------------------------------------------
-- This file is part of the luci-app-ssr-plus update.lua
-- By Mattraks
------------------------------------------------
require "luci.sys"
require "luci.model.uci"
local icount = 0
local args = arg[1]
local uci = require "luci.model.uci".cursor()
-- 以下设置更新数据库至 DNSMASQ 配置路径
-- 获取 DNSMASQ 配置 ID
local DNSMASQ_UCI_CONFIG = uci:get_first("dhcp", "dnsmasq", ".name")
-- 获取 DNSMASQ 默认配置文件
local DNSMASQ_CONF_PATH = "/tmp/etc/dnsmasq.conf." .. DNSMASQ_UCI_CONFIG
-- 检查 DNSMASQ 配置文件是否存在,如果存在则提取 conf-dir
for line in io.lines(DNSMASQ_CONF_PATH) do
local conf_dir = line:match("^conf%-dir=(.+)")
if conf_dir then
DNSMASQ_CONF_DIR = conf_dir:gsub("%s+", "") -- 去除空白字符
break
end
end
-- 设置 dnsmasq-ssrplus.d 目录路径,并去除路径末尾的斜杠
local TMP_DNSMASQ_PATH = DNSMASQ_CONF_DIR:match("^(.-)/?$") .. "/dnsmasq-ssrplus.d"
local TMP_PATH = "/var/etc/ssrplus"
-- match comments/title/whitelist/ip address/excluded_domain
local comment_pattern = "^[!\\[@]+"
local ip_pattern = "^%d+%.%d+%.%d+%.%d+"
local domain_pattern = "([%w%-%_]+%.[%w%.%-%_]+)[%/%*]*"
local excluded_domain = {
"apple.com", "sina.cn", "sina.com.cn", "baidu.com", "byr.cn", "jlike.com",
"weibo.com", "zhongsou.com", "youdao.com", "sogou.com", "so.com", "soso.com",
"aliyun.com", "taobao.com", "jd.com", "qq.com"
}
-- gfwlist parameter
local mydnsip = '127.0.0.1'
local mydnsport = '5335'
local ipsetname = 'gfwlist'
local new_appledns = uci:get_first("shadowsocksr", "global", "apple_dns", "")
local bc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-- base64decoding
local function base64_dec(data)
data = string.gsub(data, '[^' .. bc .. '=]', '')
return (data:gsub('.', function(x)
if (x == '=') then
return ''
end
local r, f = '', (bc:find(x) - 1)
for i = 6, 1, -1 do
r = r .. (f % 2 ^ i - f % 2 ^ (i - 1) > 0 and '1' or '0')
end
return r;
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then
return ''
end
local c = 0
for i = 1, 8 do
c = c + (x:sub(i, i) == '1' and 2 ^ (8 - i) or 0)
end
return string.char(c)
end))
end
-- check if domain is excluded
local function check_excluded_domain(value)
for _, domain in ipairs(excluded_domain) do
if value:find(domain) then
return true
end
end
end
-- gfwlist转码至dnsmasq格式
local function generate_gfwlist(type)
local domains, domains_map = {}, {}
local out = io.open("/tmp/ssr-update." .. type, "w")
for line in io.lines("/tmp/ssr-update.tmp") do
if not (string.find(line, comment_pattern) or string.find(line, ip_pattern) or check_excluded_domain(line)) then
local start, finish, match = string.find(line, domain_pattern)
if start and not domains_map[match] then
domains_map[match] = true
table.insert(domains, match)
end
end
end
for _, domain in ipairs(domains) do
out:write(string.format("server=/%s/%s#%s\n", domain, mydnsip, mydnsport))
out:write(string.format("ipset=/%s/%s\n", domain, ipsetname))
end
out:close()
os.remove("/tmp/ssr-update.tmp")
end
-- 更换 Apple dns
local function generate_apple(type)
local domains, domains_map = {}, {}
local out = io.open("/tmp/ssr-update." .. type, "w")
for line in io.lines("/tmp/ssr-update.tmp") do
if not (string.find(line, comment_pattern)) then
local start, finish, match = string.find(line, domain_pattern)
if start and not domains_map[match] then
domains_map[match] = true
match = string.gsub(match, "%s", "") --从域名中去除所有空白字符
table.insert(domains, match)
end
end
end
for _, domain in ipairs(domains) do
out:write(string.format("server=/%s/%s\n", domain, new_appledns))
end
out:close()
os.remove("/tmp/ssr-update.tmp")
end
-- adblock转码至dnsmasq格式
local function generate_adblock(type)
local domains, domains_map = {}, {}
local out = io.open("/tmp/ssr-update." .. type, "w")
for line in io.lines("/tmp/ssr-update.tmp") do
if not (string.find(line, comment_pattern)) then
local start, finish, match = string.find(line, domain_pattern)
if start and not domains_map[match] then
domains_map[match] = true
table.insert(domains, match)
end
end
end
for _, domain in ipairs(domains) do
out:write(string.format("address=/%s/\n", domain))
end
out:close()
os.remove("/tmp/ssr-update.tmp")
end
local log = function(...)
if args then
print("{ret=" .. table.concat({...}, ",retcount=") .. "}")
else
print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({...}, " "))
end
end
local function update(url, file, type, file2)
local Num = 1
local refresh_cmd = "curl -sSL --insecure -o /tmp/ssr-update." .. type .. " " .. url
local sret = luci.sys.call(refresh_cmd)
if sret == 0 then
if type == "gfw_data" then
local gfwlist = io.open("/tmp/ssr-update." .. type, "r")
local decode = gfwlist:read("*a")
if not decode:find("google") then
decode = base64_dec(decode)
end
gfwlist:close()
-- 写回gfwlist
gfwlist = io.open("/tmp/ssr-update.tmp", "w")
gfwlist:write(decode)
gfwlist:close()
generate_gfwlist(type)
Num = 2
end
if type == "apple_data" then
local apple = io.open("/tmp/ssr-update." .. type, "r")
local decode = apple:read("*a")
if not decode:find("apple") then
decode = base64_dec(decode)
end
apple:close()
-- 写回applechina
apple = io.open("/tmp/ssr-update.tmp", "w")
apple:write(decode)
apple:close()
if new_appledns and new_appledns ~= "" then
generate_apple(type)
else
os.remove("/tmp/ssr-update.tmp")
end
end
if type == "ad_data" then
local adblock = io.open("/tmp/ssr-update." .. type, "r")
local decode = adblock:read("*a")
if decode:find("address=") then
adblock:close()
else
adblock:close()
-- 写回adblock
adblock = io.open("/tmp/ssr-update.tmp", "w")
adblock:write(decode)
adblock:close()
generate_adblock(type)
end
end
local new_md5 = luci.sys.exec("echo -n $([ -f '/tmp/ssr-update." .. type .. "' ] && md5sum /tmp/ssr-update." .. type .. " | awk '{print $1}')")
local old_md5 = luci.sys.exec("echo -n $([ -f '" .. file .. "' ] && md5sum " .. file .. " | awk '{print $1}')")
if new_md5 == old_md5 then
if args then
log(1)
else
log("你已经是最新数据,无需更新!")
end
else
icount = luci.sys.exec("cat /tmp/ssr-update." .. type .. " | wc -l")
luci.sys.exec("cp -f /tmp/ssr-update." .. type .. " " .. file)
if file2 then
luci.sys.exec("cp -f /tmp/ssr-update." .. type .. " " .. file2)
end
if type == "gfw_data" or type == "ad_data" then
luci.sys.call("/usr/share/shadowsocksr/gfw2ipset.sh")
else
if luci.sys.call("command -v ipset >/dev/null 2>&1") == 0 then
luci.sys.call("/usr/share/shadowsocksr/chinaipset.sh " .. TMP_PATH .. "/china_ssr.txt")
end
end
if args then
log(0, tonumber(icount) / Num)
else
log("更新成功! 新的总记录数:" .. tostring(tonumber(icount) / Num))
end
end
else
if args then
log(-1)
else
log("更新失败!")
end
end
os.remove("/tmp/ssr-update." .. type)
end
if args then
if args == "gfw_data" then
update(uci:get_first("shadowsocksr", "global", "gfwlist_url"), "/etc/ssrplus/gfw_list.conf", args, TMP_DNSMASQ_PATH .. "/gfw_list.conf")
os.exit(0)
end
if args == "ip_data" then
update(uci:get_first("shadowsocksr", "global", "chnroute_url"), "/etc/ssrplus/china_ssr.txt", args, TMP_PATH .. "/china_ssr.txt")
os.exit(0)
end
if args == "apple_data" then
update(uci:get_first("shadowsocksr", "global", "apple_url"), "/etc/ssrplus/applechina.conf", args, TMP_DNSMASQ_PATH .. "/applechina.conf")
os.exit(0)
end
if args == "ad_data" then
update(uci:get_first("shadowsocksr", "global", "adblock_url"), "/etc/ssrplus/ad.conf", args, TMP_DNSMASQ_PATH .. "/ad.conf")
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")
log("正在更新【国内IP段】数据库")
update(uci:get_first("shadowsocksr", "global", "chnroute_url"), "/etc/ssrplus/china_ssr.txt", "ip_data", TMP_PATH .. "/china_ssr.txt")
if uci:get_first("shadowsocksr", "global", "apple_optimization", "0") == "1" then
log("正在更新【Apple域名】数据库")
update(uci:get_first("shadowsocksr", "global", "apple_url"), "/etc/ssrplus/applechina.conf", "apple_data", TMP_DNSMASQ_PATH .. "/applechina.conf")
end
if uci:get_first("shadowsocksr", "global", "adblock", "0") == "1" then
log("正在更新【广告屏蔽】数据库")
update(uci:get_first("shadowsocksr", "global", "adblock_url"), "/etc/ssrplus/ad.conf", "ad_data", TMP_DNSMASQ_PATH .. "/ad.conf")
end
end
File diff suppressed because it is too large. Load diff