🐶 Sync 2025-11-02 14:26:26
This commit is contained in:
250
luci-app-passwall/root/usr/share/passwall/0_default_config
Normal file
250
luci-app-passwall/root/usr/share/passwall/0_default_config
Normal file
@@ -0,0 +1,250 @@
|
||||
|
||||
config global
|
||||
option enabled '0'
|
||||
option socks_enabled '0'
|
||||
option tcp_node_socks_port '1070'
|
||||
option filter_proxy_ipv6 '1'
|
||||
option dns_shunt 'chinadns-ng'
|
||||
option dns_mode 'tcp'
|
||||
option remote_dns '1.1.1.1'
|
||||
list smartdns_remote_dns 'https://1.1.1.1/dns-query'
|
||||
option use_default_dns 'direct'
|
||||
option chinadns_ng_default_tag 'none'
|
||||
option dns_redirect '1'
|
||||
option use_direct_list '1'
|
||||
option use_proxy_list '1'
|
||||
option use_block_list '1'
|
||||
option use_gfw_list '1'
|
||||
option chn_list 'direct'
|
||||
option tcp_proxy_mode 'proxy'
|
||||
option udp_proxy_mode 'proxy'
|
||||
option localhost_proxy '1'
|
||||
option client_proxy '1'
|
||||
option acl_enable '0'
|
||||
option log_tcp '0'
|
||||
option log_udp '0'
|
||||
option loglevel 'error'
|
||||
option trojan_loglevel '4'
|
||||
option log_chinadns_ng '0'
|
||||
|
||||
config global_haproxy
|
||||
option balancing_enable '0'
|
||||
|
||||
config global_delay
|
||||
option start_daemon '1'
|
||||
option start_delay '15'
|
||||
|
||||
config global_forwarding
|
||||
option tcp_no_redir_ports 'disable'
|
||||
option udp_no_redir_ports 'disable'
|
||||
option tcp_proxy_drop_ports 'disable'
|
||||
option udp_proxy_drop_ports '443'
|
||||
option tcp_redir_ports '22,25,53,80,143,443,465,587,853,873,993,995,5222,8080,8443,9418'
|
||||
option udp_redir_ports '1:65535'
|
||||
option accept_icmp '0'
|
||||
option use_nft '0'
|
||||
option tcp_proxy_way 'redirect'
|
||||
option ipv6_tproxy '0'
|
||||
|
||||
config global_xray
|
||||
option sniffing_override_dest '0'
|
||||
|
||||
config global_singbox
|
||||
option sniff_override_destination '0'
|
||||
|
||||
config global_other
|
||||
option auto_detection_time 'tcping'
|
||||
option show_node_info '0'
|
||||
|
||||
config global_rules
|
||||
option auto_update '0'
|
||||
option chnlist_update '1'
|
||||
option chnroute_update '1'
|
||||
option chnroute6_update '1'
|
||||
option gfwlist_update '1'
|
||||
option geosite_update '0'
|
||||
option geoip_update '0'
|
||||
list gfwlist_url 'https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt'
|
||||
list chnroute_url 'https://ispip.clang.cn/all_cn.txt'
|
||||
list chnroute_url 'https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china.txt'
|
||||
list chnroute6_url 'https://ispip.clang.cn/all_cn_ipv6.txt'
|
||||
list chnroute6_url 'https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china6.txt'
|
||||
list chnlist_url 'https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf'
|
||||
list chnlist_url 'https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf'
|
||||
option v2ray_location_asset '/usr/share/v2ray/'
|
||||
option geoip_url 'https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat'
|
||||
option geosite_url 'https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat'
|
||||
|
||||
config global_app
|
||||
option sing_box_file '/usr/bin/sing-box'
|
||||
option xray_file '/usr/bin/xray'
|
||||
option hysteria_file '/usr/bin/hysteria'
|
||||
|
||||
config global_subscribe
|
||||
option filter_keyword_mode '1'
|
||||
list filter_discard_list '距离下次重置剩余'
|
||||
list filter_discard_list '套餐到期'
|
||||
list filter_discard_list '过期时间'
|
||||
list filter_discard_list '剩余流量'
|
||||
list filter_discard_list 'QQ群'
|
||||
list filter_discard_list '官网'
|
||||
|
||||
config nodes 'myshunt'
|
||||
option remarks '分流总节点'
|
||||
option type 'Xray'
|
||||
option protocol '_shunt'
|
||||
option DirectGame '_direct'
|
||||
option ProxyGame '_default'
|
||||
option AIGC '_default'
|
||||
option Streaming '_default'
|
||||
option Proxy '_default'
|
||||
option Direct '_direct'
|
||||
option default_node '_direct'
|
||||
option domainStrategy 'IPOnDemand'
|
||||
|
||||
config shunt_rules 'DirectGame'
|
||||
option remarks 'DirectGame'
|
||||
option domain_list '# steam直连域名获取国内CDN走国内线路下载
|
||||
cm.steampowered.com
|
||||
steamserver.net
|
||||
|
||||
# steam国内CDN华为云
|
||||
steampipe.steamcontent.tnkjmec.com
|
||||
# steam国内CDN白山云
|
||||
st.dl.eccdnx.com
|
||||
st.dl.bscstorage.net
|
||||
st.dl.pinyuncloud.com
|
||||
# steam国内CDN新流云(原金山云)(支持ipv6)
|
||||
dl.steam.clngaa.com
|
||||
# steam国内CDN网宿
|
||||
cdn.mileweb.cs.steampowered.com.8686c.com
|
||||
cdn-ws.content.steamchina.com
|
||||
# steam国内CDN腾讯云 (蒸汽中国独占)
|
||||
cdn-qc.content.steamchina.com
|
||||
# steam国内CDN阿里云(支持ipv6)
|
||||
cdn-ali.content.steamchina.com
|
||||
xz.pphimalayanrt.com
|
||||
lv.queniujq.cn
|
||||
alibaba.cdn.steampipe.steamcontent.com
|
||||
|
||||
# 国内游戏geosite域名
|
||||
geosite:category-games@cn'
|
||||
option ip_list '# steam直连IP
|
||||
45.121.184.0/24
|
||||
103.10.124.0/23
|
||||
103.28.54.0/24
|
||||
146.66.152.0/24
|
||||
146.66.155.0/24
|
||||
153.254.86.0/24
|
||||
155.133.224.0/22
|
||||
155.133.230.0/24
|
||||
155.133.232.0/23
|
||||
155.133.234.0/24
|
||||
155.133.236.0/22
|
||||
155.133.240.0/23
|
||||
155.133.244.0/23
|
||||
155.133.246.0/24
|
||||
155.133.248.0/21
|
||||
162.254.192.0/21
|
||||
185.25.182.0/23
|
||||
190.217.32.0/22
|
||||
192.69.96.0/22
|
||||
205.196.6.0/24
|
||||
208.64.200.0/22
|
||||
208.78.164.0/22
|
||||
205.185.194.0/24'
|
||||
|
||||
config shunt_rules 'ProxyGame'
|
||||
option remarks 'ProxyGame'
|
||||
option domain_list '# steam 商店/客服/聊天/网页布局/API/二维码/Google云同步 代理URL
|
||||
steamcommunity.com
|
||||
www.steamcommunity.com
|
||||
store.steampowered.com
|
||||
checkout.steampowered.com
|
||||
api.steampowered.com
|
||||
help.steampowered.com
|
||||
login.steampowered.com
|
||||
store.akamai.steamstatic.com
|
||||
steambroadcast.akamaized.net
|
||||
steamvideo-a.akamaihd.net
|
||||
steamusercontent-a.akamaihd.net
|
||||
steamstore-a.akamaihd.net
|
||||
steamcommunity-a.akamaihd.net
|
||||
steamcdn-a.akamaihd.net
|
||||
steamuserimages-a.akamaihd.net
|
||||
community.akamai.steamstatic.com
|
||||
avatars.akamai.steamstatic.com
|
||||
community.steamstatic.com
|
||||
cdn.akamai.steamstatic.com
|
||||
avatars.steamstatic.com
|
||||
shared.akamai.steamstatic.com
|
||||
clan.akamai.steamstatic.com
|
||||
cdn.cloudflare.steamstatic.com
|
||||
community.cloudflare.steamstatic.com
|
||||
store.cloudflare.steamstatic.com
|
||||
avatars.cloudflare.steamstatic.com
|
||||
clan.cloudflare.steamstatic.com
|
||||
shared.cloudflare.steamstatic.com
|
||||
steam-chat.com
|
||||
steamcloud-ugc.storage.googleapis.com
|
||||
steamcloud-eu-ams.storage.googleapis.com
|
||||
steamcloud-eu-fra.storage.googleapis.com
|
||||
steamcloud-finland.storage.googleapis.com
|
||||
steamcloud-saopaulo.storage.googleapis.com
|
||||
steamcloud-singapore.storage.googleapis.com
|
||||
steamcloud-sydney.storage.googleapis.com
|
||||
steamcloud-taiwan.storage.googleapis.com
|
||||
steamcloud-eu.storage.googleapis.com
|
||||
|
||||
geosite:category-games'
|
||||
|
||||
config shunt_rules 'AIGC'
|
||||
option remarks 'AIGC'
|
||||
option domain_list 'geosite:category-ai-!cn
|
||||
geosite:apple-intelligence'
|
||||
|
||||
config shunt_rules 'Streaming'
|
||||
option remarks 'Streaming'
|
||||
option domain_list 'geosite:netflix
|
||||
geosite:disney'
|
||||
|
||||
config shunt_rules 'Proxy'
|
||||
option remarks 'Proxy'
|
||||
option domain_list 'geosite:geolocation-!cn'
|
||||
option ip_list '149.154.160.0/20
|
||||
91.108.4.0/22
|
||||
91.108.56.0/24
|
||||
109.239.140.0/24
|
||||
67.198.55.0/24
|
||||
8.8.4.4
|
||||
8.8.8.8
|
||||
208.67.222.222
|
||||
208.67.220.220
|
||||
1.1.1.1
|
||||
1.1.1.2
|
||||
1.0.0.1
|
||||
9.9.9.9
|
||||
149.112.112.112
|
||||
2001:67c:4e8::/48
|
||||
2001:b28:f23c::/48
|
||||
2001:b28:f23d::/48
|
||||
2001:b28:f23f::/48
|
||||
2001:b28:f242::/48
|
||||
2001:4860:4860::8888
|
||||
2001:4860:4860::8844
|
||||
2606:4700:4700::1111
|
||||
2606:4700:4700::1001'
|
||||
|
||||
config shunt_rules 'Direct'
|
||||
option remarks 'Direct'
|
||||
option domain_list 'geosite:cn'
|
||||
option ip_list '223.5.5.5/32
|
||||
223.6.6.6/32
|
||||
119.29.29.29/32
|
||||
180.76.76.76/32
|
||||
114.114.114.114/32
|
||||
114.114.115.115/32
|
||||
1.12.12.12/32
|
||||
120.53.53.53/32
|
||||
geoip:cn
|
||||
geoip:private'
|
||||
2275
luci-app-passwall/root/usr/share/passwall/app.sh
Executable file
2275
luci-app-passwall/root/usr/share/passwall/app.sh
Executable file
File diff suppressed because it is too large
Load Diff
239
luci-app-passwall/root/usr/share/passwall/haproxy.lua
Normal file
239
luci-app-passwall/root/usr/share/passwall/haproxy.lua
Normal file
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local api = require ("luci.passwall.api")
|
||||
local appname = "passwall"
|
||||
local fs = api.fs
|
||||
local jsonc = api.jsonc
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
|
||||
local log = function(...)
|
||||
api.log(...)
|
||||
end
|
||||
|
||||
function get_ip_port_from(str)
|
||||
local result_port = sys.exec("echo -n " .. str .. " | sed -n 's/^.*[:#]\\([0-9]*\\)$/\\1/p'")
|
||||
local result_ip = sys.exec(string.format("__host=%s;__varport=%s;", str, result_port) .. "echo -n ${__host%%${__varport:+[:#]${__varport}*}}")
|
||||
return result_ip, result_port
|
||||
end
|
||||
|
||||
local new_port
|
||||
local function get_new_port()
|
||||
if new_port then
|
||||
new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port %s tcp)", appname, new_port + 1)))
|
||||
else
|
||||
new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port auto tcp)", appname)))
|
||||
end
|
||||
return new_port
|
||||
end
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local haproxy_path = var["-path"]
|
||||
local haproxy_conf = var["-conf"]
|
||||
local haproxy_dns = var["-dns"] or "119.29.29.29:53,223.5.5.5:53"
|
||||
|
||||
local cpu_thread = sys.exec('echo -n $(cat /proc/cpuinfo | grep "processor" | wc -l)') or "1"
|
||||
local health_check_type = uci:get(appname, "@global_haproxy[0]", "health_check_type") or "tcp"
|
||||
local health_check_inter = uci:get(appname, "@global_haproxy[0]", "health_check_inter") or "10"
|
||||
local console_port = uci:get(appname, "@global_haproxy[0]", "console_port")
|
||||
local bind_local = uci:get(appname, "@global_haproxy[0]", "bind_local") or "0"
|
||||
local bind_address = "0.0.0.0"
|
||||
if bind_local == "1" then bind_address = "127.0.0.1" end
|
||||
|
||||
log("HAPROXY 负载均衡:")
|
||||
log(string.format(" * 控制台端口:%s", console_port))
|
||||
fs.mkdir(haproxy_path)
|
||||
local haproxy_file = haproxy_path .. "/" .. haproxy_conf
|
||||
|
||||
local f_out = io.open(haproxy_file, "a")
|
||||
|
||||
local haproxy_config = [[
|
||||
global
|
||||
daemon
|
||||
log 127.0.0.1 local2
|
||||
maxconn 60000
|
||||
stats socket {{path}}/haproxy.sock
|
||||
nbthread {{nbthread}}
|
||||
external-check
|
||||
insecure-fork-wanted
|
||||
|
||||
defaults
|
||||
mode tcp
|
||||
log global
|
||||
option tcplog
|
||||
option dontlognull
|
||||
option http-server-close
|
||||
#option forwardfor except 127.0.0.0/8
|
||||
option redispatch
|
||||
retries 2
|
||||
timeout http-request 10s
|
||||
timeout queue 1m
|
||||
timeout connect 10s
|
||||
timeout client 1m
|
||||
timeout server 1m
|
||||
timeout http-keep-alive 10s
|
||||
timeout check 10s
|
||||
maxconn 3000
|
||||
|
||||
resolvers mydns
|
||||
resolve_retries 1
|
||||
timeout resolve 5s
|
||||
hold valid 600s
|
||||
{{dns}}
|
||||
]]
|
||||
|
||||
haproxy_config = haproxy_config:gsub("{{path}}", haproxy_path)
|
||||
haproxy_config = haproxy_config:gsub("{{nbthread}}", cpu_thread)
|
||||
|
||||
local mydns = ""
|
||||
local index = 0
|
||||
string.gsub(haproxy_dns, '[^' .. "," .. ']+', function(w)
|
||||
index = index + 1
|
||||
local s = w:gsub("#", ":")
|
||||
if not s:find(":") then
|
||||
s = s .. ":53"
|
||||
end
|
||||
mydns = mydns .. (index > 1 and "\n" or "") .. " " .. string.format("nameserver dns%s %s", index, s)
|
||||
end)
|
||||
haproxy_config = haproxy_config:gsub("{{dns}}", mydns)
|
||||
|
||||
f_out:write(haproxy_config)
|
||||
|
||||
local listens = {}
|
||||
|
||||
uci:foreach(appname, "haproxy_config", function(t)
|
||||
if t.enabled == "1" then
|
||||
local server_remark
|
||||
local server_address
|
||||
local server_port
|
||||
local lbss = t.lbss
|
||||
local listen_port = tonumber(t.haproxy_port) or 0
|
||||
local server_node = uci:get_all(appname, lbss)
|
||||
if server_node and server_node.address and server_node.port then
|
||||
server_remark = server_node.address .. ":" .. server_node.port
|
||||
server_address = server_node.address
|
||||
server_port = server_node.port
|
||||
t.origin_address = server_address
|
||||
t.origin_port = server_port
|
||||
if health_check_type == "passwall_logic" then
|
||||
if server_node.type ~= "Socks" then
|
||||
local relay_port = server_node.port
|
||||
new_port = get_new_port()
|
||||
local config_file = string.format("haproxy_%s_%s.json", t[".name"], new_port)
|
||||
sys.call(string.format('/usr/share/%s/app.sh run_socks "%s"> /dev/null',
|
||||
appname,
|
||||
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s",
|
||||
new_port, --flag
|
||||
server_node[".name"], --node
|
||||
"127.0.0.1", --bind
|
||||
new_port, --socks port
|
||||
config_file --config file
|
||||
)
|
||||
)
|
||||
)
|
||||
server_address = "127.0.0.1"
|
||||
server_port = new_port
|
||||
end
|
||||
end
|
||||
else
|
||||
server_address, server_port = get_ip_port_from(lbss)
|
||||
server_remark = server_address .. ":" .. server_port
|
||||
t.origin_address = server_address
|
||||
t.origin_port = server_port
|
||||
end
|
||||
if server_address and server_port and listen_port > 0 then
|
||||
if not listens[listen_port] then
|
||||
listens[listen_port] = {}
|
||||
end
|
||||
t.server_remark = server_remark
|
||||
t.server_address = server_address
|
||||
t.server_port = server_port
|
||||
table.insert(listens[listen_port], t)
|
||||
else
|
||||
log(" - 丢弃1个明显无效的节点")
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local sortTable = {}
|
||||
for i in pairs(listens) do
|
||||
if i ~= nil then
|
||||
table.insert(sortTable, i)
|
||||
end
|
||||
end
|
||||
table.sort(sortTable, function(a,b) return (a < b) end)
|
||||
|
||||
for i, port in pairs(sortTable) do
|
||||
log(" + 入口 %s:%s" % {bind_address, port})
|
||||
|
||||
f_out:write("\n" .. string.format([[
|
||||
listen %s
|
||||
bind %s:%s
|
||||
mode tcp
|
||||
balance roundrobin
|
||||
]], port, bind_address, port))
|
||||
|
||||
if health_check_type == "passwall_logic" then
|
||||
f_out:write(string.format([[
|
||||
option external-check
|
||||
external-check command "/usr/share/passwall/haproxy_check.sh"
|
||||
]], port, port))
|
||||
end
|
||||
|
||||
local count_M, count_B = 1, 1
|
||||
for i, o in ipairs(listens[port]) do
|
||||
local remark = o.server_remark or ""
|
||||
-- 防止重名导致无法运行
|
||||
if tostring(o.backup) ~= "1" then
|
||||
remark = "M" .. count_M .. "-" .. remark
|
||||
count_M = count_M + 1
|
||||
else
|
||||
remark = "B" .. count_B .. "-" .. remark
|
||||
count_B = count_B + 1
|
||||
end
|
||||
local server = o.server_address .. ":" .. o.server_port
|
||||
local server_conf = "server {{remark}} {{server}} weight {{weight}} {{resolvers}} check inter {{inter}} rise 1 fall 3 {{backup}}"
|
||||
server_conf = server_conf:gsub("{{remark}}", remark)
|
||||
server_conf = server_conf:gsub("{{server}}", server)
|
||||
server_conf = server_conf:gsub("{{weight}}", o.lbweight)
|
||||
local resolvers = "resolvers mydns"
|
||||
if api.is_ip(o.server_address) then
|
||||
resolvers = ""
|
||||
end
|
||||
server_conf = server_conf:gsub("{{resolvers}}", resolvers)
|
||||
server_conf = server_conf:gsub("{{inter}}", tonumber(health_check_inter) .. "s")
|
||||
server_conf = server_conf:gsub("{{backup}}", tostring(o.backup) == "1" and "backup" or "")
|
||||
|
||||
f_out:write(" " .. server_conf .. "\n")
|
||||
|
||||
if o.export ~= "0" then
|
||||
sys.call(string.format("/usr/share/passwall/app.sh add_ip2route %s %s", o.origin_address, o.export))
|
||||
end
|
||||
|
||||
log(string.format(" | - 出口节点:%s:%s,权重:%s", o.origin_address, o.origin_port, o.lbweight))
|
||||
end
|
||||
end
|
||||
|
||||
--控制台配置
|
||||
local console_user = uci:get(appname, "@global_haproxy[0]", "console_user")
|
||||
local console_password = uci:get(appname, "@global_haproxy[0]", "console_password")
|
||||
local str = [[
|
||||
listen console
|
||||
bind 0.0.0.0:%s
|
||||
mode http
|
||||
stats refresh 30s
|
||||
stats uri /
|
||||
stats admin if TRUE
|
||||
%s
|
||||
]]
|
||||
f_out:write("\n" .. string.format(str, console_port, (console_user and console_user ~= "" and console_password and console_password ~= "") and "stats auth " .. console_user .. ":" .. console_password or ""))
|
||||
|
||||
f_out:close()
|
||||
|
||||
--passwall内置健康检查URL
|
||||
if health_check_type == "passwall_logic" then
|
||||
local probeUrl = uci:get(appname, "@global_haproxy[0]", "health_probe_url") or "https://www.google.com/generate_204"
|
||||
local f_url = io.open(haproxy_path .. "/Probe_URL", "w")
|
||||
f_url:write(probeUrl)
|
||||
f_url:close()
|
||||
end
|
||||
37
luci-app-passwall/root/usr/share/passwall/haproxy_check.sh
Executable file
37
luci-app-passwall/root/usr/share/passwall/haproxy_check.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
export PATH=/usr/sbin:/usr/bin:/sbin:/bin:/root/bin
|
||||
CONFIG=passwall
|
||||
|
||||
listen_address=$1
|
||||
listen_port=$2
|
||||
server_address=$3
|
||||
server_port=$4
|
||||
|
||||
pgrep -af "${CONFIG}/" | awk '/app\.sh.*(start|stop)/ || /nftables\.sh/ || /iptables\.sh/ { found = 1 } END { exit !found }' && {
|
||||
# 特定任务执行中不检测
|
||||
exit 0
|
||||
}
|
||||
|
||||
probe_file="/tmp/etc/passwall/haproxy/Probe_URL"
|
||||
probeUrl="https://www.google.com/generate_204"
|
||||
if [ -f "$probe_file" ]; then
|
||||
firstLine=$(head -n 1 "$probe_file" | tr -d ' \t\n')
|
||||
[ -n "$firstLine" ] && probeUrl="$firstLine"
|
||||
fi
|
||||
|
||||
extra_params="-x socks5h://${server_address}:${server_port}"
|
||||
if /usr/bin/curl --help all | grep -q "\-\-retry-all-errors"; then
|
||||
extra_params="${extra_params} --retry-all-errors"
|
||||
fi
|
||||
|
||||
status=$(/usr/bin/curl -I -o /dev/null -skL ${extra_params} --connect-timeout 3 --retry 2 --max-time 10 -w "%{http_code}" "${probeUrl}")
|
||||
|
||||
case "$status" in
|
||||
200|204)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,525 @@
|
||||
local sys = require "luci.sys"
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = "passwall"
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local FLAG = var["-FLAG"]
|
||||
local LISTEN_PORT = var["-LISTEN_PORT"]
|
||||
local DNS_LOCAL = var["-DNS_LOCAL"]
|
||||
local DNS_TRUST = var["-DNS_TRUST"]
|
||||
local USE_DIRECT_LIST = var["-USE_DIRECT_LIST"]
|
||||
local USE_PROXY_LIST = var["-USE_PROXY_LIST"]
|
||||
local USE_BLOCK_LIST = var["-USE_BLOCK_LIST"]
|
||||
local GFWLIST = var["-GFWLIST"]
|
||||
local CHNLIST = var["-CHNLIST"]
|
||||
local NO_IPV6_TRUST = var["-NO_IPV6_TRUST"]
|
||||
local DEFAULT_MODE = var["-DEFAULT_MODE"]
|
||||
local DEFAULT_TAG = var["-DEFAULT_TAG"]
|
||||
local NO_LOGIC_LOG = var["-NO_LOGIC_LOG"]
|
||||
local TCP_NODE = var["-TCP_NODE"]
|
||||
local NFTFLAG = var["-NFTFLAG"]
|
||||
local REMOTE_FAKEDNS = var["-REMOTE_FAKEDNS"]
|
||||
local LOG_FILE = var["-LOG_FILE"]
|
||||
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
local fs = api.fs
|
||||
local datatypes = api.datatypes
|
||||
|
||||
local TMP_PATH = "/tmp/etc/" .. appname
|
||||
local TMP_ACL_PATH = TMP_PATH .. "/acl"
|
||||
local RULES_PATH = "/usr/share/" .. appname .. "/rules"
|
||||
local FLAG_PATH = TMP_ACL_PATH .. "/" .. FLAG
|
||||
local config_lines = {}
|
||||
local tmp_lines = {}
|
||||
local USE_GEOVIEW = uci:get(appname, "@global_rules[0]", "enable_geoview")
|
||||
|
||||
local function log(...)
|
||||
if NO_LOGIC_LOG == "1" then
|
||||
return
|
||||
end
|
||||
api.log(...)
|
||||
end
|
||||
|
||||
local function is_file_nonzero(path)
|
||||
if path and #path > 1 then
|
||||
if sys.exec('[ -s "%s" ] && echo -n 1' % path) == "1" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function insert_unique(dest_table, value, lookup_table)
|
||||
if not lookup_table[value] then
|
||||
table.insert(dest_table, value)
|
||||
lookup_table[value] = true
|
||||
end
|
||||
end
|
||||
|
||||
local function merge_array(array1, array2)
|
||||
for i, line in ipairs(array2) do
|
||||
table.insert(array1, #array1 + 1, line)
|
||||
end
|
||||
end
|
||||
|
||||
local function insert_array_before(array1, array2, target) --将array2插入到array1的target前面,target不存在则追加
|
||||
for i, line in ipairs(array1) do
|
||||
if line == target then
|
||||
for j = #array2, 1, -1 do
|
||||
table.insert(array1, i, array2[j])
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
merge_array(array1, array2)
|
||||
end
|
||||
|
||||
local function insert_array_after(array1, array2, target) --将array2插入到array1的target后面,target不存在则追加
|
||||
for i, line in ipairs(array1) do
|
||||
if line == target then
|
||||
for j = 1, #array2 do
|
||||
table.insert(array1, i + j, array2[j])
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
merge_array(array1, array2)
|
||||
end
|
||||
|
||||
local function get_geosite(list_arg, out_path)
|
||||
local geosite_path = uci:get(appname, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/"
|
||||
geosite_path = geosite_path:match("^(.*)/") .. "/geosite.dat"
|
||||
if not is_file_nonzero(geosite_path) then return 1 end
|
||||
if api.is_finded("geoview") and list_arg and out_path then
|
||||
sys.exec("geoview -type geosite -append=true -input " .. geosite_path .. " -list '" .. list_arg .. "' -output " .. out_path)
|
||||
return 0
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
if not fs.access(FLAG_PATH) then
|
||||
fs.mkdir(FLAG_PATH)
|
||||
end
|
||||
|
||||
local setflag = (NFTFLAG == "1") and "inet@passwall@" or ""
|
||||
|
||||
local only_global = (DEFAULT_MODE == "proxy" and CHNLIST == "0" and GFWLIST == "0") and 1
|
||||
|
||||
local force_https_soa = uci:get(appname, "@global[0]", "force_https_soa") or 1
|
||||
|
||||
config_lines = {
|
||||
LOG_FILE ~= "/dev/null" and "verbose" or "",
|
||||
"bind-addr ::",
|
||||
"bind-port " .. LISTEN_PORT,
|
||||
"china-dns " .. DNS_LOCAL,
|
||||
"trust-dns " .. DNS_TRUST,
|
||||
tonumber(force_https_soa) == 1 and "filter-qtype 65" or ""
|
||||
}
|
||||
|
||||
for i = 1, 6 do
|
||||
table.insert(config_lines, "#--" .. i)
|
||||
end
|
||||
|
||||
--自定义规则组,后声明的组具有更高优先级
|
||||
--屏蔽列表
|
||||
local file_block_host = TMP_ACL_PATH .. "/block_host"
|
||||
if USE_BLOCK_LIST == "1" and not fs.access(file_block_host) then
|
||||
local block_domain, lookup_block_domain = {}, {}
|
||||
local geosite_arg = ""
|
||||
for line in io.lines(RULES_PATH .. "/block_host") do
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
insert_unique(block_domain, line, lookup_block_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #block_domain > 0 then
|
||||
local f_out = io.open(file_block_host, "w")
|
||||
for i = 1, #block_domain do
|
||||
f_out:write(block_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" and api.is_finded("geoview") then
|
||||
if get_geosite(geosite_arg, file_block_host) == 0 then
|
||||
log(" * 解析[屏蔽列表] Geosite 到屏蔽域名表(blocklist)完成")
|
||||
else
|
||||
log(" * 解析[屏蔽列表] Geosite 到屏蔽域名表(blocklist)失败!")
|
||||
end
|
||||
end
|
||||
end
|
||||
if USE_BLOCK_LIST == "1" and is_file_nonzero(file_block_host) then
|
||||
tmp_lines = {
|
||||
"group null",
|
||||
"group-dnl " .. file_block_host
|
||||
}
|
||||
insert_array_after(config_lines, tmp_lines, "#--5")
|
||||
end
|
||||
|
||||
--始终用国内DNS解析节点域名
|
||||
local file_vpslist = TMP_ACL_PATH .. "/vpslist"
|
||||
if not is_file_nonzero(file_vpslist) then
|
||||
local f_out = io.open(file_vpslist, "w")
|
||||
local written_domains = {}
|
||||
uci:foreach(appname, "nodes", function(t)
|
||||
local function process_address(address)
|
||||
if address == "engage.cloudflareclient.com" then return end
|
||||
if datatypes.hostname(address) and not written_domains[address] then
|
||||
f_out:write(address .. "\n")
|
||||
written_domains[address] = true
|
||||
end
|
||||
end
|
||||
process_address(t.address)
|
||||
process_address(t.download_address)
|
||||
end)
|
||||
f_out:close()
|
||||
end
|
||||
if is_file_nonzero(file_vpslist) then
|
||||
local sets = {
|
||||
setflag .. "passwall_vps",
|
||||
setflag .. "passwall_vps6"
|
||||
}
|
||||
tmp_lines = {
|
||||
"group vpslist",
|
||||
"group-dnl " .. file_vpslist,
|
||||
"group-upstream " .. DNS_LOCAL,
|
||||
"group-ipset " .. table.concat(sets, ",")
|
||||
}
|
||||
insert_array_after(config_lines, tmp_lines, "#--6")
|
||||
log(string.format(" - 节点列表中的域名(vpslist):%s", DNS_LOCAL or "默认"))
|
||||
end
|
||||
|
||||
--直连(白名单)列表
|
||||
local file_direct_host = TMP_ACL_PATH .. "/direct_host"
|
||||
if USE_DIRECT_LIST == "1" and not fs.access(file_direct_host) then
|
||||
local direct_domain, lookup_direct_domain = {}, {}
|
||||
local geosite_arg = ""
|
||||
for line in io.lines(RULES_PATH .. "/direct_host") do
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
insert_unique(direct_domain, line, lookup_direct_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #direct_domain > 0 then
|
||||
local f_out = io.open(file_direct_host, "w")
|
||||
for i = 1, #direct_domain do
|
||||
f_out:write(direct_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" and api.is_finded("geoview") then
|
||||
if get_geosite(geosite_arg, file_direct_host) == 0 then
|
||||
log(" * 解析[直连列表] Geosite 到域名白名单(whitelist)完成")
|
||||
else
|
||||
log(" * 解析[直连列表] Geosite 到域名白名单(whitelist)失败!")
|
||||
end
|
||||
end
|
||||
end
|
||||
if USE_DIRECT_LIST == "1" and is_file_nonzero(file_direct_host) then
|
||||
local sets = {
|
||||
setflag .. "passwall_white",
|
||||
setflag .. "passwall_white6"
|
||||
}
|
||||
tmp_lines = {
|
||||
"group directlist",
|
||||
"group-dnl " .. file_direct_host,
|
||||
"group-upstream " .. DNS_LOCAL,
|
||||
"group-ipset " .. table.concat(sets, ",")
|
||||
}
|
||||
insert_array_after(config_lines, tmp_lines, "#--4")
|
||||
log(string.format(" - 域名白名单(whitelist):%s", DNS_LOCAL or "默认"))
|
||||
end
|
||||
|
||||
--代理(黑名单)列表
|
||||
local file_proxy_host = TMP_ACL_PATH .. "/proxy_host"
|
||||
if USE_PROXY_LIST == "1" and not fs.access(file_proxy_host) then
|
||||
local proxy_domain, lookup_proxy_domain = {}, {}
|
||||
local geosite_arg = ""
|
||||
for line in io.lines(RULES_PATH .. "/proxy_host") do
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
insert_unique(proxy_domain, line, lookup_proxy_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #proxy_domain > 0 then
|
||||
local f_out = io.open(file_proxy_host, "w")
|
||||
for i = 1, #proxy_domain do
|
||||
f_out:write(proxy_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" and api.is_finded("geoview") then
|
||||
if get_geosite(geosite_arg, file_proxy_host) == 0 then
|
||||
log(" * 解析[代理列表] Geosite 到代理域名表(blacklist)完成")
|
||||
else
|
||||
log(" * 解析[代理列表] Geosite 到代理域名表(blacklist)失败!")
|
||||
end
|
||||
end
|
||||
end
|
||||
if USE_PROXY_LIST == "1" and is_file_nonzero(file_proxy_host) then
|
||||
local sets = {
|
||||
setflag .. "passwall_black",
|
||||
setflag .. "passwall_black6"
|
||||
}
|
||||
if FLAG ~= "default" then
|
||||
sets = {
|
||||
setflag .. "passwall_" .. FLAG .. "_black",
|
||||
setflag .. "passwall_" .. FLAG .. "_black6"
|
||||
}
|
||||
end
|
||||
tmp_lines = {
|
||||
"group proxylist",
|
||||
"group-dnl " .. file_proxy_host,
|
||||
"group-upstream " .. DNS_TRUST,
|
||||
REMOTE_FAKEDNS ~= "1" and "group-ipset " .. table.concat(sets, ",") or ""
|
||||
}
|
||||
if NO_IPV6_TRUST == "1" then table.insert(tmp_lines, "no-ipv6 tag:proxylist") end
|
||||
insert_array_after(config_lines, tmp_lines, "#--3")
|
||||
log(string.format(" - 代理域名表(blacklist):%s", DNS_TRUST or "默认"))
|
||||
end
|
||||
|
||||
--内置组(chn/gfw)优先级在自定义组后
|
||||
--GFW列表
|
||||
if GFWLIST == "1" and is_file_nonzero(RULES_PATH .. "/gfwlist") then
|
||||
local sets = {
|
||||
setflag .. "passwall_gfw",
|
||||
setflag .. "passwall_gfw6"
|
||||
}
|
||||
if FLAG ~= "default" then
|
||||
sets = {
|
||||
setflag .. "passwall_" .. FLAG .. "_gfw",
|
||||
setflag .. "passwall_" .. FLAG .. "_gfw6"
|
||||
}
|
||||
end
|
||||
tmp_lines = {
|
||||
"gfwlist-file " .. RULES_PATH .. "/gfwlist",
|
||||
REMOTE_FAKEDNS ~= "1" and "add-taggfw-ip " .. table.concat(sets, ",") or ""
|
||||
}
|
||||
if NO_IPV6_TRUST == "1" then table.insert(tmp_lines, "no-ipv6 tag:gfw") end
|
||||
merge_array(config_lines, tmp_lines)
|
||||
log(string.format(" - 防火墙域名表(gfwlist):%s", DNS_TRUST or "默认"))
|
||||
end
|
||||
|
||||
--中国列表
|
||||
if CHNLIST ~= "0" and is_file_nonzero(RULES_PATH .. "/chnlist") then
|
||||
if CHNLIST == "direct" then
|
||||
tmp_lines = {
|
||||
"chnlist-file " .. RULES_PATH .. "/chnlist",
|
||||
"ipset-name4 " .. setflag .. "passwall_chn",
|
||||
"ipset-name6 " .. setflag .. "passwall_chn6",
|
||||
"add-tagchn-ip",
|
||||
"chnlist-first"
|
||||
}
|
||||
merge_array(config_lines, tmp_lines)
|
||||
log(string.format(" - 中国域名表(chnroute):%s", DNS_LOCAL or "默认"))
|
||||
end
|
||||
|
||||
--回中国模式
|
||||
if CHNLIST == "proxy" then
|
||||
local sets = {
|
||||
setflag .. "passwall_chn",
|
||||
setflag .. "passwall_chn6"
|
||||
}
|
||||
tmp_lines = {
|
||||
"group chn_proxy",
|
||||
"group-dnl " .. RULES_PATH .. "/chnlist",
|
||||
"group-upstream " .. DNS_TRUST,
|
||||
REMOTE_FAKEDNS ~= "1" and "group-ipset " .. table.concat(sets, ",") or ""
|
||||
}
|
||||
if NO_IPV6_TRUST == "1" then table.insert(tmp_lines, "no-ipv6 tag:chn_proxy") end
|
||||
insert_array_after(config_lines, tmp_lines, "#--1")
|
||||
log(string.format(" - 中国域名表(chnroute):%s", DNS_TRUST or "默认"))
|
||||
end
|
||||
end
|
||||
|
||||
--分流规则
|
||||
if uci:get(appname, TCP_NODE, "protocol") == "_shunt" then
|
||||
local white_domain, lookup_white_domain = {}, {}
|
||||
local shunt_domain, lookup_shunt_domain = {}, {}
|
||||
local file_white_host = FLAG_PATH .. "/shunt_direct_host"
|
||||
local file_shunt_host = FLAG_PATH .. "/shunt_proxy_host"
|
||||
local geosite_white_arg, geosite_shunt_arg = "", ""
|
||||
|
||||
local t = uci:get_all(appname, TCP_NODE)
|
||||
local default_node_id = t["default_node"] or "_direct"
|
||||
uci:foreach(appname, "shunt_rules", function(s)
|
||||
local _node_id = t[s[".name"]]
|
||||
if _node_id and _node_id ~= "_blackhole" then
|
||||
if _node_id == "_default" then
|
||||
_node_id = default_node_id
|
||||
end
|
||||
|
||||
local domain_list = s.domain_list or ""
|
||||
for line in string.gmatch(domain_list, "[^\r\n]+") do
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") then
|
||||
if line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
if _node_id == "_direct" then
|
||||
geosite_white_arg = geosite_white_arg .. (geosite_white_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
geosite_shunt_arg = geosite_shunt_arg .. (geosite_shunt_arg ~= "" and "," or "") .. line
|
||||
end
|
||||
else
|
||||
if line:find("domain:") or line:find("full:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
end
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
if _node_id == "_direct" then
|
||||
insert_unique(white_domain, line, lookup_white_domain)
|
||||
else
|
||||
insert_unique(shunt_domain, line, lookup_shunt_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if _node_id ~= "_direct" then
|
||||
log(string.format(" - Sing-Box/Xray分流规则(%s):%s", s.remarks, DNS_TRUST or "默认"))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if is_file_nonzero(file_white_host) == nil then
|
||||
if #white_domain > 0 then
|
||||
local f_out = io.open(file_white_host, "w")
|
||||
for i = 1, #white_domain do
|
||||
f_out:write(white_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
end
|
||||
|
||||
if is_file_nonzero(file_shunt_host) == nil then
|
||||
if #shunt_domain > 0 then
|
||||
local f_out = io.open(file_shunt_host, "w")
|
||||
for i = 1, #shunt_domain do
|
||||
f_out:write(shunt_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
end
|
||||
|
||||
if GFWLIST == "1" and CHNLIST == "0" and USE_GEOVIEW == "1" and api.is_finded("geoview") then --仅GFW模式解析geosite
|
||||
local return_white, return_shunt
|
||||
if geosite_white_arg ~= "" then
|
||||
return_white = get_geosite(geosite_white_arg, file_white_host)
|
||||
end
|
||||
if geosite_shunt_arg ~= "" then
|
||||
return_shunt = get_geosite(geosite_shunt_arg, file_shunt_host)
|
||||
end
|
||||
if (return_white == nil or return_white == 0) and (return_shunt == nil or return_shunt == 0) then
|
||||
log(" * 解析[分流节点] Geosite 完成")
|
||||
else
|
||||
log(" * 解析[分流节点] Geosite 失败!")
|
||||
end
|
||||
end
|
||||
|
||||
local sets = {
|
||||
setflag .. "passwall_shunt",
|
||||
setflag .. "passwall_shunt6"
|
||||
}
|
||||
if FLAG ~= "default" then
|
||||
sets = {
|
||||
setflag .. "passwall_" .. FLAG .. "_shunt",
|
||||
setflag .. "passwall_" .. FLAG .. "_shunt6"
|
||||
}
|
||||
end
|
||||
|
||||
if is_file_nonzero(file_white_host) then
|
||||
if USE_DIRECT_LIST == "1" then
|
||||
--当白名单启用时,添加到白名单组一同处理
|
||||
for i, v in ipairs(config_lines) do
|
||||
if v == "group-dnl " .. file_direct_host then
|
||||
config_lines[i] = "group-dnl " .. file_direct_host .. "," .. file_white_host
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
--当白名单不启用时,创建新组,ipset到shuntlist
|
||||
tmp_lines = {
|
||||
"group whitelist",
|
||||
"group-dnl " .. file_white_host,
|
||||
"group-upstream " .. DNS_LOCAL,
|
||||
"group-ipset " .. table.concat(sets, ",")
|
||||
}
|
||||
insert_array_after(config_lines, tmp_lines, "#--4")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if is_file_nonzero(file_shunt_host) then
|
||||
tmp_lines = {
|
||||
"group shuntlist",
|
||||
"group-dnl " .. file_shunt_host,
|
||||
"group-upstream " .. DNS_TRUST,
|
||||
(not only_global and REMOTE_FAKEDNS == "1") and "" or ("group-ipset " .. table.concat(sets, ","))
|
||||
}
|
||||
if NO_IPV6_TRUST == "1" then table.insert(tmp_lines, "no-ipv6 tag:shuntlist") end
|
||||
insert_array_after(config_lines, tmp_lines, "#--2")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--只使用gfwlist模式,GFW列表以外的域名及默认使用本地DNS
|
||||
if GFWLIST == "1" and CHNLIST == "0" then DEFAULT_TAG = "chn" end
|
||||
|
||||
--回中国模式,中国列表以外的域名及默认使用本地DNS
|
||||
if CHNLIST == "proxy" then DEFAULT_TAG = "chn" end
|
||||
|
||||
--全局模式,默认使用远程DNS
|
||||
if only_global then
|
||||
DEFAULT_TAG = "gfw"
|
||||
if NO_IPV6_TRUST == "1" and uci:get(appname, TCP_NODE, "protocol") ~= "_shunt" then
|
||||
table.insert(config_lines, "no-ipv6")
|
||||
end
|
||||
end
|
||||
|
||||
--是否接受直连 DNS 空响应
|
||||
if DEFAULT_TAG == "none_noip" then table.insert(config_lines, "noip-as-chnip") end
|
||||
|
||||
if DEFAULT_TAG == nil or DEFAULT_TAG == "smart" or DEFAULT_TAG == "none_noip" then DEFAULT_TAG = "none" end
|
||||
|
||||
table.insert(config_lines, "default-tag " .. DEFAULT_TAG)
|
||||
|
||||
if DEFAULT_TAG == "none" then
|
||||
table.insert(config_lines, "verdict-cache 5000")
|
||||
end
|
||||
|
||||
table.insert(config_lines, "hosts")
|
||||
|
||||
if DEFAULT_TAG == "chn" then
|
||||
log(string.format(" - 默认 DNS :%s", DNS_LOCAL))
|
||||
elseif DEFAULT_TAG == "gfw" then
|
||||
log(string.format(" - 默认 DNS :%s", DNS_TRUST))
|
||||
else
|
||||
log(string.format(" - 默认 DNS :%s", "智能匹配"))
|
||||
end
|
||||
|
||||
--输出配置文件
|
||||
if #config_lines > 0 then
|
||||
for i = 1, #config_lines do
|
||||
line = config_lines[i]
|
||||
if line ~= "" and not line:find("^#--") then
|
||||
print(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
log(" - ChinaDNS-NG已作为Dnsmasq上游,如果你自行配置了错误的DNS流程,将会导致域名(直连/代理域名)分流失效!!!")
|
||||
720
luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua
Normal file
720
luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua
Normal file
@@ -0,0 +1,720 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = "passwall"
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
local fs = api.fs
|
||||
local datatypes = api.datatypes
|
||||
local TMP = {}
|
||||
|
||||
local function tinsert(table_name, val)
|
||||
if table_name and type(table_name) == "table" then
|
||||
if not TMP[table_name] then
|
||||
TMP[table_name] = {}
|
||||
end
|
||||
if TMP[table_name][val] then
|
||||
return false
|
||||
end
|
||||
table.insert(table_name, val)
|
||||
TMP[table_name][val] = true
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function backup_servers()
|
||||
local DNSMASQ_DNS = uci:get("dhcp", "@dnsmasq[0]", "server")
|
||||
if DNSMASQ_DNS and #DNSMASQ_DNS > 0 then
|
||||
uci:set(appname, "@global[0]", "dnsmasq_servers", DNSMASQ_DNS)
|
||||
api.uci_save(uci, appname, true)
|
||||
end
|
||||
end
|
||||
|
||||
local function restore_servers()
|
||||
local dns_table = {}
|
||||
local DNSMASQ_DNS = uci:get("dhcp", "@dnsmasq[0]", "server")
|
||||
if DNSMASQ_DNS and #DNSMASQ_DNS > 0 then
|
||||
for k, v in ipairs(DNSMASQ_DNS) do
|
||||
tinsert(dns_table, v)
|
||||
end
|
||||
end
|
||||
local OLD_SERVER = uci:get(appname, "@global[0]", "dnsmasq_servers")
|
||||
if OLD_SERVER and #OLD_SERVER > 0 then
|
||||
for k, v in ipairs(OLD_SERVER) do
|
||||
tinsert(dns_table, v)
|
||||
end
|
||||
uci:delete(appname, "@global[0]", "dnsmasq_servers")
|
||||
api.uci_save(uci, appname, true)
|
||||
end
|
||||
if dns_table and #dns_table > 0 then
|
||||
uci:set_list("dhcp", "@dnsmasq[0]", "server", dns_table)
|
||||
api.uci_save(uci, "dhcp", true)
|
||||
end
|
||||
end
|
||||
|
||||
function stretch()
|
||||
local dnsmasq_server = uci:get("dhcp", "@dnsmasq[0]", "server")
|
||||
local dnsmasq_noresolv = uci:get("dhcp", "@dnsmasq[0]", "noresolv")
|
||||
local _flag
|
||||
if dnsmasq_server and #dnsmasq_server > 0 then
|
||||
for k, v in ipairs(dnsmasq_server) do
|
||||
if not v:find("/") then
|
||||
_flag = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if not _flag and dnsmasq_noresolv == "1" then
|
||||
uci:delete("dhcp", "@dnsmasq[0]", "noresolv")
|
||||
local RESOLVFILE = "/tmp/resolv.conf.d/resolv.conf.auto"
|
||||
local file = io.open(RESOLVFILE, "r")
|
||||
if not file then
|
||||
RESOLVFILE = "/tmp/resolv.conf.auto"
|
||||
else
|
||||
local size = file:seek("end")
|
||||
file:close()
|
||||
if size == 0 then
|
||||
RESOLVFILE = "/tmp/resolv.conf.auto"
|
||||
end
|
||||
end
|
||||
uci:set("dhcp", "@dnsmasq[0]", "resolvfile", RESOLVFILE)
|
||||
api.uci_save(uci, "dhcp", true)
|
||||
end
|
||||
end
|
||||
|
||||
function restart(var)
|
||||
local LOG = var["-LOG"]
|
||||
sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
|
||||
if LOG == "1" then
|
||||
api.log("重启 dnsmasq 服务")
|
||||
end
|
||||
end
|
||||
|
||||
function logic_restart(var)
|
||||
local LOG = var["-LOG"]
|
||||
local DEFAULT_DNS = api.get_cache_var("DEFAULT_DNS")
|
||||
if DEFAULT_DNS then
|
||||
backup_servers()
|
||||
--sys.call("sed -i '/list server/d' /etc/config/dhcp >/dev/null 2>&1")
|
||||
local dns_table = {}
|
||||
local dnsmasq_server = uci:get("dhcp", "@dnsmasq[0]", "server")
|
||||
if dnsmasq_server and #dnsmasq_server > 0 then
|
||||
for k, v in ipairs(dnsmasq_server) do
|
||||
if v:find("/") then
|
||||
tinsert(dns_table, v)
|
||||
end
|
||||
end
|
||||
uci:set_list("dhcp", "@dnsmasq[0]", "server", dns_table)
|
||||
api.uci_save(uci, "dhcp", true)
|
||||
end
|
||||
sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
|
||||
restore_servers()
|
||||
else
|
||||
sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
|
||||
end
|
||||
if LOG == "1" then
|
||||
api.log("重启 dnsmasq 服务")
|
||||
end
|
||||
end
|
||||
|
||||
function copy_instance(var)
|
||||
local LISTEN_PORT = var["-LISTEN_PORT"]
|
||||
local TMP_DNSMASQ_PATH = var["-TMP_DNSMASQ_PATH"]
|
||||
local conf_lines = {}
|
||||
local DEFAULT_DNSMASQ_CFGID = sys.exec("echo -n $(uci -q show dhcp.@dnsmasq[0] | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')")
|
||||
for line in io.lines("/tmp/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID) do
|
||||
local filter
|
||||
if line:find("passwall") then filter = true end
|
||||
if line:find("ubus") then filter = true end
|
||||
if line:find("dhcp") then filter = true end
|
||||
if line:find("server=") == 1 then filter = true end
|
||||
if line:find("port=") == 1 then filter = true end
|
||||
if line:find("conf%-dir=") == 1 then
|
||||
filter = true
|
||||
if TMP_DNSMASQ_PATH then
|
||||
local tmp_path = line:sub(1 + #"conf-dir=")
|
||||
sys.call(string.format("cp -r %s/* %s/ 2>/dev/null", tmp_path, TMP_DNSMASQ_PATH))
|
||||
end
|
||||
end
|
||||
if line:find("address=") == 1 or (line:find("server=") == 1 and line:find("/")) then filter = nil end
|
||||
if not filter then
|
||||
tinsert(conf_lines, line)
|
||||
end
|
||||
end
|
||||
tinsert(conf_lines, "port=" .. LISTEN_PORT)
|
||||
if TMP_DNSMASQ_PATH then
|
||||
sys.call("rm -rf " .. TMP_DNSMASQ_PATH .. "/*passwall*")
|
||||
end
|
||||
if var["-return"] == "1" then
|
||||
return conf_lines
|
||||
end
|
||||
if #conf_lines > 0 then
|
||||
local DNSMASQ_CONF = var["-DNSMASQ_CONF"]
|
||||
local conf_out = io.open(DNSMASQ_CONF, "a")
|
||||
conf_out:write(table.concat(conf_lines, "\n"))
|
||||
conf_out:write("\n")
|
||||
conf_out:close()
|
||||
end
|
||||
end
|
||||
|
||||
function add_rule(var)
|
||||
local FLAG = var["-FLAG"]
|
||||
local TMP_DNSMASQ_PATH = var["-TMP_DNSMASQ_PATH"]
|
||||
local DNSMASQ_CONF_FILE = var["-DNSMASQ_CONF_FILE"]
|
||||
local LISTEN_PORT = var["-LISTEN_PORT"]
|
||||
local DEFAULT_DNS = var["-DEFAULT_DNS"]
|
||||
local LOCAL_DNS = var["-LOCAL_DNS"]
|
||||
local TUN_DNS = var["-TUN_DNS"]
|
||||
local REMOTE_FAKEDNS = var["-REMOTE_FAKEDNS"]
|
||||
local USE_DEFAULT_DNS = var["-USE_DEFAULT_DNS"]
|
||||
local CHINADNS_DNS = var["-CHINADNS_DNS"]
|
||||
local TCP_NODE = var["-TCP_NODE"]
|
||||
local USE_DIRECT_LIST = var["-USE_DIRECT_LIST"]
|
||||
local USE_PROXY_LIST = var["-USE_PROXY_LIST"]
|
||||
local USE_BLOCK_LIST = var["-USE_BLOCK_LIST"]
|
||||
local USE_GFW_LIST = var["-USE_GFW_LIST"]
|
||||
local CHN_LIST = var["-CHN_LIST"]
|
||||
local DEFAULT_PROXY_MODE = var["-DEFAULT_PROXY_MODE"]
|
||||
local NO_PROXY_IPV6 = var["-NO_PROXY_IPV6"]
|
||||
local NO_LOGIC_LOG = var["-NO_LOGIC_LOG"]
|
||||
local NFTFLAG = var["-NFTFLAG"]
|
||||
local CACHE_PATH = api.CACHE_PATH
|
||||
local CACHE_FLAG = "dnsmasq_" .. FLAG
|
||||
local CACHE_DNS_PATH = CACHE_PATH .. "/" .. CACHE_FLAG
|
||||
local CACHE_TEXT_FILE = CACHE_DNS_PATH .. ".txt"
|
||||
local USE_CHINADNS_NG = "0"
|
||||
|
||||
local list1 = {}
|
||||
local excluded_domain = {}
|
||||
|
||||
local function log(...)
|
||||
if NO_LOGIC_LOG == "1" then
|
||||
return
|
||||
end
|
||||
api.log(...)
|
||||
end
|
||||
|
||||
local function check_dns(domain, dns)
|
||||
if domain == "" or domain:find("#") then
|
||||
return false
|
||||
end
|
||||
if not dns then
|
||||
return
|
||||
end
|
||||
for k,v in ipairs(list1[domain].dns) do
|
||||
if dns == v then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function check_ipset(domain, ipset)
|
||||
if domain == "" or domain:find("#") then
|
||||
return false
|
||||
end
|
||||
if not ipset then
|
||||
return
|
||||
end
|
||||
for k,v in ipairs(list1[domain].ipsets) do
|
||||
if ipset == v then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function set_domain_address(domain, address)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
dns = {},
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
if not list1[domain].address then
|
||||
list1[domain].address = address
|
||||
end
|
||||
end
|
||||
|
||||
local function set_domain_dns(domain, dns)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not dns then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
dns = {},
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
for line in string.gmatch(dns, '[^' .. "," .. ']+') do
|
||||
if not check_dns(domain, line) then
|
||||
table.insert(list1[domain].dns, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function set_domain_ipset(domain, ipset)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not ipset then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
dns = {},
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
for line in string.gmatch(ipset, '[^' .. "," .. ']+') do
|
||||
if not check_ipset(domain, line) then
|
||||
table.insert(list1[domain].ipsets, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function add_excluded_domain(domain)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
table.insert(excluded_domain, domain)
|
||||
end
|
||||
|
||||
local function check_excluded_domain(domain)
|
||||
if domain == "" or domain:find("#") then
|
||||
return false
|
||||
end
|
||||
for k,v in ipairs(excluded_domain) do
|
||||
if domain == v or domain:sub(-#("."..v)) == "."..v then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local cache_text = ""
|
||||
local nodes_address_md5 = sys.exec("echo -n $(uci show passwall | grep '\\.address') | md5sum")
|
||||
local new_rules = sys.exec("echo -n $(find /usr/share/passwall/rules -type f | xargs md5sum)")
|
||||
local new_text = TMP_DNSMASQ_PATH .. DNSMASQ_CONF_FILE .. DEFAULT_DNS .. LOCAL_DNS .. TUN_DNS .. REMOTE_FAKEDNS .. USE_DEFAULT_DNS .. CHINADNS_DNS .. USE_DIRECT_LIST .. USE_PROXY_LIST .. USE_BLOCK_LIST .. USE_GFW_LIST .. CHN_LIST .. DEFAULT_PROXY_MODE .. NO_PROXY_IPV6 .. nodes_address_md5 .. new_rules .. NFTFLAG
|
||||
if fs.access(CACHE_TEXT_FILE) then
|
||||
for line in io.lines(CACHE_TEXT_FILE) do
|
||||
cache_text = line
|
||||
end
|
||||
end
|
||||
|
||||
if cache_text ~= new_text then
|
||||
api.remove(CACHE_DNS_PATH .. "*")
|
||||
end
|
||||
|
||||
local dnsmasq_default_dns
|
||||
if USE_DEFAULT_DNS ~= "nil" then
|
||||
if USE_DEFAULT_DNS == "direct" then
|
||||
dnsmasq_default_dns = LOCAL_DNS
|
||||
end
|
||||
if USE_DEFAULT_DNS == "remote" then
|
||||
dnsmasq_default_dns = TUN_DNS
|
||||
end
|
||||
if USE_DEFAULT_DNS == "remote" and CHN_LIST == "direct" then
|
||||
dnsmasq_default_dns = TUN_DNS
|
||||
end
|
||||
end
|
||||
|
||||
local only_global
|
||||
if DEFAULT_PROXY_MODE == "proxy" and CHN_LIST == "0" and USE_GFW_LIST == "0" then
|
||||
--没有启用中国列表和GFW列表时
|
||||
dnsmasq_default_dns = TUN_DNS
|
||||
only_global = 1
|
||||
end
|
||||
if USE_DEFAULT_DNS == "chinadns_ng" and CHINADNS_DNS ~= "0" then
|
||||
dnsmasq_default_dns = CHINADNS_DNS
|
||||
USE_CHINADNS_NG = "1"
|
||||
end
|
||||
|
||||
local setflag_4= (NFTFLAG == "1") and "4#inet#passwall#" or ""
|
||||
local setflag_6= (NFTFLAG == "1") and "6#inet#passwall#" or ""
|
||||
|
||||
if not fs.access(CACHE_DNS_PATH) then
|
||||
fs.mkdir(CACHE_DNS_PATH)
|
||||
|
||||
--屏蔽列表
|
||||
if USE_CHINADNS_NG == "0" then
|
||||
if USE_BLOCK_LIST == "1" then
|
||||
for line in io.lines("/usr/share/passwall/rules/block_host") do
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
set_domain_address(line, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fwd_dns
|
||||
local no_ipv6
|
||||
|
||||
--始终用国内DNS解析节点域名
|
||||
if true then
|
||||
fwd_dns = LOCAL_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
else
|
||||
local sets = {
|
||||
setflag_4 .. "passwall_vps",
|
||||
setflag_6 .. "passwall_vps6"
|
||||
}
|
||||
uci:foreach(appname, "nodes", function(t)
|
||||
local function process_address(address)
|
||||
if address == "engage.cloudflareclient.com" then return end
|
||||
if datatypes.hostname(address) then
|
||||
set_domain_dns(address, fwd_dns)
|
||||
set_domain_ipset(address, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
process_address(t.address)
|
||||
process_address(t.download_address)
|
||||
end)
|
||||
log(string.format(" - 节点列表中的域名(vpslist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
|
||||
--直连(白名单)列表
|
||||
if USE_DIRECT_LIST == "1" then
|
||||
if fs.access("/usr/share/passwall/rules/direct_host") then
|
||||
fwd_dns = LOCAL_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local sets = {
|
||||
setflag_4 .. "passwall_white",
|
||||
setflag_6 .. "passwall_white6"
|
||||
}
|
||||
--始终用国内DNS解析直连(白名单)列表
|
||||
for line in io.lines("/usr/share/passwall/rules/direct_host") do
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
add_excluded_domain(line)
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
log(string.format(" - 域名白名单(whitelist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--代理(黑名单)列表
|
||||
if USE_PROXY_LIST == "1" then
|
||||
if fs.access("/usr/share/passwall/rules/proxy_host") then
|
||||
fwd_dns = TUN_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local set_name = "passwall_black"
|
||||
local set6_name = "passwall_black6"
|
||||
if FLAG ~= "default" then
|
||||
set_name = "passwall_" .. FLAG .. "_black"
|
||||
set6_name = "passwall_" .. FLAG .. "_black6"
|
||||
end
|
||||
local sets = {
|
||||
setflag_4 .. set_name
|
||||
}
|
||||
if NO_PROXY_IPV6 ~= "1" then
|
||||
table.insert(sets, setflag_6 .. set6_name)
|
||||
end
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
sets = {}
|
||||
end
|
||||
--始终使用远程DNS解析代理(黑名单)列表
|
||||
for line in io.lines("/usr/share/passwall/rules/proxy_host") do
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
add_excluded_domain(line)
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
log(string.format(" - 代理域名表(blacklist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--GFW列表
|
||||
if USE_GFW_LIST == "1" then
|
||||
if fs.access("/usr/share/passwall/rules/gfwlist") then
|
||||
fwd_dns = TUN_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local set_name = "passwall_gfw"
|
||||
local set6_name = "passwall_gfw6"
|
||||
if FLAG ~= "default" then
|
||||
set_name = "passwall_" .. FLAG .. "_gfw"
|
||||
set6_name = "passwall_" .. FLAG .. "_gfw6"
|
||||
end
|
||||
local sets = {
|
||||
setflag_4 .. set_name
|
||||
}
|
||||
if NO_PROXY_IPV6 ~= "1" then
|
||||
table.insert(sets, setflag_6 .. set6_name)
|
||||
end
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
sets = {}
|
||||
end
|
||||
for line in io.lines("/usr/share/passwall/rules/gfwlist") do
|
||||
if line ~= "" and not line:find("#") and not check_excluded_domain(line) then
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
if dnsmasq_default_dns == fwd_dns then
|
||||
fwd_dns = nil
|
||||
else
|
||||
set_domain_dns(line, fwd_dns)
|
||||
end
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
log(string.format(" - 防火墙域名表(gfwlist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--中国列表
|
||||
if CHN_LIST ~= "0" then
|
||||
if fs.access("/usr/share/passwall/rules/chnlist") then
|
||||
fwd_dns = nil
|
||||
if CHN_LIST == "direct" then
|
||||
fwd_dns = LOCAL_DNS
|
||||
end
|
||||
if CHN_LIST == "proxy" then
|
||||
fwd_dns = TUN_DNS
|
||||
end
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local sets = {
|
||||
setflag_4 .. "passwall_chn",
|
||||
setflag_6 .. "passwall_chn6"
|
||||
}
|
||||
if CHN_LIST == "proxy" then
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
sets = {
|
||||
setflag_4 .. "passwall_chn"
|
||||
}
|
||||
end
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
sets = {}
|
||||
end
|
||||
end
|
||||
for line in io.lines("/usr/share/passwall/rules/chnlist") do
|
||||
if line ~= "" and not line:find("#") and not check_excluded_domain(line) then
|
||||
if CHN_LIST == "proxy" and NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
if dnsmasq_default_dns == fwd_dns then
|
||||
fwd_dns = nil
|
||||
else
|
||||
set_domain_dns(line, fwd_dns)
|
||||
end
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
log(string.format(" - 中国域名表(chnroute):%s", fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--分流规则
|
||||
if uci:get(appname, TCP_NODE, "protocol") == "_shunt" and USE_CHINADNS_NG == "0" then
|
||||
local t = uci:get_all(appname, TCP_NODE)
|
||||
local default_node_id = t["default_node"] or "_direct"
|
||||
uci:foreach(appname, "shunt_rules", function(s)
|
||||
local _node_id = t[s[".name"]]
|
||||
if _node_id and _node_id ~= "_blackhole" then
|
||||
if _node_id == "_default" then
|
||||
_node_id = default_node_id
|
||||
end
|
||||
|
||||
fwd_dns = nil
|
||||
no_ipv6 = nil
|
||||
|
||||
local sets = {}
|
||||
|
||||
if _node_id == "_direct" then
|
||||
fwd_dns = LOCAL_DNS
|
||||
if USE_DIRECT_LIST == "1" then
|
||||
table.insert(sets, setflag_4 .. "passwall_white")
|
||||
table.insert(sets, setflag_6 .. "passwall_white6")
|
||||
else
|
||||
local set_name = "passwall_shunt"
|
||||
local set6_name = "passwall_shunt6"
|
||||
if FLAG ~= "default" then
|
||||
set_name = "passwall_" .. FLAG .. "_shunt"
|
||||
set6_name = "passwall_" .. FLAG .. "_shunt6"
|
||||
end
|
||||
table.insert(sets, setflag_4 .. set_name)
|
||||
table.insert(sets, setflag_6 .. set6_name)
|
||||
end
|
||||
else
|
||||
local set_name = "passwall_shunt"
|
||||
local set6_name = "passwall_shunt6"
|
||||
if FLAG ~= "default" then
|
||||
set_name = "passwall_" .. FLAG .. "_shunt"
|
||||
set6_name = "passwall_" .. FLAG .. "_shunt6"
|
||||
end
|
||||
fwd_dns = TUN_DNS
|
||||
table.insert(sets, setflag_4 .. set_name)
|
||||
if NO_PROXY_IPV6 ~= "1" then
|
||||
table.insert(sets, setflag_6 .. set6_name)
|
||||
else
|
||||
no_ipv6 = true
|
||||
end
|
||||
if not only_global then
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
sets = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local domain_list = s.domain_list or ""
|
||||
for line in string.gmatch(domain_list, "[^\r\n]+") do
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("geosite:") and not line:find("ext:") then
|
||||
if line:find("domain:") or line:find("full:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
end
|
||||
line = api.get_std_domain(line)
|
||||
add_excluded_domain(line)
|
||||
|
||||
if no_ipv6 then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
if _node_id ~= "_direct" then
|
||||
log(string.format(" - Sing-Box/Xray分流规则(%s):%s", s.remarks, fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
end)
|
||||
elseif only_global == 1 and NO_PROXY_IPV6 == "1" then
|
||||
--节点:固定节点
|
||||
--代理模式:全局模式
|
||||
--过滤代理域名 IPv6:启用
|
||||
--禁止解析所有IPv6记录
|
||||
list1["#"] = {
|
||||
dns = {},
|
||||
ipsets = {},
|
||||
address = "::"
|
||||
}
|
||||
end
|
||||
|
||||
if list1 and next(list1) then
|
||||
local address_out = io.open(CACHE_DNS_PATH .. "/000-address.conf", "a")
|
||||
local server_out = io.open(CACHE_DNS_PATH .. "/001-server.conf", "a")
|
||||
local ipset_out = io.open(CACHE_DNS_PATH .. "/ipset.conf", "a")
|
||||
local set_name = "ipset"
|
||||
if NFTFLAG == "1" then
|
||||
set_name = "nftset"
|
||||
end
|
||||
for key, value in pairs(list1) do
|
||||
if value.address then
|
||||
local domain = "." .. key
|
||||
if key == "#" then
|
||||
domain = key
|
||||
end
|
||||
address_out:write(string.format("address=/%s/%s", domain, value.address) .. "\n")
|
||||
end
|
||||
if value.dns and #value.dns > 0 then
|
||||
for i, dns in ipairs(value.dns) do
|
||||
server_out:write(string.format("server=/.%s/%s", key, dns) .. "\n")
|
||||
end
|
||||
end
|
||||
if value.ipsets and #value.ipsets > 0 then
|
||||
local ipsets_str = ""
|
||||
for i, ipset in ipairs(value.ipsets) do
|
||||
ipsets_str = ipsets_str .. ipset .. ","
|
||||
end
|
||||
ipsets_str = ipsets_str:sub(1, #ipsets_str - 1)
|
||||
ipset_out:write(string.format("%s=/.%s/%s", set_name, key, ipsets_str) .. "\n")
|
||||
end
|
||||
end
|
||||
address_out:close()
|
||||
server_out:close()
|
||||
ipset_out:close()
|
||||
end
|
||||
|
||||
local f_out = io.open(CACHE_TEXT_FILE, "a")
|
||||
f_out:write(new_text)
|
||||
f_out:close()
|
||||
end
|
||||
|
||||
if USE_CHINADNS_NG == "0" then
|
||||
api.remove(TMP_DNSMASQ_PATH)
|
||||
fs.symlink(CACHE_DNS_PATH, TMP_DNSMASQ_PATH)
|
||||
end
|
||||
|
||||
if DNSMASQ_CONF_FILE ~= "nil" then
|
||||
local conf_lines = {}
|
||||
if LISTEN_PORT then
|
||||
--Copy dnsmasq instance
|
||||
conf_lines = copy_instance({["-LISTEN_PORT"] = LISTEN_PORT, ["-TMP_DNSMASQ_PATH"] = TMP_DNSMASQ_PATH, ["-return"] = "1"})
|
||||
--dhcp.leases to hosts
|
||||
local hosts = "/tmp/etc/" .. appname .. "_tmp/dhcp-hosts"
|
||||
sys.call("touch " .. hosts)
|
||||
tinsert(conf_lines, "addn-hosts=" .. hosts)
|
||||
else
|
||||
--Modify the default dnsmasq service
|
||||
end
|
||||
if USE_CHINADNS_NG == "0" then
|
||||
tinsert(conf_lines, string.format("conf-dir=%s", TMP_DNSMASQ_PATH))
|
||||
end
|
||||
if dnsmasq_default_dns then
|
||||
for s in string.gmatch(dnsmasq_default_dns, '[^' .. "," .. ']+') do
|
||||
tinsert(conf_lines, string.format("server=%s", s))
|
||||
end
|
||||
tinsert(conf_lines, "all-servers")
|
||||
tinsert(conf_lines, "no-poll")
|
||||
tinsert(conf_lines, "no-resolv")
|
||||
if USE_CHINADNS_NG == "0" then
|
||||
log(string.format(" - 默认:%s", dnsmasq_default_dns))
|
||||
end
|
||||
|
||||
if FLAG == "default" then
|
||||
api.set_cache_var("DEFAULT_DNS", DEFAULT_DNS)
|
||||
end
|
||||
end
|
||||
if #conf_lines > 0 then
|
||||
local conf_out = io.open(DNSMASQ_CONF_FILE, "a")
|
||||
conf_out:write(table.concat(conf_lines, "\n"))
|
||||
conf_out:write("\n")
|
||||
conf_out:close()
|
||||
end
|
||||
end
|
||||
|
||||
if USE_CHINADNS_NG == "0" then
|
||||
log(" - PassWall必须依赖于Dnsmasq,如果你自行配置了错误的DNS流程,将会导致域名(直连/代理域名)分流失效!!!")
|
||||
end
|
||||
end
|
||||
|
||||
_G.stretch = stretch
|
||||
_G.restart = restart
|
||||
_G.logic_restart = logic_restart
|
||||
_G.copy_instance = copy_instance
|
||||
_G.add_rule = add_rule
|
||||
|
||||
if arg[1] then
|
||||
local func =_G[arg[1]]
|
||||
if func then
|
||||
func(api.get_function_args(arg))
|
||||
end
|
||||
end
|
||||
30
luci-app-passwall/root/usr/share/passwall/helper_smartdns.sh
Executable file
30
luci-app-passwall/root/usr/share/passwall/helper_smartdns.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
restart() {
|
||||
local no_log
|
||||
eval_set_val $@
|
||||
_LOG_FILE=$LOG_FILE
|
||||
[ -n "$no_log" ] && LOG_FILE="/dev/null"
|
||||
rm -rf /tmp/smartdns.cache
|
||||
/etc/init.d/smartdns reload >/dev/null 2>&1
|
||||
LOG_FILE=${_LOG_FILE}
|
||||
}
|
||||
|
||||
del() {
|
||||
rm -rf /tmp/etc/smartdns/passwall.conf
|
||||
sed -i "/passwall/d" /etc/smartdns/custom.conf >/dev/null 2>&1
|
||||
rm -rf /tmp/smartdns.cache
|
||||
/etc/init.d/smartdns reload >/dev/null 2>&1
|
||||
}
|
||||
|
||||
arg1=$1
|
||||
shift
|
||||
case $arg1 in
|
||||
del)
|
||||
del $@
|
||||
;;
|
||||
restart)
|
||||
restart $@
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
@@ -0,0 +1,657 @@
|
||||
require "luci.sys"
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = "passwall"
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local FLAG = var["-FLAG"]
|
||||
local SMARTDNS_CONF = var["-SMARTDNS_CONF"]
|
||||
local LOCAL_GROUP = var["-LOCAL_GROUP"]
|
||||
local REMOTE_GROUP = var["-REMOTE_GROUP"]
|
||||
local REMOTE_PROXY_SERVER = var["-REMOTE_PROXY_SERVER"]
|
||||
local USE_DEFAULT_DNS = var["-USE_DEFAULT_DNS"]
|
||||
local REMOTE_DNS = var["-REMOTE_DNS"]
|
||||
local TUN_DNS = var["-TUN_DNS"]
|
||||
local DNS_MODE = var["-DNS_MODE"]
|
||||
local REMOTE_FAKEDNS = var["-REMOTE_FAKEDNS"]
|
||||
local TCP_NODE = var["-TCP_NODE"]
|
||||
local USE_DIRECT_LIST = var["-USE_DIRECT_LIST"]
|
||||
local USE_PROXY_LIST = var["-USE_PROXY_LIST"]
|
||||
local USE_BLOCK_LIST = var["-USE_BLOCK_LIST"]
|
||||
local USE_GFW_LIST = var["-USE_GFW_LIST"]
|
||||
local CHN_LIST = var["-CHN_LIST"]
|
||||
local DEFAULT_PROXY_MODE = var["-DEFAULT_PROXY_MODE"]
|
||||
local NO_PROXY_IPV6 = var["-NO_PROXY_IPV6"]
|
||||
local NO_LOGIC_LOG = var["-NO_LOGIC_LOG"]
|
||||
local NFTFLAG = var["-NFTFLAG"]
|
||||
local SUBNET = var["-SUBNET"]
|
||||
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
local fs = api.fs
|
||||
local datatypes = api.datatypes
|
||||
|
||||
local TMP_PATH = "/tmp/etc/" .. appname
|
||||
local TMP_ACL_PATH = TMP_PATH .. "/acl"
|
||||
local RULES_PATH = "/usr/share/" .. appname .. "/rules"
|
||||
local FLAG_PATH = TMP_ACL_PATH .. "/" .. FLAG
|
||||
local TMP_CONF_FILE = FLAG_PATH .. "/smartdns.conf"
|
||||
local config_lines = {}
|
||||
local tmp_lines = {}
|
||||
local USE_GEOVIEW = uci:get(appname, "@global_rules[0]", "enable_geoview")
|
||||
|
||||
local function log(...)
|
||||
if NO_LOGIC_LOG == "1" then
|
||||
return
|
||||
end
|
||||
api.log(...)
|
||||
end
|
||||
|
||||
local function is_file_nonzero(path)
|
||||
if path and #path > 1 then
|
||||
if sys.exec('[ -s "%s" ] && echo -n 1' % path) == "1" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function insert_unique(dest_table, value, lookup_table)
|
||||
if not lookup_table[value] then
|
||||
table.insert(dest_table, value)
|
||||
lookup_table[value] = true
|
||||
end
|
||||
end
|
||||
|
||||
local function merge_array(array1, array2)
|
||||
for i, line in ipairs(array2) do
|
||||
table.insert(array1, #array1 + 1, line)
|
||||
end
|
||||
end
|
||||
|
||||
local function insert_array_before(array1, array2, target) --将array2插入到array1的target前面,target不存在则追加
|
||||
for i, line in ipairs(array1) do
|
||||
if line == target then
|
||||
for j = #array2, 1, -1 do
|
||||
table.insert(array1, i, array2[j])
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
merge_array(array1, array2)
|
||||
end
|
||||
|
||||
local function insert_array_after(array1, array2, target) --将array2插入到array1的target后面,target不存在则追加
|
||||
for i, line in ipairs(array1) do
|
||||
if line == target then
|
||||
for j = 1, #array2 do
|
||||
table.insert(array1, i + j, array2[j])
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
merge_array(array1, array2)
|
||||
end
|
||||
|
||||
local function get_geosite(list_arg, out_path)
|
||||
local geosite_path = uci:get(appname, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/"
|
||||
geosite_path = geosite_path:match("^(.*)/") .. "/geosite.dat"
|
||||
if not is_file_nonzero(geosite_path) then return 1 end
|
||||
if api.is_finded("geoview") and list_arg and out_path then
|
||||
sys.exec("geoview -type geosite -append=true -input " .. geosite_path .. " -list '" .. list_arg .. "' -output " .. out_path)
|
||||
return 0
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
if not fs.access(FLAG_PATH) then
|
||||
fs.mkdir(FLAG_PATH)
|
||||
end
|
||||
|
||||
local LOCAL_EXTEND_ARG = ""
|
||||
if LOCAL_GROUP == "nil" then
|
||||
LOCAL_GROUP = nil
|
||||
log(" * 注意:国内分组名未设置,可能会导致 DNS 分流错误!")
|
||||
else
|
||||
--从smartdns配置中读取参数
|
||||
local custom_conf_path = "/etc/smartdns/custom.conf"
|
||||
local options = {
|
||||
{key = "dualstack_ip_selection", config_key = "dualstack-ip-selection", yes_no = true, arg_yes = "-d yes", arg_no = "-d no", default = "yes"},
|
||||
{key = "speed_check_mode", config_key = "speed-check-mode", prefix = "-c ", default = "ping,tcp:80,tcp:443"},
|
||||
{key = "serve_expired", config_key = "serve-expired", yes_no = true, arg_yes = "", arg_no = "-no-serve-expired", default = "yes"},
|
||||
{key = "response_mode", config_key = "response-mode", prefix = "-r ", default = "first-ping"},
|
||||
{key = "rr_ttl", config_key = "rr-ttl", prefix = "-rr-ttl "},
|
||||
{key = "rr_ttl_min", config_key = "rr-ttl-min", prefix = "-rr-ttl-min "},
|
||||
{key = "rr_ttl_max", config_key = "rr-ttl-max", prefix = "-rr-ttl-max "}
|
||||
}
|
||||
-- 从 custom.conf 中读取值,以最后出现的值为准
|
||||
local custom_config = {}
|
||||
local f_in = io.open(custom_conf_path, "r")
|
||||
if f_in then
|
||||
for line in f_in:lines() do
|
||||
line = api.trim(line)
|
||||
if line ~= "" and not line:match("^#") then
|
||||
local param, value = line:match("^(%S+)%s+(%S+)$")
|
||||
if param and value then custom_config[param] = value end
|
||||
end
|
||||
end
|
||||
f_in:close()
|
||||
end
|
||||
-- 从 smartdns 配置中读取值,优先级以 custom.conf 为准
|
||||
for _, opt in ipairs(options) do
|
||||
local val = custom_config[opt.config_key] or uci:get("smartdns", "@smartdns[0]", opt.key) or opt.default
|
||||
if val == "yes" then val = "1" elseif val == "no" then val = "0" end
|
||||
if opt.yes_no then
|
||||
local arg = (val == "1" and opt.arg_yes or opt.arg_no)
|
||||
if arg and arg ~= "" then
|
||||
LOCAL_EXTEND_ARG = LOCAL_EXTEND_ARG .. (LOCAL_EXTEND_ARG ~= "" and " " or "") .. arg
|
||||
end
|
||||
else
|
||||
if val and (not opt.value or (opt.invert and val ~= opt.value) or (not opt.invert and val == opt.value)) then
|
||||
LOCAL_EXTEND_ARG = LOCAL_EXTEND_ARG .. (LOCAL_EXTEND_ARG ~= "" and " " or "") .. (opt.prefix or "") .. (opt.arg or val)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not REMOTE_GROUP or REMOTE_GROUP == "nil" then
|
||||
REMOTE_GROUP = "passwall_proxy"
|
||||
if REMOTE_DNS then
|
||||
REMOTE_DNS = REMOTE_DNS:gsub("#", ":")
|
||||
end
|
||||
sys.call('sed -i "/passwall/d" /etc/smartdns/custom.conf >/dev/null 2>&1')
|
||||
end
|
||||
|
||||
local force_https_soa = uci:get(appname, "@global[0]", "force_https_soa") or 1
|
||||
local proxy_server_name = "passwall-proxy-server"
|
||||
config_lines = {
|
||||
tonumber(force_https_soa) == 1 and "force-qtype-SOA 65" or "force-qtype-SOA -,65",
|
||||
"server 114.114.114.114 -bootstrap-dns",
|
||||
DNS_MODE == "socks" and string.format("proxy-server socks5://%s -name %s", REMOTE_PROXY_SERVER, proxy_server_name) or nil
|
||||
}
|
||||
if DNS_MODE == "socks" then
|
||||
for w in string.gmatch(REMOTE_DNS, '[^|]+') do
|
||||
local server_dns = api.trim(w)
|
||||
local server_param
|
||||
|
||||
local dnsType = string.match(server_dns, "^(.-)://")
|
||||
dnsType = dnsType and string.lower(dnsType) or nil
|
||||
local dnsServer = string.match(server_dns, "://(.+)") or server_dns
|
||||
|
||||
if dnsType and dnsType ~= "" and dnsType ~= "udp" then
|
||||
if dnsType == "tcp" then
|
||||
server_param = "server-tcp " .. dnsServer
|
||||
elseif dnsType == "tls" then
|
||||
server_param = "server-tls " .. dnsServer
|
||||
elseif dnsType == "quic" then
|
||||
server_param = "server-quic " .. dnsServer
|
||||
elseif dnsType == "https" or dnsType == "h3" then
|
||||
local http_host = nil
|
||||
local url = w
|
||||
local port = 443
|
||||
local s = api.split(w, ",")
|
||||
if s and #s > 1 then
|
||||
url = s[1]
|
||||
local dns_ip = s[2]
|
||||
local host_port = api.get_domain_from_url(s[1])
|
||||
if host_port and #host_port > 0 then
|
||||
http_host = host_port
|
||||
local s2 = api.split(host_port, ":")
|
||||
if s2 and #s2 > 1 then
|
||||
http_host = s2[1]
|
||||
port = s2[2]
|
||||
end
|
||||
url = url:gsub(http_host, dns_ip)
|
||||
end
|
||||
end
|
||||
server_dns = url
|
||||
if http_host then
|
||||
server_dns = server_dns .. " -http-host " .. http_host
|
||||
end
|
||||
server_param = (dnsType == "https" and "server-https " or "server-h3 ") .. server_dns
|
||||
end
|
||||
else
|
||||
server_param = "server " .. dnsServer
|
||||
|
||||
end
|
||||
|
||||
if not api.is_local_ip(w) then
|
||||
server_param = server_param .. " -proxy " .. proxy_server_name
|
||||
end
|
||||
|
||||
server_param = server_param .. " -group " .. REMOTE_GROUP .. " -exclude-default-group"
|
||||
if SUBNET and SUBNET ~= "" and SUBNET ~= "0" then
|
||||
server_param = server_param .. " -subnet " .. SUBNET
|
||||
end
|
||||
table.insert(config_lines, server_param)
|
||||
end
|
||||
REMOTE_FAKEDNS = 0
|
||||
else
|
||||
local server_param = string.format("server %s -group %s -exclude-default-group", TUN_DNS:gsub("#", ":"), REMOTE_GROUP)
|
||||
table.insert(config_lines, server_param)
|
||||
log(" - " .. DNS_MODE:gsub("^%l",string.upper) .. " " .. TUN_DNS .. " -> " .. REMOTE_GROUP)
|
||||
end
|
||||
|
||||
--设置默认 DNS 分组(托底组)
|
||||
local DEFAULT_DNS_GROUP = (USE_DEFAULT_DNS == "direct" and LOCAL_GROUP) or
|
||||
(USE_DEFAULT_DNS == "remote" and REMOTE_GROUP)
|
||||
local only_global = (DEFAULT_PROXY_MODE == "proxy" and CHN_LIST == "0" and USE_GFW_LIST == "0") and 1 --没有启用中国列表和GFW列表时(全局)
|
||||
if only_global == 1 then
|
||||
DEFAULT_DNS_GROUP = REMOTE_GROUP
|
||||
end
|
||||
if DEFAULT_DNS_GROUP then
|
||||
local domain_rules_str = "domain-rules /./ -nameserver " .. DEFAULT_DNS_GROUP
|
||||
if DEFAULT_DNS_GROUP == REMOTE_GROUP then
|
||||
domain_rules_str = domain_rules_str .. " -speed-check-mode none -d no -no-serve-expired"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
domain_rules_str = domain_rules_str .. " -address #6"
|
||||
end
|
||||
elseif DEFAULT_DNS_GROUP == LOCAL_GROUP then
|
||||
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||
end
|
||||
table.insert(config_lines, domain_rules_str)
|
||||
end
|
||||
|
||||
local setflag = (NFTFLAG == "1") and "inet#passwall#" or ""
|
||||
local set_type = (NFTFLAG == "1") and "-nftset" or "-ipset"
|
||||
|
||||
--预设排序标签(越往后优先级越高)
|
||||
for i = 1, 8 do
|
||||
table.insert(config_lines, "#--" .. i)
|
||||
end
|
||||
|
||||
--屏蔽列表
|
||||
local file_block_host = TMP_ACL_PATH .. "/block_host"
|
||||
if USE_BLOCK_LIST == "1" and not fs.access(file_block_host) then
|
||||
local block_domain, lookup_block_domain = {}, {}
|
||||
local geosite_arg = ""
|
||||
for line in io.lines(RULES_PATH .. "/block_host") do
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
insert_unique(block_domain, line, lookup_block_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #block_domain > 0 then
|
||||
local f_out = io.open(file_block_host, "w")
|
||||
for i = 1, #block_domain do
|
||||
f_out:write(block_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" and api.is_finded("geoview") then
|
||||
if get_geosite(geosite_arg, file_block_host) == 0 then
|
||||
log(" * 解析[屏蔽列表] Geosite 到屏蔽域名表(blocklist)完成")
|
||||
else
|
||||
log(" * 解析[屏蔽列表] Geosite 到屏蔽域名表(blocklist)失败!")
|
||||
end
|
||||
end
|
||||
end
|
||||
if USE_BLOCK_LIST == "1" and is_file_nonzero(file_block_host) then
|
||||
local domain_set_name = "passwall-block"
|
||||
tmp_lines = {
|
||||
string.format("domain-set -name %s -file %s", domain_set_name, file_block_host),
|
||||
string.format("domain-rules /domain-set:%s/ -a #", domain_set_name)
|
||||
}
|
||||
insert_array_after(config_lines, tmp_lines, "#--7")
|
||||
end
|
||||
|
||||
--始终用国内DNS解析节点域名
|
||||
local file_vpslist = TMP_ACL_PATH .. "/vpslist"
|
||||
if not is_file_nonzero(file_vpslist) then
|
||||
local f_out = io.open(file_vpslist, "w")
|
||||
local written_domains = {}
|
||||
uci:foreach(appname, "nodes", function(t)
|
||||
local function process_address(address)
|
||||
if address == "engage.cloudflareclient.com" then return end
|
||||
if datatypes.hostname(address) and not written_domains[address] then
|
||||
f_out:write(address .. "\n")
|
||||
written_domains[address] = true
|
||||
end
|
||||
end
|
||||
process_address(t.address)
|
||||
process_address(t.download_address)
|
||||
end)
|
||||
f_out:close()
|
||||
end
|
||||
if is_file_nonzero(file_vpslist) then
|
||||
local domain_set_name = "passwall-vpslist"
|
||||
tmp_lines = {
|
||||
string.format("domain-set -name %s -file %s", domain_set_name, file_vpslist)
|
||||
}
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_vps",
|
||||
"#6:" .. setflag .. "passwall_vps6"
|
||||
}
|
||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",")
|
||||
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||
table.insert(tmp_lines, domain_rules_str)
|
||||
insert_array_after(config_lines, tmp_lines, "#--8")
|
||||
log(string.format(" - 节点列表中的域名(vpslist)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||
end
|
||||
|
||||
--直连(白名单)列表
|
||||
local file_direct_host = TMP_ACL_PATH .. "/direct_host"
|
||||
if USE_DIRECT_LIST == "1" and not fs.access(file_direct_host) then
|
||||
local direct_domain, lookup_direct_domain = {}, {}
|
||||
local geosite_arg = ""
|
||||
for line in io.lines(RULES_PATH .. "/direct_host") do
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
insert_unique(direct_domain, line, lookup_direct_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #direct_domain > 0 then
|
||||
local f_out = io.open(file_direct_host, "w")
|
||||
for i = 1, #direct_domain do
|
||||
f_out:write(direct_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" and api.is_finded("geoview") then
|
||||
if get_geosite(geosite_arg, file_direct_host) == 0 then
|
||||
log(" * 解析[直连列表] Geosite 到域名白名单(whitelist)完成")
|
||||
else
|
||||
log(" * 解析[直连列表] Geosite 到域名白名单(whitelist)失败!")
|
||||
end
|
||||
end
|
||||
end
|
||||
if USE_DIRECT_LIST == "1" and is_file_nonzero(file_direct_host) then
|
||||
local domain_set_name = "passwall-directlist"
|
||||
tmp_lines = {
|
||||
string.format("domain-set -name %s -file %s", domain_set_name, file_direct_host)
|
||||
}
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_white",
|
||||
"#6:" .. setflag .. "passwall_white6"
|
||||
}
|
||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",")
|
||||
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||
table.insert(tmp_lines, domain_rules_str)
|
||||
insert_array_after(config_lines, tmp_lines, "#--6")
|
||||
log(string.format(" - 域名白名单(whitelist)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||
end
|
||||
|
||||
--代理(黑名单)列表
|
||||
local file_proxy_host = TMP_ACL_PATH .. "/proxy_host"
|
||||
if USE_PROXY_LIST == "1" and not fs.access(file_proxy_host) then
|
||||
local proxy_domain, lookup_proxy_domain = {}, {}
|
||||
local geosite_arg = ""
|
||||
for line in io.lines(RULES_PATH .. "/proxy_host") do
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
insert_unique(proxy_domain, line, lookup_proxy_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #proxy_domain > 0 then
|
||||
local f_out = io.open(file_proxy_host, "w")
|
||||
for i = 1, #proxy_domain do
|
||||
f_out:write(proxy_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" and api.is_finded("geoview") then
|
||||
if get_geosite(geosite_arg, file_proxy_host) == 0 then
|
||||
log(" * 解析[代理列表] Geosite 到代理域名表(blacklist)完成")
|
||||
else
|
||||
log(" * 解析[代理列表] Geosite 到代理域名表(blacklist)失败!")
|
||||
end
|
||||
end
|
||||
end
|
||||
if USE_PROXY_LIST == "1" and is_file_nonzero(file_proxy_host) then
|
||||
local domain_set_name = "passwall-proxylist"
|
||||
tmp_lines = {
|
||||
string.format("domain-set -name %s -file %s", domain_set_name, file_proxy_host)
|
||||
}
|
||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ -nameserver %s', domain_set_name, REMOTE_GROUP)
|
||||
domain_rules_str = domain_rules_str .. " -speed-check-mode none"
|
||||
domain_rules_str = domain_rules_str .. " -no-serve-expired"
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_black"
|
||||
}
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
domain_rules_str = domain_rules_str .. " -address #6"
|
||||
domain_rules_str = REMOTE_FAKEDNS ~= "1" and (domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",")) or domain_rules_str
|
||||
else
|
||||
table.insert(sets, "#6:" .. setflag .. "passwall_black6")
|
||||
domain_rules_str = REMOTE_FAKEDNS ~= "1" and (domain_rules_str .. " -d no " .. set_type .. " " .. table.concat(sets, ",")) or domain_rules_str
|
||||
end
|
||||
table.insert(tmp_lines, domain_rules_str)
|
||||
insert_array_after(config_lines, tmp_lines, "#--5")
|
||||
log(string.format(" - 代理域名表(blacklist)使用分组:%s", REMOTE_GROUP or "默认"))
|
||||
end
|
||||
|
||||
--GFW列表
|
||||
if USE_GFW_LIST == "1" and is_file_nonzero(RULES_PATH .. "/gfwlist") then
|
||||
local domain_set_name = "passwall-gfwlist"
|
||||
tmp_lines = {
|
||||
string.format("domain-set -name %s -file %s", domain_set_name, RULES_PATH .. "/gfwlist")
|
||||
}
|
||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ -nameserver %s', domain_set_name, REMOTE_GROUP)
|
||||
domain_rules_str = domain_rules_str .. " -speed-check-mode none"
|
||||
domain_rules_str = domain_rules_str .. " -no-serve-expired"
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_gfw"
|
||||
}
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
domain_rules_str = domain_rules_str .. " -address #6"
|
||||
domain_rules_str = REMOTE_FAKEDNS ~= "1" and (domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",")) or domain_rules_str
|
||||
else
|
||||
table.insert(sets, "#6:" .. setflag .. "passwall_gfw6")
|
||||
domain_rules_str = REMOTE_FAKEDNS ~= "1" and (domain_rules_str .. " -d no " .. set_type .. " " .. table.concat(sets, ",")) or domain_rules_str
|
||||
end
|
||||
table.insert(tmp_lines, domain_rules_str)
|
||||
insert_array_after(config_lines, tmp_lines, "#--1")
|
||||
log(string.format(" - 防火墙域名表(gfwlist)使用分组:%s", REMOTE_GROUP or "默认"))
|
||||
end
|
||||
|
||||
--中国列表
|
||||
if CHN_LIST ~= "0" and is_file_nonzero(RULES_PATH .. "/chnlist") then
|
||||
local domain_set_name = "passwall-chnlist"
|
||||
tmp_lines = {
|
||||
string.format("domain-set -name %s -file %s", domain_set_name, RULES_PATH .. "/chnlist")
|
||||
}
|
||||
|
||||
if CHN_LIST == "direct" then
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_chn",
|
||||
"#6:" .. setflag .. "passwall_chn6"
|
||||
}
|
||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",")
|
||||
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||
table.insert(tmp_lines, domain_rules_str)
|
||||
insert_array_after(config_lines, tmp_lines, "#--2")
|
||||
log(string.format(" - 中国域名表(chnroute)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||
end
|
||||
|
||||
--回中国模式
|
||||
if CHN_LIST == "proxy" then
|
||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ -nameserver %s', domain_set_name, REMOTE_GROUP)
|
||||
domain_rules_str = domain_rules_str .. " -speed-check-mode none"
|
||||
domain_rules_str = domain_rules_str .. " -no-serve-expired"
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_chn"
|
||||
}
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
domain_rules_str = domain_rules_str .. " -address #6"
|
||||
domain_rules_str = REMOTE_FAKEDNS ~= "1" and (domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",")) or domain_rules_str
|
||||
else
|
||||
table.insert(sets, "#6:" .. setflag .. "passwall_chn6")
|
||||
domain_rules_str = REMOTE_FAKEDNS ~= "1" and (domain_rules_str .. " -d no " .. set_type .. " " .. table.concat(sets, ",")) or domain_rules_str
|
||||
end
|
||||
table.insert(tmp_lines, domain_rules_str)
|
||||
insert_array_after(config_lines, tmp_lines, "#--2")
|
||||
log(string.format(" - 中国域名表(chnroute)使用分组:%s", REMOTE_GROUP or "默认"))
|
||||
end
|
||||
end
|
||||
|
||||
--分流规则
|
||||
if uci:get(appname, TCP_NODE, "protocol") == "_shunt" then
|
||||
local white_domain, lookup_white_domain = {}, {}
|
||||
local shunt_domain, lookup_shunt_domain = {}, {}
|
||||
local file_white_host = FLAG_PATH .. "/shunt_direct_host"
|
||||
local file_shunt_host = FLAG_PATH .. "/shunt_proxy_host"
|
||||
local geosite_white_arg, geosite_shunt_arg = "", ""
|
||||
|
||||
local t = uci:get_all(appname, TCP_NODE)
|
||||
local default_node_id = t["default_node"] or "_direct"
|
||||
uci:foreach(appname, "shunt_rules", function(s)
|
||||
local _node_id = t[s[".name"]]
|
||||
if _node_id and _node_id ~= "_blackhole" then
|
||||
if _node_id == "_default" then
|
||||
_node_id = default_node_id
|
||||
end
|
||||
|
||||
local domain_list = s.domain_list or ""
|
||||
for line in string.gmatch(domain_list, "[^\r\n]+") do
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") then
|
||||
if line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
if _node_id == "_direct" then
|
||||
geosite_white_arg = geosite_white_arg .. (geosite_white_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
geosite_shunt_arg = geosite_shunt_arg .. (geosite_shunt_arg ~= "" and "," or "") .. line
|
||||
end
|
||||
else
|
||||
if line:find("domain:") or line:find("full:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
end
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
if _node_id == "_direct" then
|
||||
insert_unique(white_domain, line, lookup_white_domain)
|
||||
else
|
||||
insert_unique(shunt_domain, line, lookup_shunt_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if _node_id ~= "_direct" then
|
||||
log(string.format(" - Sing-Box/Xray分流规则(%s)使用分组:%s", s.remarks, REMOTE_GROUP or "默认"))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if is_file_nonzero(file_white_host) == nil then
|
||||
if #white_domain > 0 then
|
||||
local f_out = io.open(file_white_host, "w")
|
||||
for i = 1, #white_domain do
|
||||
f_out:write(white_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
end
|
||||
|
||||
if is_file_nonzero(file_shunt_host) == nil then
|
||||
if #shunt_domain > 0 then
|
||||
local f_out = io.open(file_shunt_host, "w")
|
||||
for i = 1, #shunt_domain do
|
||||
f_out:write(shunt_domain[i] .. "\n")
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
end
|
||||
|
||||
if USE_GFW_LIST == "1" and CHN_LIST == "0" and USE_GEOVIEW == "1" and api.is_finded("geoview") then --仅GFW模式解析geosite
|
||||
local return_white, return_shunt
|
||||
if geosite_white_arg ~= "" then
|
||||
return_white = get_geosite(geosite_white_arg, file_white_host)
|
||||
end
|
||||
if geosite_shunt_arg ~= "" then
|
||||
return_shunt = get_geosite(geosite_shunt_arg, file_shunt_host)
|
||||
end
|
||||
if (return_white == nil or return_white == 0) and (return_shunt == nil or return_shunt == 0) then
|
||||
log(" * 解析[分流节点] Geosite 完成")
|
||||
else
|
||||
log(" * 解析[分流节点] Geosite 失败!")
|
||||
end
|
||||
end
|
||||
|
||||
if is_file_nonzero(file_white_host) then
|
||||
local domain_set_name = "passwall-whitehost"
|
||||
tmp_lines = {
|
||||
string.format("domain-set -name %s -file %s", domain_set_name, file_white_host)
|
||||
}
|
||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||
if USE_DIRECT_LIST == "1" then
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_white",
|
||||
"#6:" .. setflag .. "passwall_white6"
|
||||
}
|
||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",")
|
||||
else
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_shunt",
|
||||
"#6:" .. setflag .. "passwall_shunt6"
|
||||
}
|
||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",")
|
||||
end
|
||||
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||
table.insert(tmp_lines, domain_rules_str)
|
||||
insert_array_after(config_lines, tmp_lines, "#--4")
|
||||
end
|
||||
|
||||
if is_file_nonzero(file_shunt_host) then
|
||||
local domain_set_name = "passwall-shuntlist"
|
||||
tmp_lines = {
|
||||
string.format("domain-set -name %s -file %s", domain_set_name, file_shunt_host)
|
||||
}
|
||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ -nameserver %s', domain_set_name, REMOTE_GROUP)
|
||||
domain_rules_str = domain_rules_str .. " -speed-check-mode none"
|
||||
domain_rules_str = domain_rules_str .. " -no-serve-expired"
|
||||
local sets = {
|
||||
"#4:" .. setflag .. "passwall_shunt"
|
||||
}
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
domain_rules_str = domain_rules_str .. " -address #6"
|
||||
domain_rules_str = (not only_global and REMOTE_FAKEDNS == "1")
|
||||
and domain_rules_str
|
||||
or (domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ","))
|
||||
else
|
||||
table.insert(sets, "#6:" .. setflag .. "passwall_shunt6")
|
||||
domain_rules_str = (not only_global and REMOTE_FAKEDNS == "1")
|
||||
and domain_rules_str
|
||||
or (domain_rules_str .. " -d no " .. set_type .. " " .. table.concat(sets, ","))
|
||||
end
|
||||
table.insert(tmp_lines, domain_rules_str)
|
||||
insert_array_after(config_lines, tmp_lines, "#--3")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if #config_lines > 0 then
|
||||
local f_out = io.open(TMP_CONF_FILE, "w")
|
||||
for i = 1, #config_lines do
|
||||
line = config_lines[i]
|
||||
if line ~= "" and not line:find("^#--") then
|
||||
f_out:write(line .. "\n")
|
||||
end
|
||||
end
|
||||
f_out:close()
|
||||
end
|
||||
|
||||
if DEFAULT_DNS_GROUP then
|
||||
log(string.format(" - 默认 DNS 分组:%s", DEFAULT_DNS_GROUP))
|
||||
end
|
||||
|
||||
fs.symlink(TMP_CONF_FILE, SMARTDNS_CONF)
|
||||
sys.call(string.format('echo "conf-file %s" >> /etc/smartdns/custom.conf', string.gsub(SMARTDNS_CONF, appname, appname .. "*")))
|
||||
log(" - 请让SmartDNS作为Dnsmasq的上游或重定向!")
|
||||
1506
luci-app-passwall/root/usr/share/passwall/iptables.sh
Executable file
1506
luci-app-passwall/root/usr/share/passwall/iptables.sh
Executable file
File diff suppressed because it is too large
Load Diff
53
luci-app-passwall/root/usr/share/passwall/lease2hosts.sh
Executable file
53
luci-app-passwall/root/usr/share/passwall/lease2hosts.sh
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
|
||||
# dhcp.leases to hosts
|
||||
|
||||
CONFIG=passwall
|
||||
TMP_PATH=/tmp/etc/${CONFIG}
|
||||
TMP_PATH2=/tmp/etc/${CONFIG}_tmp
|
||||
LOCK_FILE=/tmp/lock/${CONFIG}_lease2hosts.lock
|
||||
LEASE_FILE="/tmp/dhcp.leases"
|
||||
HOSTS_FILE="$TMP_PATH2/dhcp-hosts"
|
||||
TMP_FILE="/tmp/dhcp-hosts.tmp"
|
||||
|
||||
exec 99>"$LOCK_FILE"
|
||||
flock -n 99
|
||||
if [ "$?" != 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
reload_dnsmasq_pids() {
|
||||
local pidfile pid
|
||||
find $TMP_PATH/acl -type f -name 'dnsmasq.pid' 2>/dev/null | while read pidfile; do
|
||||
if [ -s "$pidfile" ]; then
|
||||
read pid < "$pidfile"
|
||||
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||
kill -HUP "$pid"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
while true; do
|
||||
|
||||
if [ -f "$LEASE_FILE" ]; then
|
||||
awk 'NF >= 4 && $4 != "*" {print $3" "$4}' "$LEASE_FILE" | sort > "$TMP_FILE"
|
||||
if [ -s "$TMP_FILE" ]; then
|
||||
if [ ! -f "$HOSTS_FILE" ] || ! cmp -s "$TMP_FILE" "$HOSTS_FILE"; then
|
||||
mv "$TMP_FILE" "$HOSTS_FILE"
|
||||
reload_dnsmasq_pids
|
||||
else
|
||||
rm -f "$TMP_FILE"
|
||||
fi
|
||||
else
|
||||
if [ -s "$HOSTS_FILE" ]; then
|
||||
: > "$HOSTS_FILE"
|
||||
reload_dnsmasq_pids
|
||||
fi
|
||||
rm -f "$TMP_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 60
|
||||
|
||||
done 2>/dev/null
|
||||
46
luci-app-passwall/root/usr/share/passwall/monitor.sh
Executable file
46
luci-app-passwall/root/usr/share/passwall/monitor.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
CONFIG=passwall
|
||||
TMP_PATH=/tmp/etc/$CONFIG
|
||||
TMP_SCRIPT_FUNC_PATH=$TMP_PATH/script_func
|
||||
LOCK_FILE_DIR=/tmp/lock
|
||||
LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}_script.lock
|
||||
|
||||
config_n_get() {
|
||||
local ret=$(uci -q get $CONFIG.$1.$2 2>/dev/null)
|
||||
echo ${ret:=$3}
|
||||
}
|
||||
|
||||
config_t_get() {
|
||||
local index=0
|
||||
[ -n "$4" ] && index=$4
|
||||
local ret=$(uci -q get $CONFIG.@$1[$index].$2 2>/dev/null)
|
||||
echo ${ret:=$3}
|
||||
}
|
||||
|
||||
ENABLED=$(config_t_get global enabled 0)
|
||||
[ "$ENABLED" != 1 ] && return 1
|
||||
ENABLED=$(config_t_get global_delay start_daemon 0)
|
||||
[ "$ENABLED" != 1 ] && return 1
|
||||
sleep 58s
|
||||
while [ "$ENABLED" -eq 1 ]; do
|
||||
[ -f "$LOCK_FILE" ] && {
|
||||
sleep 6s
|
||||
continue
|
||||
}
|
||||
touch $LOCK_FILE
|
||||
|
||||
for filename in $(ls ${TMP_SCRIPT_FUNC_PATH}); do
|
||||
cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename})
|
||||
cmd_check=$(echo $cmd | awk -F '>' '{print $1}')
|
||||
[ -n "$(echo $cmd_check | grep "dns2socks")" ] && cmd_check=$(echo $cmd_check | sed "s#:# #g")
|
||||
icount=$(pgrep -f "$(echo $cmd_check)" | wc -l)
|
||||
if [ $icount = 0 ]; then
|
||||
#echo "${cmd} 进程挂掉,重启" >> /tmp/log/passwall.log
|
||||
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
|
||||
fi
|
||||
done
|
||||
|
||||
rm -f $LOCK_FILE
|
||||
sleep 58s
|
||||
done
|
||||
1481
luci-app-passwall/root/usr/share/passwall/nftables.sh
Executable file
1481
luci-app-passwall/root/usr/share/passwall/nftables.sh
Executable file
File diff suppressed because it is too large
Load Diff
541
luci-app-passwall/root/usr/share/passwall/rule_update.lua
Executable file
541
luci-app-passwall/root/usr/share/passwall/rule_update.lua
Executable file
@@ -0,0 +1,541 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local api = require ("luci.passwall.api")
|
||||
local name = api.appname
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
local jsonc = api.jsonc
|
||||
local fs = api.fs
|
||||
|
||||
local arg1 = arg[1]
|
||||
local arg2 = arg[2]
|
||||
local arg3 = arg[3]
|
||||
|
||||
local nftable_name = "inet passwall"
|
||||
local rule_path = "/usr/share/" .. name .. "/rules"
|
||||
local reboot = 0
|
||||
local gfwlist_update = "0"
|
||||
local chnroute_update = "0"
|
||||
local chnroute6_update = "0"
|
||||
local chnlist_update = "0"
|
||||
local geoip_update = "0"
|
||||
local geosite_update = "0"
|
||||
|
||||
-- match comments/title/whitelist/ip address/excluded_domain
|
||||
local comment_pattern = "^[#!\\[@]+"
|
||||
local ip_pattern = "^%d+%.%d+%.%d+%.%d+"
|
||||
local ip4_ipset_pattern = "^%d+%.%d+%.%d+%.%d+[%/][%d]+$"
|
||||
local ip6_ipset_pattern = ":-[%x]+%:+[%x]-[%/][%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","bing.com"}
|
||||
|
||||
local gfwlist_url = uci:get(name, "@global_rules[0]", "gfwlist_url") or {"https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"}
|
||||
local chnroute_url = uci:get(name, "@global_rules[0]", "chnroute_url") or {"https://ispip.clang.cn/all_cn.txt"}
|
||||
local chnroute6_url = uci:get(name, "@global_rules[0]", "chnroute6_url") or {"https://ispip.clang.cn/all_cn_ipv6.txt"}
|
||||
local chnlist_url = uci:get(name, "@global_rules[0]", "chnlist_url") or {"https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf","https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf","https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/google.china.conf"}
|
||||
local geoip_url = uci:get(name, "@global_rules[0]", "geoip_url") or "https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat"
|
||||
local geosite_url = uci:get(name, "@global_rules[0]", "geosite_url") or "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
|
||||
local asset_location = uci:get(name, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/"
|
||||
local use_nft = uci:get(name, "@global_forwarding[0]", "use_nft") or "0"
|
||||
local geo2rule = uci:get(name, "@global_rules[0]", "geo2rule") or "0"
|
||||
local geoip_update_ok, geosite_update_ok = false, false
|
||||
asset_location = asset_location:match("/$") and asset_location or (asset_location .. "/")
|
||||
|
||||
--兼容旧版本geo下载方式的配置,择机删除。
|
||||
if geoip_url:match(".*/([^/]+)$") == "latest" then
|
||||
geoip_url = "https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat"
|
||||
end
|
||||
if geosite_url:match(".*/([^/]+)$") == "latest" then
|
||||
geosite_url = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
|
||||
end
|
||||
|
||||
if arg3 == "cron" then
|
||||
arg2 = nil
|
||||
end
|
||||
|
||||
local log = function(...)
|
||||
if arg1 then
|
||||
if arg1 == "log" then
|
||||
api.log(...)
|
||||
elseif arg1 == "print" then
|
||||
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
|
||||
print(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function gen_nftset(set_name, ip_type, tmp_file, input_file)
|
||||
f = io.open(input_file, "r")
|
||||
local element = f:read("*all")
|
||||
f:close()
|
||||
|
||||
nft_file, err = io.open(tmp_file, "w")
|
||||
nft_file:write('#!/usr/sbin/nft -f\n')
|
||||
nft_file:write(string.format('define %s = {%s}\n', set_name, string.gsub(element, "%s*%c+", " timeout 3650d, ")))
|
||||
if sys.call(string.format('nft "list set %s %s" >/dev/null 2>&1', nftable_name, set_name)) ~= 0 then
|
||||
nft_file:write(string.format('add set %s %s { type %s; flags interval, timeout; timeout 2d; gc-interval 2d; auto-merge; }\n', nftable_name, set_name, ip_type))
|
||||
end
|
||||
nft_file:write(string.format('add element %s %s $%s\n', nftable_name, set_name, set_name))
|
||||
nft_file:close()
|
||||
sys.call(string.format('nft -f %s &>/dev/null',tmp_file))
|
||||
os.remove(tmp_file)
|
||||
end
|
||||
|
||||
--gen cache for nftset from file
|
||||
local function gen_cache(set_name, ip_type, input_file, output_file)
|
||||
local tmp_dir = "/tmp/"
|
||||
local tmp_file = output_file .. "_tmp"
|
||||
local tmp_set_name = set_name .. "_tmp"
|
||||
gen_nftset(tmp_set_name, ip_type, tmp_file, input_file)
|
||||
sys.call(string.format('nft list set %s %s | sed "s/%s/%s/g" | cat > %s', nftable_name, tmp_set_name, tmp_set_name, set_name, output_file))
|
||||
sys.call(string.format('nft flush set %s %s', nftable_name, tmp_set_name))
|
||||
sys.call(string.format('nft delete set %s %s', nftable_name, tmp_set_name))
|
||||
end
|
||||
|
||||
-- curl
|
||||
local function curl(url, file, valifile)
|
||||
local args = {
|
||||
"-skL", "-w %{http_code}", "--retry 3", "--connect-timeout 3", "--max-time 300", "--speed-limit 51200 --speed-time 15"
|
||||
}
|
||||
if file then
|
||||
args[#args + 1] = "-o " .. file
|
||||
end
|
||||
if valifile then
|
||||
args[#args + 1] = "--dump-header " .. valifile
|
||||
end
|
||||
local return_code, result = api.curl_auto(url, nil, args)
|
||||
return tonumber(result)
|
||||
end
|
||||
|
||||
--check excluded domain
|
||||
local function check_excluded_domain(value)
|
||||
for k,v in ipairs(excluded_domain) do
|
||||
if value:find(v) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function line_count(file_path)
|
||||
local num = 0
|
||||
for _ in io.lines(file_path) do
|
||||
num = num + 1
|
||||
end
|
||||
return num;
|
||||
end
|
||||
|
||||
local function non_file_check(file_path, vali_file)
|
||||
if fs.readfile(file_path, 10) then
|
||||
local size_str = sys.exec("grep -i 'Content-Length' " .. vali_file .. " | tail -n1 | sed 's/[^0-9]//g'")
|
||||
local remote_file_size = tonumber(size_str ~= "" and size_str or nil)
|
||||
local local_file_size = tonumber(fs.stat(file_path, "size"))
|
||||
if remote_file_size and local_file_size then
|
||||
if remote_file_size == local_file_size then
|
||||
return nil;
|
||||
else
|
||||
log("下载文件大小校验出错,原始文件大小" .. remote_file_size .. "B,下载文件大小:" .. local_file_size .. "B。")
|
||||
return true;
|
||||
end
|
||||
else
|
||||
return nil;
|
||||
end
|
||||
else
|
||||
log("下载文件读取出错。")
|
||||
return true;
|
||||
end
|
||||
end
|
||||
|
||||
local function GeoToRule(rule_name, rule_type, out_path)
|
||||
if not api.is_finded("geoview") then
|
||||
log(rule_name .. "生成失败,缺少 geoview 组件。")
|
||||
return false;
|
||||
end
|
||||
local geosite_path = asset_location .. "geosite.dat"
|
||||
local geoip_path = asset_location .. "geoip.dat"
|
||||
local file_path = (rule_type == "domain") and geosite_path or geoip_path
|
||||
local arg
|
||||
if rule_type == "domain" then
|
||||
if rule_name == "gfwlist" then
|
||||
arg = "-type geosite -list gfw"
|
||||
else
|
||||
arg = "-type geosite -list cn"
|
||||
end
|
||||
elseif rule_type == "ip4" then
|
||||
arg = "-type geoip -list cn -ipv6=false"
|
||||
elseif rule_type == "ip6" then
|
||||
arg = "-type geoip -list cn -ipv4=false"
|
||||
end
|
||||
cmd = string.format("geoview -input '%s' %s -lowmem=true -output '%s'", file_path, arg, out_path)
|
||||
sys.exec(cmd)
|
||||
return true;
|
||||
end
|
||||
|
||||
--fetch rule
|
||||
local function fetch_rule(rule_name,rule_type,url,exclude_domain)
|
||||
local sret = 200
|
||||
local sret_tmp = 0
|
||||
local domains = {}
|
||||
local file_tmp = "/tmp/" ..rule_name.. "_tmp"
|
||||
local vali_file = "/tmp/" ..rule_name.. "_vali"
|
||||
local download_file_tmp = "/tmp/" ..rule_name.. "_dl"
|
||||
local unsort_file_tmp = "/tmp/" ..rule_name.. "_unsort"
|
||||
|
||||
if geo2rule == "1" then
|
||||
url = {"geo2rule"}
|
||||
log(rule_name.. " 开始生成...")
|
||||
else
|
||||
log(rule_name.. " 开始更新...")
|
||||
end
|
||||
for k,v in ipairs(url) do
|
||||
if v ~= "geo2rule" then
|
||||
sret_tmp = curl(v, download_file_tmp..k, vali_file..k)
|
||||
if sret_tmp == 200 and non_file_check(download_file_tmp..k, vali_file..k) then
|
||||
log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载文件过程出错,尝试重新下载。")
|
||||
os.remove(download_file_tmp..k)
|
||||
os.remove(vali_file..k)
|
||||
sret_tmp = curl(v, download_file_tmp..k, vali_file..k)
|
||||
if sret_tmp == 200 and non_file_check(download_file_tmp..k, vali_file..k) then
|
||||
sret = 0
|
||||
sret_tmp = 0
|
||||
log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载文件过程出错,请检查网络或下载链接后重试!")
|
||||
end
|
||||
end
|
||||
else
|
||||
if not GeoToRule(rule_name, rule_type, download_file_tmp..k) then return 1 end
|
||||
sret_tmp = 200
|
||||
end
|
||||
|
||||
if sret_tmp == 200 then
|
||||
if rule_name == "gfwlist" and geo2rule == "0" then
|
||||
local domains = {}
|
||||
local gfwlist = io.open(download_file_tmp..k, "r")
|
||||
local decode = api.base64Decode(gfwlist:read("*all"))
|
||||
gfwlist:close()
|
||||
|
||||
gfwlist = io.open(download_file_tmp..k, "w")
|
||||
gfwlist:write(decode)
|
||||
gfwlist:close()
|
||||
end
|
||||
|
||||
if rule_type == "domain" and exclude_domain == true then
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
line = line:gsub("full:", "")
|
||||
if not (string.find(line, comment_pattern) or string.find(line, ip_pattern) or check_excluded_domain(line) or string.find(line, ":")) then
|
||||
local match = string.match(line, domain_pattern)
|
||||
if match then
|
||||
domains[match] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif rule_type == "domain" then
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
line = line:gsub("full:", "")
|
||||
if not (string.find(line, comment_pattern) or string.find(line, ip_pattern) or string.find(line, ":")) then
|
||||
local match = string.match(line, domain_pattern)
|
||||
if match then
|
||||
domains[match] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif rule_type == "ip4" then
|
||||
local out = io.open(unsort_file_tmp, "a")
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
if string.match(line, ip4_ipset_pattern) and not string.match(line, "^0%..*") then
|
||||
out:write(string.format("%s\n", line))
|
||||
end
|
||||
end
|
||||
out:close()
|
||||
|
||||
elseif rule_type == "ip6" then
|
||||
local out = io.open(unsort_file_tmp, "a")
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
if string.match(line, ip6_ipset_pattern) and not string.match(line, "^::(/%d+)?$") then
|
||||
out:write(string.format("%s\n", line))
|
||||
end
|
||||
end
|
||||
out:close()
|
||||
|
||||
end
|
||||
else
|
||||
sret = 0
|
||||
log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载失败,请检查网络或下载链接后重试!")
|
||||
end
|
||||
os.remove(download_file_tmp..k)
|
||||
os.remove(vali_file..k)
|
||||
end
|
||||
|
||||
if sret == 200 then
|
||||
if rule_type == "domain" then
|
||||
local out = io.open(unsort_file_tmp, "w")
|
||||
for k,v in pairs(domains) do
|
||||
out:write(string.format("%s\n", k))
|
||||
end
|
||||
out:close()
|
||||
end
|
||||
sys.call("LC_ALL=C sort -u " .. unsort_file_tmp .. " > " .. file_tmp)
|
||||
os.remove(unsort_file_tmp)
|
||||
|
||||
local old_md5 = sys.exec("echo -n $(md5sum " .. rule_path .. "/" ..rule_name.. " | awk '{print $1}')"):gsub("\n", "")
|
||||
local new_md5 = sys.exec("echo -n $([ -f '" ..file_tmp.. "' ] && md5sum " ..file_tmp.." | awk '{print $1}')"):gsub("\n", "")
|
||||
if old_md5 ~= new_md5 then
|
||||
local count = line_count(file_tmp)
|
||||
if use_nft == "1" and (rule_type == "ip6" or rule_type == "ip4") then
|
||||
local output_file = file_tmp.. ".nft"
|
||||
if rule_type == "ip4" then
|
||||
local set_name = "passwall_" ..rule_name
|
||||
if rule_name == "chnroute" then
|
||||
set_name = "passwall_chn"
|
||||
end
|
||||
gen_cache(set_name, "ipv4_addr", file_tmp, output_file)
|
||||
elseif rule_type == "ip6" then
|
||||
local set_name = "passwall_" ..rule_name
|
||||
if rule_name == "chnroute6" then
|
||||
set_name = "passwall_chn6"
|
||||
end
|
||||
gen_cache(set_name, "ipv6_addr", file_tmp, output_file)
|
||||
end
|
||||
sys.exec(string.format('mv -f %s %s', output_file, rule_path .. "/" ..rule_name.. ".nft"))
|
||||
os.remove(output_file)
|
||||
end
|
||||
sys.exec("mv -f "..file_tmp .. " " ..rule_path .. "/" ..rule_name)
|
||||
reboot = 1
|
||||
log(rule_name.. " 更新成功,总规则数 " ..count.. " 条。")
|
||||
else
|
||||
log(rule_name.. " 版本一致,无需更新。")
|
||||
end
|
||||
else
|
||||
log(rule_name.. " 文件下载失败!")
|
||||
end
|
||||
os.remove(file_tmp)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function fetch_geofile(geo_name, geo_type, url)
|
||||
local tmp_path = "/tmp/" .. geo_name
|
||||
local asset_path = asset_location .. geo_name
|
||||
local down_filename = url:match("^.*/([^/?#]+)")
|
||||
local sha_url = url:gsub(down_filename, down_filename .. ".sha256sum")
|
||||
local sha_path = tmp_path .. ".sha256sum"
|
||||
local vali_file = tmp_path .. ".vali"
|
||||
|
||||
local function verify_sha256(sha_file)
|
||||
return sys.call("sha256sum -c " .. sha_file .. " > /dev/null 2>&1") == 0
|
||||
end
|
||||
|
||||
local sha_verify = curl(sha_url, sha_path) == 200
|
||||
if sha_verify then
|
||||
local f = io.open(sha_path, "r")
|
||||
if f then
|
||||
local content = f:read("*l")
|
||||
f:close()
|
||||
if content then
|
||||
content = content:gsub(down_filename, tmp_path)
|
||||
f = io.open(sha_path, "w")
|
||||
if f then
|
||||
f:write(content)
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
if fs.access(asset_path) then
|
||||
sys.call(string.format("cp -f %s %s", asset_path, tmp_path))
|
||||
if verify_sha256(sha_path) then
|
||||
log(geo_type .. " 版本一致,无需更新。")
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local sret_tmp = curl(url, tmp_path, vali_file)
|
||||
if sret_tmp == 200 and non_file_check(tmp_path, vali_file) then
|
||||
log(geo_type .. " 下载文件过程出错,尝试重新下载。")
|
||||
os.remove(tmp_path)
|
||||
os.remove(vali_file)
|
||||
sret_tmp = curl(url, tmp_path, vali_file)
|
||||
if sret_tmp == 200 and non_file_check(tmp_path, vali_file) then
|
||||
sret_tmp = 0
|
||||
log(geo_type .. " 下载文件过程出错,请检查网络或下载链接后重试!")
|
||||
end
|
||||
end
|
||||
if sret_tmp == 200 then
|
||||
if sha_verify then
|
||||
if verify_sha256(sha_path) then
|
||||
sys.call(string.format("mkdir -p %s && cp -f %s %s", asset_location, tmp_path, asset_path))
|
||||
reboot = 1
|
||||
log(geo_type .. " 更新成功。")
|
||||
if geo_type == "geoip" then
|
||||
geoip_update_ok = true
|
||||
else
|
||||
geosite_update_ok = true
|
||||
end
|
||||
else
|
||||
log(geo_type .. " 更新失败,请稍后重试或更换更新URL。")
|
||||
return 1
|
||||
end
|
||||
else
|
||||
if fs.access(asset_path) and sys.call(string.format("cmp -s %s %s", tmp_path, asset_path)) == 0 then
|
||||
log(geo_type .. " 版本一致,无需更新。")
|
||||
return 0
|
||||
end
|
||||
sys.call(string.format("mkdir -p %s && cp -f %s %s", asset_location, tmp_path, asset_path))
|
||||
reboot = 1
|
||||
log(geo_type .. " 更新成功。")
|
||||
if geo_type == "geoip" then
|
||||
geoip_update_ok = true
|
||||
else
|
||||
geosite_update_ok = true
|
||||
end
|
||||
end
|
||||
else
|
||||
log(geo_type .. " 更新失败,请稍后重试或更换更新URL。")
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function fetch_gfwlist()
|
||||
fetch_rule("gfwlist","domain",gfwlist_url,true)
|
||||
end
|
||||
|
||||
local function fetch_chnroute()
|
||||
fetch_rule("chnroute","ip4",chnroute_url,false)
|
||||
end
|
||||
|
||||
local function fetch_chnroute6()
|
||||
fetch_rule("chnroute6","ip6",chnroute6_url,false)
|
||||
end
|
||||
|
||||
local function fetch_chnlist()
|
||||
fetch_rule("chnlist","domain",chnlist_url,false)
|
||||
end
|
||||
|
||||
local function fetch_geoip()
|
||||
fetch_geofile("geoip.dat","geoip",geoip_url)
|
||||
end
|
||||
|
||||
local function fetch_geosite()
|
||||
fetch_geofile("geosite.dat","geosite",geosite_url)
|
||||
end
|
||||
|
||||
if arg2 then
|
||||
string.gsub(arg2, '[^' .. "," .. ']+', function(w)
|
||||
if w == "gfwlist" then
|
||||
gfwlist_update = "1"
|
||||
end
|
||||
if w == "chnroute" then
|
||||
chnroute_update = "1"
|
||||
end
|
||||
if w == "chnroute6" then
|
||||
chnroute6_update = "1"
|
||||
end
|
||||
if w == "chnlist" then
|
||||
chnlist_update = "1"
|
||||
end
|
||||
if w == "geoip" then
|
||||
geoip_update = "1"
|
||||
end
|
||||
if w == "geosite" then
|
||||
geosite_update = "1"
|
||||
end
|
||||
end)
|
||||
else
|
||||
gfwlist_update = uci:get(name, "@global_rules[0]", "gfwlist_update") or "1"
|
||||
chnroute_update = uci:get(name, "@global_rules[0]", "chnroute_update") or "1"
|
||||
chnroute6_update = uci:get(name, "@global_rules[0]", "chnroute6_update") or "1"
|
||||
chnlist_update = uci:get(name, "@global_rules[0]", "chnlist_update") or "1"
|
||||
geoip_update = uci:get(name, "@global_rules[0]", "geoip_update") or "1"
|
||||
geosite_update = uci:get(name, "@global_rules[0]", "geosite_update") or "1"
|
||||
end
|
||||
if gfwlist_update == "0" and chnroute_update == "0" and chnroute6_update == "0" and chnlist_update == "0" and geoip_update == "0" and geosite_update == "0" then
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
log("开始更新规则...")
|
||||
local function safe_call(func, err_msg)
|
||||
xpcall(func, function(e)
|
||||
log(e)
|
||||
log(debug.traceback())
|
||||
log(err_msg)
|
||||
end)
|
||||
end
|
||||
|
||||
local function remove_tmp_geofile(name)
|
||||
os.remove("/tmp/" .. name .. ".dat")
|
||||
os.remove("/tmp/" .. name .. ".dat.sha256sum")
|
||||
os.remove("/tmp/" .. name .. ".dat.vali")
|
||||
end
|
||||
|
||||
if geo2rule == "1" then
|
||||
if geoip_update == "1" then
|
||||
log("geoip 开始更新...")
|
||||
safe_call(fetch_geoip, "更新geoip发生错误...")
|
||||
remove_tmp_geofile("geoip")
|
||||
end
|
||||
|
||||
if geosite_update == "1" then
|
||||
log("geosite 开始更新...")
|
||||
safe_call(fetch_geosite, "更新geosite发生错误...")
|
||||
remove_tmp_geofile("geosite")
|
||||
end
|
||||
|
||||
if geoip_update_ok then
|
||||
safe_call(fetch_chnroute, "生成chnroute发生错误...")
|
||||
safe_call(fetch_chnroute6, "生成chnroute6发生错误...")
|
||||
end
|
||||
|
||||
if geosite_update_ok then
|
||||
safe_call(fetch_gfwlist, "生成gfwlist发生错误...")
|
||||
safe_call(fetch_chnlist, "生成chnlist发生错误...")
|
||||
end
|
||||
else
|
||||
if gfwlist_update == "1" then
|
||||
safe_call(fetch_gfwlist, "更新gfwlist发生错误...")
|
||||
end
|
||||
|
||||
if chnroute_update == "1" then
|
||||
safe_call(fetch_chnroute, "更新chnroute发生错误...")
|
||||
end
|
||||
|
||||
if chnroute6_update == "1" then
|
||||
safe_call(fetch_chnroute6, "更新chnroute6发生错误...")
|
||||
end
|
||||
|
||||
if chnlist_update == "1" then
|
||||
safe_call(fetch_chnlist, "更新chnlist发生错误...")
|
||||
end
|
||||
|
||||
if geoip_update == "1" then
|
||||
log("geoip 开始更新...")
|
||||
safe_call(fetch_geoip, "更新geoip发生错误...")
|
||||
remove_tmp_geofile("geoip")
|
||||
end
|
||||
|
||||
if geosite_update == "1" then
|
||||
log("geosite 开始更新...")
|
||||
safe_call(fetch_geosite, "更新geosite发生错误...")
|
||||
remove_tmp_geofile("geosite")
|
||||
end
|
||||
end
|
||||
|
||||
uci:set(name, "@global_rules[0]", "gfwlist_update", gfwlist_update)
|
||||
uci:set(name, "@global_rules[0]", "chnroute_update", chnroute_update)
|
||||
uci:set(name, "@global_rules[0]", "chnroute6_update", chnroute6_update)
|
||||
uci:set(name, "@global_rules[0]", "chnlist_update", chnlist_update)
|
||||
uci:set(name, "@global_rules[0]", "geoip_update", geoip_update)
|
||||
uci:set(name, "@global_rules[0]", "geosite_update", geosite_update)
|
||||
api.uci_save(uci, name, true)
|
||||
|
||||
if reboot == 1 then
|
||||
if arg3 == "cron" then
|
||||
if not fs.access("/var/lock/" .. name .. ".lock") then
|
||||
sys.call("touch /tmp/lock/" .. name .. "_cron.lock")
|
||||
end
|
||||
end
|
||||
|
||||
log("重启服务,应用新的规则。")
|
||||
uci:set(name, "@global[0]", "flush_set", "1")
|
||||
api.uci_save(uci, name, true, true)
|
||||
end
|
||||
log("规则更新完毕...\n")
|
||||
117151
luci-app-passwall/root/usr/share/passwall/rules/chnlist
Normal file
117151
luci-app-passwall/root/usr/share/passwall/rules/chnlist
Normal file
File diff suppressed because it is too large
Load Diff
4241
luci-app-passwall/root/usr/share/passwall/rules/chnroute
Normal file
4241
luci-app-passwall/root/usr/share/passwall/rules/chnroute
Normal file
File diff suppressed because it is too large
Load Diff
1525
luci-app-passwall/root/usr/share/passwall/rules/chnroute6
Normal file
1525
luci-app-passwall/root/usr/share/passwall/rules/chnroute6
Normal file
File diff suppressed because it is too large
Load Diff
45
luci-app-passwall/root/usr/share/passwall/rules/direct_host
Normal file
45
luci-app-passwall/root/usr/share/passwall/rules/direct_host
Normal file
@@ -0,0 +1,45 @@
|
||||
cn
|
||||
apple.com
|
||||
microsoft.com
|
||||
dyndns.com
|
||||
douyucdn.cn
|
||||
douyucdn2.cn
|
||||
location.services.mozilla.com
|
||||
|
||||
#steam
|
||||
steamcontent.com
|
||||
dl.steam.clngaa.com
|
||||
dl.steam.ksyna.com
|
||||
st.dl.bscstorage.net
|
||||
st.dl.eccdnx.com
|
||||
st.dl.pinyuncloud.com
|
||||
cdn.mileweb.cs.steampowered.com.8686c.com
|
||||
cdn-ws.content.steamchina.com
|
||||
cdn-qc.content.steamchina.com
|
||||
cdn-ali.content.steamchina.com
|
||||
epicgames-download1-1251447533.file.myqcloud.com
|
||||
|
||||
#DoT/DoH
|
||||
dns.alidns.com
|
||||
doh.pub
|
||||
dot.pub
|
||||
doh.360.cn
|
||||
dot.360.cn
|
||||
|
||||
# Synology ddns
|
||||
account.synology.com
|
||||
checkip.dyndns.org
|
||||
checkip.synology.com
|
||||
checkipv6.dyndns.org
|
||||
checkipv6.synology.com
|
||||
checkport.synology.com
|
||||
ddns.synology.com
|
||||
|
||||
# google
|
||||
dl.google.com
|
||||
|
||||
# github proxy
|
||||
gh-proxy.com
|
||||
gitmirror.com
|
||||
ghfast.top
|
||||
|
||||
10
luci-app-passwall/root/usr/share/passwall/rules/direct_ip
Normal file
10
luci-app-passwall/root/usr/share/passwall/rules/direct_ip
Normal file
@@ -0,0 +1,10 @@
|
||||
114.114.114.114
|
||||
114.114.115.115
|
||||
223.5.5.5
|
||||
223.6.6.6
|
||||
119.29.29.29
|
||||
180.76.76.76
|
||||
1.12.12.12
|
||||
120.53.53.53
|
||||
180.184.1.1
|
||||
180.184.2.2
|
||||
@@ -0,0 +1,26 @@
|
||||
courier.push.apple.com
|
||||
rbsxbxp-mim.vivox.com
|
||||
rbsxbxp.www.vivox.com
|
||||
rbsxbxp-ws.vivox.com
|
||||
rbspsxp.www.vivox.com
|
||||
rbspsxp-mim.vivox.com
|
||||
rbspsxp-ws.vivox.com
|
||||
rbswxp.www.vivox.com
|
||||
rbswxp-mim.vivox.com
|
||||
disp-rbspsp-5-1.vivox.com
|
||||
disp-rbsxbp-5-1.vivox.com
|
||||
proxy.rbsxbp.vivox.com
|
||||
proxy.rbspsp.vivox.com
|
||||
proxy.rbswp.vivox.com
|
||||
rbswp.vivox.com
|
||||
rbsxbp.vivox.com
|
||||
rbspsp.vivox.com
|
||||
rbspsp.www.vivox.com
|
||||
rbswp.www.vivox.com
|
||||
rbsxbp.www.vivox.com
|
||||
rbsxbxp.vivox.com
|
||||
rbspsxp.vivox.com
|
||||
rbswxp.vivox.com
|
||||
Mijia Cloud
|
||||
dlg.io.mi.com
|
||||
marscdn.c2c.wechat.com
|
||||
5895
luci-app-passwall/root/usr/share/passwall/rules/gfwlist
Normal file
5895
luci-app-passwall/root/usr/share/passwall/rules/gfwlist
Normal file
File diff suppressed because it is too large
Load Diff
16
luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv4
Normal file
16
luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv4
Normal file
@@ -0,0 +1,16 @@
|
||||
0.0.0.0/8
|
||||
10.0.0.0/8
|
||||
100.64.0.0/10
|
||||
127.0.0.0/8
|
||||
169.254.0.0/16
|
||||
172.16.0.0/12
|
||||
192.0.0.0/24
|
||||
192.0.2.0/24
|
||||
192.88.99.0/24
|
||||
192.168.0.0/16
|
||||
198.51.100.0/24
|
||||
203.0.113.0/24
|
||||
224.0.0.0/4
|
||||
233.252.0.0/24
|
||||
240.0.0.0/4
|
||||
255.255.255.255/32
|
||||
16
luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv6
Normal file
16
luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv6
Normal file
@@ -0,0 +1,16 @@
|
||||
::/128
|
||||
::1/128
|
||||
::ffff:0:0/96
|
||||
::ffff:0:0:0/96
|
||||
64:ff9b::/96
|
||||
64:ff9b:1::/48
|
||||
100::/64
|
||||
2001::/32
|
||||
2001:20::/28
|
||||
2001:db8::/32
|
||||
2002::/16
|
||||
3fff::/20
|
||||
5f00::/16
|
||||
fc00::/7
|
||||
fe80::/64
|
||||
ff00::/8
|
||||
13
luci-app-passwall/root/usr/share/passwall/rules/proxy_host
Normal file
13
luci-app-passwall/root/usr/share/passwall/rules/proxy_host
Normal file
@@ -0,0 +1,13 @@
|
||||
engage.cloudflareclient.com
|
||||
github.com
|
||||
bing.com
|
||||
c.mi.com
|
||||
apple-relay.apple.com
|
||||
|
||||
#google
|
||||
googleapis.cn
|
||||
googleapis.com
|
||||
google.com.tw
|
||||
google.com.hk
|
||||
gstatic.com
|
||||
xn--ngstr-lra8j.com
|
||||
21
luci-app-passwall/root/usr/share/passwall/rules/proxy_ip
Normal file
21
luci-app-passwall/root/usr/share/passwall/rules/proxy_ip
Normal file
@@ -0,0 +1,21 @@
|
||||
149.154.160.0/20
|
||||
91.108.4.0/22
|
||||
91.108.56.0/24
|
||||
109.239.140.0/24
|
||||
67.198.55.0/24
|
||||
8.8.4.4
|
||||
8.8.8.8
|
||||
208.67.222.222
|
||||
208.67.220.220
|
||||
104.16.249.249
|
||||
104.16.248.249
|
||||
1.1.1.1
|
||||
1.1.1.2
|
||||
1.0.0.1
|
||||
9.9.9.9
|
||||
149.112.112.112
|
||||
2001:67c:4e8::/48
|
||||
2001:b28:f23c::/48
|
||||
2001:b28:f23d::/48
|
||||
2001:b28:f23f::/48
|
||||
2001:b28:f242::/48
|
||||
201
luci-app-passwall/root/usr/share/passwall/socks_auto_switch.sh
Executable file
201
luci-app-passwall/root/usr/share/passwall/socks_auto_switch.sh
Executable file
@@ -0,0 +1,201 @@
|
||||
#!/bin/sh
|
||||
|
||||
CONFIG=passwall
|
||||
LOG_FILE=/tmp/log/$CONFIG.log
|
||||
LOCK_FILE_DIR=/tmp/lock
|
||||
LOG_EVENT_FILTER=
|
||||
LOG_EVENT_CMD=
|
||||
flag=0
|
||||
|
||||
echolog() {
|
||||
local d="$(date "+%Y-%m-%d %H:%M:%S")"
|
||||
local c="$1"
|
||||
echo -e "$d: $c" >> $LOG_FILE
|
||||
[ -n "$LOG_EVENT_CMD" ] && [ -n "$(echo -n $c |grep -E "$LOG_EVENT_FILTER")" ] && {
|
||||
$(echo -n $LOG_EVENT_CMD |sed "s/%s/$c/g")
|
||||
}
|
||||
}
|
||||
|
||||
config_n_get() {
|
||||
local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null)
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
test_url() {
|
||||
local url=$1
|
||||
local try=1
|
||||
[ -n "$2" ] && try=$2
|
||||
local timeout=2
|
||||
[ -n "$3" ] && timeout=$3
|
||||
local extra_params=$4
|
||||
if /usr/bin/curl --help all | grep -q "\-\-retry-all-errors"; then
|
||||
extra_params="--retry-all-errors ${extra_params}"
|
||||
fi
|
||||
local status=$(/usr/bin/curl -I -o /dev/null -skL ${extra_params} --connect-timeout ${timeout} --retry ${try} -w %{http_code} "$url")
|
||||
case "$status" in
|
||||
204)
|
||||
status=200
|
||||
;;
|
||||
esac
|
||||
echo $status
|
||||
}
|
||||
|
||||
test_proxy() {
|
||||
local result=0
|
||||
local status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x socks5h://127.0.0.1:${socks_port}")
|
||||
if [ "$status" = "200" ]; then
|
||||
result=0
|
||||
else
|
||||
local status2=$(test_url "https://www.baidu.com" ${retry_num} ${connect_timeout})
|
||||
if [ "$status2" = "200" ]; then
|
||||
result=1
|
||||
else
|
||||
result=2
|
||||
ping -c 3 -W 1 223.5.5.5 > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && {
|
||||
result=1
|
||||
}
|
||||
fi
|
||||
fi
|
||||
echo $result
|
||||
}
|
||||
|
||||
test_node() {
|
||||
local node_id=$1
|
||||
local _type=$(echo $(config_n_get ${node_id} type) | tr 'A-Z' 'a-z')
|
||||
[ -n "${_type}" ] && {
|
||||
local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp,udp)
|
||||
/usr/share/${CONFIG}/app.sh run_socks flag="test_node_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=test_node_${node_id}.json
|
||||
local curlx="socks5h://127.0.0.1:${_tmp_port}"
|
||||
sleep 1s
|
||||
local _proxy_status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x $curlx")
|
||||
# 结束 SS 插件进程
|
||||
local pid_file="/tmp/etc/${CONFIG}/test_node_${node_id}_plugin.pid"
|
||||
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
|
||||
pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
|
||||
rm -rf /tmp/etc/${CONFIG}/test_node_${node_id}*.*
|
||||
if [ "${_proxy_status}" -eq 200 ]; then
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
test_auto_switch() {
|
||||
flag=$((flag + 1))
|
||||
local b_nodes=$1
|
||||
local now_node=$2
|
||||
[ -z "$now_node" ] && {
|
||||
if [ -n "$(/usr/share/${CONFIG}/app.sh get_cache_var "socks_${id}")" ]; then
|
||||
now_node=$(/usr/share/${CONFIG}/app.sh get_cache_var "socks_${id}")
|
||||
else
|
||||
#echolog "Socks切换检测:未知错误"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
[ $flag -le 1 ] && {
|
||||
main_node=$now_node
|
||||
}
|
||||
|
||||
local status=$(test_proxy)
|
||||
if [ "$status" = "2" ]; then
|
||||
echolog "Socks切换检测:无法连接到网络,请检查网络是否正常!"
|
||||
return 2
|
||||
fi
|
||||
|
||||
#检测主节点是否能使用
|
||||
if [ "$restore_switch" = "1" ] && [ -n "$main_node" ] && [ "$now_node" != "$main_node" ]; then
|
||||
test_node ${main_node}
|
||||
[ $? -eq 0 ] && {
|
||||
#主节点正常,切换到主节点
|
||||
echolog "Socks切换检测:${id}主节点【$(config_n_get $main_node type):[$(config_n_get $main_node remarks)]】正常,切换到主节点!"
|
||||
/usr/share/${CONFIG}/app.sh socks_node_switch flag=${id} new_node=${main_node}
|
||||
[ $? -eq 0 ] && {
|
||||
echolog "Socks切换检测:${id}节点切换完毕!"
|
||||
}
|
||||
return 0
|
||||
}
|
||||
fi
|
||||
|
||||
if [ "$status" = "0" ]; then
|
||||
#echolog "Socks切换检测:${id}【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】正常。"
|
||||
return 0
|
||||
elif [ "$status" = "1" ]; then
|
||||
local new_node msg
|
||||
if [ "$backup_node_num" -gt 1 ]; then
|
||||
# 有多个后备节点时
|
||||
local first_node found node
|
||||
for node in $b_nodes; do
|
||||
[ -z "$first_node" ] && first_node="$node" # 记录第一个节点
|
||||
[ "$found" = "1" ] && { new_node="$node"; break; } # 找到当前节点后取下一个
|
||||
[ "$node" = "$now_node" ] && found=1 # 标记找到当前节点
|
||||
done
|
||||
# 如果没找到当前节点,或者当前节点是最后一个,就取第一个节点
|
||||
[ -z "$new_node" ] && new_node="$first_node"
|
||||
msg="切换到$([ "$now_node" = "$main_node" ] && echo 备用节点 || echo 下一个备用节点)检测!"
|
||||
else
|
||||
# 只有一个后备节点时,与主节点轮询
|
||||
new_node=$([ "$now_node" = "$main_node" ] && echo "$b_nodes" || echo "$main_node")
|
||||
msg="切换到$([ "$now_node" = "$main_node" ] && echo 备用节点 || echo 主节点)检测!"
|
||||
fi
|
||||
echolog "Socks切换检测:${id}【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】异常,$msg"
|
||||
test_node ${new_node}
|
||||
if [ $? -eq 0 ]; then
|
||||
# [ "$restore_switch" = "0" ] && {
|
||||
# uci set $CONFIG.${id}.node=$new_node
|
||||
# [ -z "$(echo $b_nodes | grep $main_node)" ] && uci add_list $CONFIG.${id}.autoswitch_backup_node=$main_node
|
||||
# uci commit $CONFIG
|
||||
# }
|
||||
echolog "Socks切换检测:${id}【$(config_n_get $new_node type):[$(config_n_get $new_node remarks)]】正常,切换到此节点!"
|
||||
/usr/share/${CONFIG}/app.sh socks_node_switch flag=${id} new_node=${new_node}
|
||||
[ $? -eq 0 ] && {
|
||||
echolog "Socks切换检测:${id}节点切换完毕!"
|
||||
}
|
||||
return 0
|
||||
else
|
||||
test_auto_switch "${b_nodes}" ${new_node}
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
id=$1
|
||||
LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}_socks_auto_switch_${id}.lock
|
||||
LOG_EVENT_FILTER=$(uci -q get "${CONFIG}.global[0].log_event_filter" 2>/dev/null)
|
||||
LOG_EVENT_CMD=$(uci -q get "${CONFIG}.global[0].log_event_cmd" 2>/dev/null)
|
||||
main_node=$(config_n_get $id node)
|
||||
socks_port=$(config_n_get $id port 0)
|
||||
delay=$(config_n_get $id autoswitch_testing_time 30)
|
||||
connect_timeout=$(config_n_get $id autoswitch_connect_timeout 3)
|
||||
retry_num=$(config_n_get $id autoswitch_retry_num 1)
|
||||
restore_switch=$(config_n_get $id autoswitch_restore_switch 0)
|
||||
probe_url=$(config_n_get $id autoswitch_probe_url "https://www.google.com/generate_204")
|
||||
backup_node=$(config_n_get $id autoswitch_backup_node)
|
||||
if [ -n "$backup_node" ]; then
|
||||
backup_node=$(echo "$backup_node" | tr -s ' ' '\n' | uniq | tr -s '\n' ' ')
|
||||
backup_node_num=$(printf "%s\n" "$backup_node" | wc -w)
|
||||
if [ "$backup_node_num" -eq 1 ]; then
|
||||
[ "$main_node" = "$backup_node" ] && return
|
||||
fi
|
||||
else
|
||||
return
|
||||
fi
|
||||
while [ -n "$backup_node" ]; do
|
||||
[ -f "$LOCK_FILE" ] && {
|
||||
sleep 6s
|
||||
continue
|
||||
}
|
||||
pgrep -af "${CONFIG}/" | awk '/app\.sh.*(start|stop)/ || /nftables\.sh/ || /iptables\.sh/ { found = 1 } END { exit !found }' && {
|
||||
# 特定任务执行中不检测
|
||||
sleep 6s
|
||||
continue
|
||||
}
|
||||
touch $LOCK_FILE
|
||||
test_auto_switch "$backup_node"
|
||||
rm -f $LOCK_FILE
|
||||
sleep ${delay}
|
||||
done
|
||||
}
|
||||
|
||||
start $@
|
||||
2040
luci-app-passwall/root/usr/share/passwall/subscribe.lua
Executable file
2040
luci-app-passwall/root/usr/share/passwall/subscribe.lua
Executable file
File diff suppressed because it is too large
Load Diff
102
luci-app-passwall/root/usr/share/passwall/tasks.sh
Executable file
102
luci-app-passwall/root/usr/share/passwall/tasks.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/sh
|
||||
|
||||
## 循环更新脚本
|
||||
|
||||
CONFIG=passwall
|
||||
APP_PATH=/usr/share/$CONFIG
|
||||
TMP_PATH=/tmp/etc/$CONFIG
|
||||
LOCK_FILE=/tmp/lock/${CONFIG}_tasks.lock
|
||||
CFG_UPDATE_INT=0
|
||||
|
||||
config_n_get() {
|
||||
local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null)
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
config_t_get() {
|
||||
local index=${4:-0}
|
||||
local ret=$(uci -q get "${CONFIG}.@${1}[${index}].${2}" 2>/dev/null)
|
||||
echo "${ret:=${3}}"
|
||||
}
|
||||
|
||||
exec 99>"$LOCK_FILE"
|
||||
flock -n 99
|
||||
if [ "$?" != 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
while true
|
||||
do
|
||||
|
||||
if [ "$CFG_UPDATE_INT" -ne 0 ]; then
|
||||
|
||||
stop_week_mode=$(config_t_get global_delay stop_week_mode)
|
||||
stop_interval_mode=$(config_t_get global_delay stop_interval_mode)
|
||||
stop_interval_mode=$(expr "$stop_interval_mode" \* 60)
|
||||
if [ -n "$stop_week_mode" ]; then
|
||||
[ "$stop_week_mode" = "8" ] && {
|
||||
[ "$(expr "$CFG_UPDATE_INT" % "$stop_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG stop > /dev/null 2>&1 &
|
||||
}
|
||||
fi
|
||||
|
||||
start_week_mode=$(config_t_get global_delay start_week_mode)
|
||||
start_interval_mode=$(config_t_get global_delay start_interval_mode)
|
||||
start_interval_mode=$(expr "$start_interval_mode" \* 60)
|
||||
if [ -n "$start_week_mode" ]; then
|
||||
[ "$start_week_mode" = "8" ] && {
|
||||
[ "$(expr "$CFG_UPDATE_INT" % "$start_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG start > /dev/null 2>&1 &
|
||||
}
|
||||
fi
|
||||
|
||||
restart_week_mode=$(config_t_get global_delay restart_week_mode)
|
||||
restart_interval_mode=$(config_t_get global_delay restart_interval_mode)
|
||||
restart_interval_mode=$(expr "$restart_interval_mode" \* 60)
|
||||
if [ -n "$restart_week_mode" ]; then
|
||||
[ "$restart_week_mode" = "8" ] && {
|
||||
[ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG restart > /dev/null 2>&1 &
|
||||
}
|
||||
fi
|
||||
|
||||
autoupdate=$(config_t_get global_rules auto_update)
|
||||
weekupdate=$(config_t_get global_rules week_update)
|
||||
hourupdate=$(config_t_get global_rules interval_update)
|
||||
hourupdate=$(expr "$hourupdate" \* 60)
|
||||
if [ "$autoupdate" = "1" ]; then
|
||||
[ "$weekupdate" = "8" ] && {
|
||||
[ "$(expr "$CFG_UPDATE_INT" % "$hourupdate")" -eq 0 ] && lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 &
|
||||
}
|
||||
fi
|
||||
|
||||
TMP_SUB_PATH=$TMP_PATH/sub_tasks
|
||||
mkdir -p $TMP_SUB_PATH
|
||||
for item in $(uci show ${CONFIG} | grep "=subscribe_list" | cut -d '.' -sf 2 | cut -d '=' -sf 1); do
|
||||
if [ "$(config_n_get $item auto_update 0)" = "1" ]; then
|
||||
cfgid=$(uci show ${CONFIG}.$item | head -n 1 | cut -d '.' -sf 2 | cut -d '=' -sf 1)
|
||||
remark=$(config_n_get $item remark)
|
||||
week_update=$(config_n_get $item week_update)
|
||||
hour_update=$(config_n_get $item interval_update)
|
||||
echo "$cfgid" >> $TMP_SUB_PATH/${week_update}_${hour_update}
|
||||
fi
|
||||
done
|
||||
|
||||
[ -d "${TMP_SUB_PATH}" ] && {
|
||||
for name in $(ls ${TMP_SUB_PATH}); do
|
||||
week_update=$(echo $name | awk -F '_' '{print $1}')
|
||||
hour_update=$(echo $name | awk -F '_' '{print $2}')
|
||||
hour_update=$(expr "$hour_update" \* 60)
|
||||
cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g')
|
||||
[ "$week_update" = "8" ] && {
|
||||
[ "$(expr "$CFG_UPDATE_INT" % "$hour_update")" -eq 0 ] && lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 &
|
||||
}
|
||||
|
||||
done
|
||||
rm -rf $TMP_SUB_PATH
|
||||
}
|
||||
|
||||
fi
|
||||
|
||||
CFG_UPDATE_INT=$(expr "$CFG_UPDATE_INT" + 10)
|
||||
|
||||
sleep 600
|
||||
|
||||
done 2>/dev/null
|
||||
115
luci-app-passwall/root/usr/share/passwall/test.sh
Executable file
115
luci-app-passwall/root/usr/share/passwall/test.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/bin/sh
|
||||
|
||||
CONFIG=passwall
|
||||
LOG_FILE=/tmp/log/$CONFIG.log
|
||||
|
||||
echolog() {
|
||||
local d="$(date "+%Y-%m-%d %H:%M:%S")"
|
||||
#echo -e "$d: $1"
|
||||
echo -e "$d: $1" >> $LOG_FILE
|
||||
}
|
||||
|
||||
config_n_get() {
|
||||
local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null)
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
lua_api() {
|
||||
local func=${1}
|
||||
[ -z "${func}" ] && {
|
||||
echo "nil"
|
||||
return
|
||||
}
|
||||
echo $(lua -e "local api = require 'luci.passwall.api' print(api.${func})")
|
||||
}
|
||||
|
||||
test_url() {
|
||||
local url=$1
|
||||
local try=1
|
||||
[ -n "$2" ] && try=$2
|
||||
local timeout=2
|
||||
[ -n "$3" ] && timeout=$3
|
||||
local extra_params=$4
|
||||
curl --help all | grep "\-\-retry-all-errors" > /dev/null
|
||||
[ $? == 0 ] && extra_params="--retry-all-errors ${extra_params}"
|
||||
status=$(/usr/bin/curl -I -o /dev/null -skL $extra_params --connect-timeout ${timeout} --retry ${try} -w %{http_code} "$url")
|
||||
case "$status" in
|
||||
204|\
|
||||
200)
|
||||
status=200
|
||||
;;
|
||||
esac
|
||||
echo $status
|
||||
}
|
||||
|
||||
test_proxy() {
|
||||
result=0
|
||||
status=$(test_url "https://www.google.com/generate_204" ${retry_num} ${connect_timeout})
|
||||
if [ "$status" = "200" ]; then
|
||||
result=0
|
||||
else
|
||||
status2=$(test_url "https://www.baidu.com" ${retry_num} ${connect_timeout})
|
||||
if [ "$status2" = "200" ]; then
|
||||
result=1
|
||||
else
|
||||
result=2
|
||||
ping -c 3 -W 1 223.5.5.5 > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && {
|
||||
result=1
|
||||
}
|
||||
fi
|
||||
fi
|
||||
echo $result
|
||||
}
|
||||
|
||||
url_test_node() {
|
||||
result=0
|
||||
local node_id=$1
|
||||
local _type=$(echo $(config_n_get ${node_id} type) | tr 'A-Z' 'a-z')
|
||||
[ -n "${_type}" ] && {
|
||||
if [ "${_type}" == "socks" ]; then
|
||||
local _address=$(config_n_get ${node_id} address)
|
||||
local _port=$(config_n_get ${node_id} port)
|
||||
[ -n "${_address}" ] && [ -n "${_port}" ] && {
|
||||
local curlx="socks5h://${_address}:${_port}"
|
||||
local _username=$(config_n_get ${node_id} username)
|
||||
local _password=$(config_n_get ${node_id} password)
|
||||
[ -n "${_username}" ] && [ -n "${_password}" ] && curlx="socks5h://${_username}:${_password}@${_address}:${_port}"
|
||||
}
|
||||
else
|
||||
local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp)
|
||||
/usr/share/${CONFIG}/app.sh run_socks flag="url_test_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=url_test_${node_id}.json
|
||||
local curlx="socks5h://127.0.0.1:${_tmp_port}"
|
||||
fi
|
||||
sleep 1s
|
||||
# 兼容 curl 8.6 time_starttransfer 错误
|
||||
local _cmd="-V 2>/dev/null | head -n 1 | awk '{print \$2}' | cut -d. -f1,2 | tr -d ' \\n'"
|
||||
local _curl="/usr/bin/curl"
|
||||
local curl_ver=$(lua_api "get_bin_version_cache(\"${_curl}\", \"${_cmd}\")")
|
||||
|
||||
local curl_arg="-w %{http_code}:%{time_starttransfer} http://"
|
||||
[ "${curl_ver}" = "8.6" ] && curl_arg="-w %{http_code}:%{time_appconnect} https://"
|
||||
|
||||
local chn_list=$(config_n_get @global[0] chn_list direct)
|
||||
local probeUrl="www.google.com/generate_204"
|
||||
[ "${chn_list}" = "proxy" ] && probeUrl="www.baidu.com"
|
||||
result=$(${_curl} --max-time 5 -o /dev/null -I -skL -x ${curlx} ${curl_arg}${probeUrl})
|
||||
# 结束 SS 插件进程
|
||||
local pid_file="/tmp/etc/${CONFIG}/url_test_${node_id}_plugin.pid"
|
||||
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
|
||||
pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
|
||||
rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.*
|
||||
}
|
||||
echo $result
|
||||
}
|
||||
|
||||
arg1=$1
|
||||
shift
|
||||
case $arg1 in
|
||||
test_url)
|
||||
test_url $@
|
||||
;;
|
||||
url_test_node)
|
||||
url_test_node $@
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"luci-app-passwall": {
|
||||
"description": "Grant UCI access for luci-app-passwall",
|
||||
"read": {
|
||||
"uci": [ "passwall", "passwall_server" ]
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "passwall", "passwall_server" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"config": "passwall_server",
|
||||
"init": "passwall_server"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"config": "passwall",
|
||||
"init": "passwall"
|
||||
}
|
||||
Reference in New Issue
Block a user