update 2026-10-07 18:47:42

This commit is contained in:
action committed 2026-10-07 18:47:42 +08:00
1 parent 617f7ca2a6
commit bd061545ed
4832 files changed
+1693456

No files matched your search

@@ -0,0 +1,4 @@
config global 'global'
option enable '0'
@@ -0,0 +1,35 @@
#!/bin/sh
[ "$ACTION" = "ifup" ] || [ "$ACTION" = "ifupdate" ] || exit 0
. /usr/share/passwall2/utils.sh
([ "$(get_cache_var "ENABLED_DEFAULT_ACL")" = "1" ] || [ "$(get_cache_var "ENABLED_ACLS")" = "1" ]) && [ -f ${LOCK_PATH}/${CONFIG}_ready.lock ] && {
default_device=$(ip route show default 2>/dev/null | awk -F 'dev ' '{print $2}' | awk '{print $1}' | head -n1)
default6_device=$(ip -6 route show default 2>/dev/null | awk -F 'dev ' '{print $2}' | awk '{print $1}' | head -n1)
( [ "$default_device" = "$DEVICE" ] || [ "$default6_device" = "$DEVICE" ] ) && {
if [ "$ACTION" = "ifupdate" ]; then
USE_TABLES=$(get_cache_var "USE_TABLES")
[ -n "$USE_TABLES" ] && { ${APP_PATH}/${USE_TABLES}.sh update_wan_sets >/dev/null 2>&1 & }
exit 0
fi
[ ! -d ${LOCK_PATH} ] && mkdir -p ${LOCK_PATH}
LOCK_FILE="${LOCK_PATH}/${CONFIG}_ifup.lock"
if [ -s ${LOCK_FILE} ]; then
SPID=$(cat ${LOCK_FILE})
if [ -e /proc/${SPID}/status ]; then
exit 1
fi
cat /dev/null > ${LOCK_FILE}
fi
echo $$ > ${LOCK_FILE}
/etc/init.d/${CONFIG} restart >/dev/null 2>&1 &
logger -p notice -t network -s "${CONFIG}: restart when $INTERFACE ifup"
rm -rf ${LOCK_FILE}
}
}
@@ -0,0 +1,22 @@
#!/bin/sh
[ "$ACTION" = "step" ] || exit 0
offset=${offset#-}
offset=${offset%.*}
# Skip service restart if the time adjustment is less than 120 seconds.
[ "$offset" -lt 120 ] && exit 0
. /usr/share/passwall2/utils.sh
NTP_LOCK_FILE="${LOCK_PATH}/${CONFIG}_ntp.lock"
[ -f "${NTP_LOCK_FILE}" ] && exit 0
([ "$(get_cache_var "ENABLED_DEFAULT_ACL")" = "1" ] || [ "$(get_cache_var "ENABLED_ACLS")" = "1" ]) && [ -f ${LOCK_PATH}/${CONFIG}_ready.lock ] && {
echo $$ > ${NTP_LOCK_FILE}
/etc/init.d/${CONFIG} restart >/dev/null 2>&1 &
logger -p notice -t network -s "${CONFIG}: restart after NTP time step (${offset}s)"
}
+87
View File
@@ -0,0 +1,87 @@
#!/bin/sh /etc/rc.common
START=99
STOP=15
. $IPKG_INSTROOT/usr/share/passwall2/utils.sh
APP_FILE=${APP_PATH}/app.sh
LOCK_FILE=${LOCK_PATH}/${CONFIG}.lock
set_lock() {
[ ! -d "$LOCK_PATH" ] && mkdir -p $LOCK_PATH
exec 999>"$LOCK_FILE"
flock -xn 999
}
unset_lock() {
flock -u 999
rm -rf "$LOCK_FILE"
}
unlock() {
failcount=1
while [ "$failcount" -le 10 ]; do
if [ -f "$LOCK_FILE" ]; then
let "failcount++"
sleep 1s
[ "$failcount" -ge 10 ] && unset_lock
else
break
fi
done
}
boot_func() {
rm -f ${LOCK_PATH}/${CONFIG}_ready.lock
rm -f ${LOCK_PATH}/${CONFIG}_rule_update.lock
rm -f ${LOCK_PATH}/${CONFIG}_subscribe.lock
local delay=$(uci -q get ${CONFIG}.@global_delay[0].start_delay || echo 1)
if [ "$delay" -gt 0 ]; then
log_i18n 0 "Start after a delay of %s seconds!" "${delay}"
sleep $delay
fi
restart boot
touch ${LOCK_PATH}/${CONFIG}_ready.lock
}
boot() {
boot_func >/dev/null 2>&1 &
}
start() {
set_lock
[ $? == 1 ] && log_i18n 0 "The script is already running, do not run it again. Exit." && exit 0
$APP_FILE start
unset_lock
}
stop() {
unlock
set_lock
[ $? == 1 ] && log_i18n 0 "Stop the script and wait for a timeout, then exit without repeating the process." && exit 0
$APP_FILE stop
unset_lock
}
restart() {
set_lock
[ $? == 1 ] && log_i18n 0 "The script is already running, do not run it again. Exit." && exit 0
rule_lock=${LOCK_PATH}/${CONFIG}_rule_update.lock
sub_lock=${LOCK_PATH}/${CONFIG}_subscribe.lock
for i in $(seq 1 300); do
if [ ! -f "$rule_lock" ] && [ ! -f "$sub_lock" ]; then
break
fi
sleep 2
done
rm -f "$rule_lock" "$sub_lock"
$APP_FILE stop
$APP_FILE start $1
unset_lock
}
reload() {
echo "Warning: This service does not support configuration reloading"
echo "Performing full restart instead..."
restart
}
+31
View File
@@ -0,0 +1,31 @@
#!/bin/sh /etc/rc.common
START=99
boot_func() {
sleep 90
restart
}
boot() {
boot_func >/dev/null 2>&1 &
}
start() {
lua /usr/lib/lua/luci/passwall2/server_app.lua start
}
stop() {
lua /usr/lib/lua/luci/passwall2/server_app.lua stop
}
restart() {
stop
start
}
reload() {
echo "Warning: This service does not support configuration reloading"
echo "Performing full restart instead..."
restart
}
@@ -0,0 +1,41 @@
#!/bin/sh
if [ -e "/etc/config/ucitrack" ]; then
uci -q batch <<-EOF
delete ucitrack.@passwall2[-1]
add ucitrack passwall2
set ucitrack.@passwall2[-1].init=passwall2
commit ucitrack
EOF
fi
uci -q batch <<-EOF
delete firewall.passwall2
set firewall.passwall2=include
set firewall.passwall2.type='script'
set firewall.passwall2.path='/var/etc/passwall2.include'
set dhcp.@dnsmasq[0].localuse=1
commit dhcp
set uhttpd.main.max_requests=50
commit uhttpd
EOF
if [ -x "/sbin/fw3" ]; then
uci -q set firewall.passwall2.reload='1'
else
uci -q delete firewall.passwall2.reload
uci -q delete firewall.passwall2.fw4_compatible
fi
uci commit firewall
[ ! -s "/etc/config/passwall2" ] && cp -f /usr/share/passwall2/0_default_config /etc/config/passwall2
chmod +x /usr/share/passwall2/*.sh
rm -f /tmp/luci-indexcache /tmp/luci-indexcache.*
rm -rf /tmp/luci-modulecache/
killall -HUP rpcd 2>/dev/null
exit 0
@@ -0,0 +1,16 @@
#!/bin/sh
if [ -e "/etc/config/ucitrack" ]; then
uci -q batch <<-EOF
delete ucitrack.@passwall2_server[-1]
add ucitrack passwall2_server
set ucitrack.@passwall2_server[-1].init=passwall2_server
commit ucitrack
EOF
fi
rm -f /tmp/luci-indexcache /tmp/luci-indexcache.*
rm -rf /tmp/luci-modulecache/
killall -HUP rpcd 2>/dev/null
exit 0
@@ -0,0 +1,228 @@
config global
option enabled '0'
option node_socks_port '1070'
option localhost_proxy '1'
option client_proxy '1'
option socks_enabled '0'
option acl_enable '0'
option node 'rulenode'
option direct_dns_protocol 'auto'
option direct_dns_query_strategy 'UseIP'
option remote_dns_protocol 'tcp'
option remote_dns '1.1.1.1'
option remote_dns_query_strategy 'UseIPv4'
option dns_hosts 'cloudflare-dns.com 1.1.1.1
dns.google.com 8.8.8.8'
option log_node '1'
option loglevel 'error'
config global_haproxy
option balancing_enable '0'
config global_delay
option start_daemon '1'
option start_delay '60'
config global_forwarding
option tcp_redir_ports '1:65535'
option udp_redir_ports '1:65535'
option accept_icmp '0'
option prefer_nft '1'
option tcp_proxy_way 'redirect'
option ipv6_tproxy '0'
config global_xray
option sniffing_override_dest '0'
config global_other
option auto_detection_time 'tcping'
option show_node_info '0'
config global_rules
option geosite_update '1'
option geoip_update '1'
option v2ray_location_asset '/usr/share/v2ray/'
option geoip_url 'https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat'
option geosite_url 'https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat'
config global_app
option geoview_file '/usr/bin/geoview'
option xray_file '/usr/bin/xray'
option sing_box_file '/usr/bin/sing-box'
config global_subscribe
option filter_keyword_mode '1'
config global_singbox
config nodes 'examplenode'
option remarks 'Example'
option type 'Xray'
option protocol 'socks'
option address 'passwall2.github'
option port '1080'
option tls '0'
option transport 'raw'
option tcp_guise 'none'
option tcp_fast_open '0'
option tcpMptcp '0'
config nodes 'rulenode'
option remarks 'rulenode'
option type 'Xray'
option protocol '_shunt'
option PrivateIP '_direct'
option default_node 'examplenode'
option domainStrategy 'IPOnDemand'
option domainMatcher 'hybrid'
option write_ipset_direct '1'
option enable_geoview_ip '1'
option shunt_group 'CN'
config shunt_rules 'DirectFront'
option remarks 'DirectFront'
option network 'tcp,udp'
option domain_list 'domain:ip233.cn'
option group 'CN'
config shunt_rules 'ProxyFront'
option remarks 'ProxyFront'
option network 'tcp,udp'
option domain_list 'domain:www.gstatic.com
domain:ssl.gstatic.com
domain:fonts.gstatic.com'
option group 'CN'
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'
option network 'tcp,udp'
option group 'CN'
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
domain:blizzard.com
geosite:category-games'
option network 'tcp,udp'
option group 'CN'
config shunt_rules 'CN'
option remarks 'CN'
option domain_list 'geosite:cn'
option ip_list 'geoip:cn'
option network 'tcp,udp'
option group 'CN'
config shunt_rules 'Iran'
option remarks 'Iran'
option network 'tcp,udp'
option domain_list 'geosite:ir
#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-ir.srs'
option ip_list 'geoip:ir
#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ir.srs'
option group 'IR'
config shunt_rules 'Russia_Block'
option remarks 'Russia_Block'
option network 'tcp,udp'
option domain_list 'geosite:ru-blocked
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geosite/geosite-ru-blocked.srs'
option ip_list 'geoip:ru-blocked
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru-blocked.srs'
option group 'RU'
config shunt_rules 'Russia'
option remarks 'Russia'
option network 'tcp,udp'
option ip_list 'geoip:ru
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru.srs'
option group 'RU'
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,230 @@
-- Copyright (C) 2026 xiaorouji
local api = require "luci.passwall2.api"
local jsonc = api.jsonc
local sys = api.sys
local fs = api.fs
local i18n = api.i18n
local log = api.log
local uci, uci_get, uci_set, uci_del, uci_foreach, uci_save = api.uci, api.uci_get_c, api.uci_set_c, api.uci_del_c, api.uci_foreach_c, api.uci_save_c
local VAR = {} -- Set VAR
local D = {} -- Default Global Access Control
local L = {
acl = {},
node = {},
node_order = {}
}
function add_args(t, k, v)
if not t or not k or not v then return end
table.insert(t, k .. "=" .. '"' .. v .. '"')
end
function init_acl()
if true then
-- Get Default AC
D.flag = "acl_default"
D.remarks = api.i18n.translatef("Default")
D.tcp_no_redir_ports = uci_get("@global_forwarding[0]", "tcp_no_redir_ports")
D.udp_no_redir_ports = uci_get("@global_forwarding[0]", "udp_no_redir_ports")
D.tcp_redir_ports = uci_get("@global_forwarding[0]", "tcp_redir_ports")
D.udp_redir_ports = uci_get("@global_forwarding[0]", "udp_redir_ports")
D.node = uci_get("@global[0]", "node")
D.local_proxy = uci_get("@global[0]", "localhost_proxy")
D.client_proxy = uci_get("@global[0]", "client_proxy")
if D.node then
local node = uci_get(D.node)
if node and node[".type"] == "nodes" then
local n = {
config = node,
enabled = uci_get("@global[0]", "enabled") or "0",
log = uci_get("@global[0]", "log_node") or "0",
loglevel = uci_get("@global[0]", "loglevel") or "warn",
socks_listen = uci_get("@global[0]", "node_socks_bind_local") == "0" and "0.0.0.0" or "127.0.0.1",
socks_port = uci_get("@global[0]", "node_socks_port"),
redir_port = api.get_new_port(),
dns_port = api.get_new_port(),
direct_dns_query_strategy = uci_get("@global[0]", "direct_dns_query_strategy") or "UseIP",
remote_dns_protocol = uci_get("@global[0]", "remote_dns_protocol") or "tcp",
remote_dns_detour = uci_get("@global[0]", "remote_dns_detour") or "remote",
remote_dns = uci_get("@global[0]", "remote_dns") or "1.1.1.1:53",
remote_dns_doh = uci_get("@global[0]", "remote_dns_doh") or "https://1.1.1.1/dns-query",
remote_dns_client_ip = uci_get("@global[0]", "remote_dns_client_ip"),
remote_dns_query_strategy = uci_get("@global[0]", "remote_dns_query_strategy") or "UseIPv4",
remote_rewrite_ttl = uci_get("@global[0]", "remote_rewrite_ttl"),
remote_fakedns = uci_get("@global[0]", "remote_fakedns") or "0",
}
L.node[D.flag] = n
D.use = D.flag
if n.enabled == "1" then
L.node_order[#L.node_order + 1] = D.flag
end
end
end
end
local acl_num = 0
if uci_get("@global[0]", "acl_enable") == "1" then
uci_foreach("acl_rule", function(o)
if o.enabled == "1" then
acl_num = acl_num + 1
local a = {}
a.flag = o[".name"]
a.remarks = o.remarks
a.interface = o.interface
a.sources = o.sources
a.tcp_no_redir_ports = o.tcp_no_redir_ports
a.udp_no_redir_ports = o.udp_no_redir_ports
a.tcp_redir_ports = o.tcp_redir_ports
a.udp_redir_ports = o.udp_redir_ports
a.client_proxy = "1"
a.local_proxy = "0"
a.node = o.node
if o.mode == "0" then
a.tcp_no_redir_ports = "1:65535"
a.udp_no_redir_ports = "1:65535"
elseif o.mode == "2" then
local U = L.node[D.use]
if U.enabled == "0" then
U.enabled = "1"
D.local_proxy = "0"
D.client_proxy = "0"
end
a.use = D.use
end
if a.tcp_no_redir_ports == "1:65535" and a.udp_no_redir_ports == "1:65535" then
a.node = nil
end
if a.node then
local node = uci_get(a.node)
if node and node[".type"] == "nodes" then
local n = {
config = node,
enabled = "1",
log = o.log or "0",
loglevel = o.loglevel or "warn",
redir_port = api.get_new_port(),
dns_port = api.get_new_port(),
direct_dns_query_strategy = o.direct_dns_query_strategy,
remote_dns_protocol = o.remote_dns_protocol,
remote_dns_detour = o.remote_dns_detour,
remote_dns = o.remote_dns,
remote_dns_doh = o.remote_dns_doh,
remote_dns_client_ip = o.remote_dns_client_ip,
remote_dns_query_strategy = o.remote_dns_query_strategy,
remote_rewrite_ttl = o.remote_rewrite_ttl,
remote_fakedns = o.remote_fakedns,
}
L.node[a.flag] = n
a.use = a.flag
if n.enabled == "1" then
L.node_order[#L.node_order + 1] = a.flag
end
end
end
L.acl[#L.acl + 1] = a
end
end)
end
if next(D) then
L.acl[#L.acl + 1] = D
end
VAR["ENABLED_ACLS"] = acl_num > 0 and 1 or 0
return L
end
function acl_app(l)
if not l then l = L end
for i, v in ipairs(l.acl) do
local sid = v.flag
local acl_path = api.TMP_ACL_PATH .. "/" .. sid
sys.call("mkdir -p " .. acl_path)
local out = io.open(acl_path .. "/var", "a")
for k2, v2 in pairs(v) do
if type(v2) == "string" then
out:write(k2 .. "=" .. '"' .. v2 .. '"' .. "\n")
end
end
if v.use then
local node = l.node[v.use]
if node.config then
out:write("node" .. "=" .. '"' .. node.config[".name"] .. '"' .. "\n")
out:write("node_remarks" .. "=" .. '"' .. node.config.remarks .. '"' .. "\n")
end
out:write("redir_port" .. "=" .. '"' .. node.redir_port .. '"' .. "\n")
end
out:close()
local source_list = "any"
if v.sources and #v.sources > 0 then
local s1 = {}
for i, w in ipairs(v.sources) do
if api.iprange(w) then
s1[#s1 + 1] = "iprange:" .. w
elseif w:find("ipset:") == 1 then
s1[#s1 + 1] = "ipset:" .. w
else
local _ip_or_mac = api.ip_or_mac(w)
if _ip_or_mac == "ip" then
s1[#s1 + 1] = "ip:" .. w
elseif _ip_or_mac == "mac" then
s1[#s1 + 1] = "mac:" .. w
end
end
end
if #s1 > 0 then
source_list = table.concat(s1, "\n")
end
end
if #source_list > 0 then
local out = io.open(acl_path .. "/source_list", "a")
out:write(source_list)
out:close()
end
end
-- Gen shell param
for _, v1 in ipairs(l.node_order) do
local v = l.node[v1]
if v.enabled == "1" then
local flag = v1
local node = v.config
local run_args = {}
local config_path = api.TMP_ACL_PATH .. "/" .. flag
local config_file = config_path .. ".json"
local log_file = v.log == "0" and "/dev/null" or config_path .. ".log"
local dns_server, dns_port = api.parseDNS(v.remote_dns)
add_args(run_args, "flag", flag)
add_args(run_args, "node", node[".name"])
add_args(run_args, "redir_port", v.redir_port)
add_args(run_args, "socks_address", v.socks_listen)
add_args(run_args, "socks_port", v.socks_port)
add_args(run_args, "dns_listen_port", v.dns_port)
add_args(run_args, "direct_dns_query_strategy", v.direct_dns_query_strategy)
add_args(run_args, "remote_dns_protocol", v.remote_dns_protocol)
add_args(run_args, "remote_dns_tcp_server", dns_server)
add_args(run_args, "remote_dns_tcp_port", dns_port)
add_args(run_args, "remote_dns_udp_server", dns_server)
add_args(run_args, "remote_dns_udp_port", dns_port)
add_args(run_args, "remote_dns_doh", v.remote_dns_doh)
add_args(run_args, "remote_dns_client_ip", v.remote_dns_client_ip)
add_args(run_args, "remote_dns_detour", v.remote_dns_detour)
add_args(run_args, "remote_dns_query_strategy", v.remote_dns_query_strategy)
add_args(run_args, "remote_fakedns", v.remote_fakedns)
add_args(run_args, "remote_rewrite_ttl", v.remote_rewrite_ttl)
add_args(run_args, "config_file", config_file)
add_args(run_args, "loglevel", v.loglevel)
add_args(run_args, "log_file", log_file)
local out = io.open(api.TMP_ACL_PATH .. "/acl_node_" .. flag , "a")
out:write(table.concat(run_args, " "))
out:close()
end
end
for k, v in pairs(VAR) do
api.set_cache_var(k, v)
end
end
local acls = init_acl()
acl_app()
print(jsonc.stringify(acls, 1))
@@ -0,0 +1,13 @@
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.168.0.0/16
224.0.0.0/4
240.0.0.0/4
::/127
fc00::/7
fe80::/10
ff00::/8
@@ -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
@@ -0,0 +1,241 @@
#!/usr/bin/lua
local api = require ("luci.passwall2.api")
local appname = api.appname
local fs = api.fs
local uci = api.uci
local sys = api.sys
local log = api.log
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 var = api.get_args(arg)
local haproxy_path = var["-path"]
local haproxy_conf = var["-conf"]
local haproxy_dns = "127.0.0.1"
local cpu_thread = sys.exec('echo -n $(cat /proc/cpuinfo | grep "processor" | wc -l)') or "1"
local health_check_type = api.uci_get_c("@global_haproxy[0]", "health_check_type") or "tcp"
local health_check_inter = api.uci_get_c("@global_haproxy[0]", "health_check_inter") or "20"
local balancingStrategy = api.uci_get_c("@global_haproxy[0]", "balancingStrategy") or "roundrobin"
local console_port = api.uci_get_c("@global_haproxy[0]", "console_port")
local bind_local = api.uci_get_c("@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(0, "HAProxy: ")
log(1, api.i18n.translatef("Console Port: %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
spread-checks 50
tune.max-checks-per-thread 5
defaults
mode tcp
log global
option tcplog
option dontlognull
option abortonclose
#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 = {}
api.uci_foreach_c("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 = api.uci_get_c(lbss)
local hop = (health_check_type == "script_logic") and (server_node.hysteria_hop or server_node.hysteria2_hop) or nil
hop = hop and hop:gsub(":", "-") or nil
if server_node and server_node.address and (server_node.port or hop) then
server_remark = server_node.address .. ":" .. (server_node.port or hop)
server_address = server_node.address
server_port = server_node.port or hop
t.origin_address = server_address
t.origin_port = server_port
if health_check_type == "script_logic" then
if server_node.type ~= "Socks" then
local new_port
local cache = api.get_socks_port_by_cache(server_node[".name"])
if cache then
new_port = cache
else
new_port = api.get_new_port()
local config_file = string.format("%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
)
)
)
api.set_socks_port_to_cache(server_node[".name"], new_port)
end
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, api.i18n.translate("Discard one obviously invalid node."))
end
end
end)
local inter = tonumber((health_check_inter or "20"):match("^%d+"))
inter = (inter and inter >= 20) and inter or 20
local inter_arg = "inter " .. inter .. "s fastinter " .. inter / 2 .. "s downinter " .. inter * 1.5 .. "s"
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(1, api.i18n.translatef("Entrance %s:%s", bind_address, port))
f_out:write("\n" .. string.format([[
listen %s
bind %s:%s
mode tcp
balance %s
]], port, bind_address, port, balancingStrategy))
if health_check_type == "script_logic" then
f_out:write(string.format([[
option external-check
external-check command "/usr/share/%s/haproxy_check.sh"
]], appname))
end
local count_M, count_B = 1, 1
for i, o in ipairs(listens[port]) do
local remark = o.server_remark or ""
-- To prevent duplicate names from causing the program to fail to run.
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_arg}} rise 2 fall 2 {{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_arg}}", inter_arg)
server_conf = server_conf:gsub("{{backup}}", tostring(o.backup) == "1" and "backup" or "")
f_out:write(" " .. api.trim(server_conf) .. "\n")
if o.export ~= "0" then
sys.call(string.format(". /usr/share/%s/utils.sh ; add_ip2route %s %s", appname, o.origin_address, o.export))
end
log(2, string.format(api.i18n.translatef("Node: %s:%s, Weight: %s", o.origin_address, o.origin_port, o.lbweight)))
end
end
-- Console config
local console_user = api.uci_get_c("@global_haproxy[0]", "console_user")
local console_password = api.uci_get_c("@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()
-- Built-in health check URL
if health_check_type == "script_logic" then
local probeUrl = api.uci_get_c("@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
@@ -0,0 +1,37 @@
#!/bin/sh
export PATH=/usr/sbin:/usr/bin:/sbin:/bin:/root/bin
CONFIG=passwall2
listen_address=$1
listen_port=$2
server_address=$3
server_port=$4
busybox pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null && {
# No detection during specific task execution
exit 0
}
probe_file="/tmp/etc/${CONFIG}/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 1 --max-time 6 -w "%{http_code}" "${probeUrl}")
case "$status" in
200|204)
exit 0
;;
*)
exit 1
;;
esac
@@ -0,0 +1,374 @@
local api = require "luci.passwall2.api"
local c_config = api.c_config
local sys = api.sys
local jsonc = api.jsonc
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 = api.uci_get("dhcp", "@dnsmasq[0]", "server")
if DNSMASQ_DNS and #DNSMASQ_DNS > 0 then
api.uci_set_c("@global[0]", "dnsmasq_servers", DNSMASQ_DNS)
api.uci_save_c(true)
end
end
local function restore_servers()
local dns_table = {}
local DNSMASQ_DNS = api.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 = api.uci_get_c("@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
api.uci_del_c("@global[0]", "dnsmasq_servers")
api.uci_save_c(true)
end
if dns_table and #dns_table > 0 then
api.uci_set("dhcp", "@dnsmasq[0]", "server", dns_table)
api.uci_save(nil, "dhcp", true)
end
end
function stretch()
local dnsmasq_server = api.uci_get("dhcp", "@dnsmasq[0]", "server")
local dnsmasq_noresolv = api.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
api.uci_del("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
api.uci_set("dhcp", "@dnsmasq[0]", "resolvfile", RESOLVFILE)
api.uci_save(nil, "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(0, api.i18n.translate("Restart dnsmasq service."))
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 = api.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
api.uci_set("dhcp", "@dnsmasq[0]", "server", dns_table)
api.uci_save(nil, "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(0, api.i18n.translate("Restart dnsmasq service."))
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]}')")
local conf_file = "/var/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID
local retry = 5
while not fs.access(conf_file) and retry > 0 do
api.nixio.nanosleep(1, 0)
retry = retry - 1
end
if fs.access(conf_file) then
for line in io.lines(conf_file) do
local filter
if line:find("passwall2") 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
else
sys.call("logger -t passwall2 'ERROR: dnsmasq config " .. conf_file .. " not found after 5s wait! DNS hijacking will fail.'")
end
tinsert(conf_lines, "port=" .. LISTEN_PORT)
--dhcp.leases to hostsMore actions
local hosts = api.CACHE_PATH .. "/dhcp-hosts"
sys.call("touch " .. hosts)
tinsert(conf_lines, "addn-hosts=" .. hosts)
if TMP_DNSMASQ_PATH then
sys.call("rm -rf " .. TMP_DNSMASQ_PATH .. "/*passwall2*")
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 DEFAULT_DNS = var["DEFAULT_DNS"]
local LOCAL_DNS = var["LOCAL_DNS"]
local TUN_DNS = var["TUN_DNS"]
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 list1 = {}
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_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 cache_text = ""
local nodes_address_md5 = sys.exec("echo -n $(uci show %s | grep '\\.address') | md5sum" % c_config)
local new_text = TMP_DNSMASQ_PATH .. DNSMASQ_CONF_FILE .. DEFAULT_DNS .. LOCAL_DNS .. TUN_DNS .. nodes_address_md5 .. 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 = TUN_DNS
local setflag_4= (NFTFLAG == "1") and "4#inet#passwall2#" or ""
local setflag_6= (NFTFLAG == "1") and "6#inet#passwall2#" or ""
if not fs.access(CACHE_DNS_PATH) then
fs.mkdir(CACHE_DNS_PATH)
local fwd_dns
-- Always use domestic DNS to resolve node domain names
if true then
fwd_dns = LOCAL_DNS
api.uci_foreach_c("nodes", function(t)
local function process_address(address)
address = (address or ""):lower()
if address == "engage.cloudflareclient.com" then return end
if datatypes.hostname(address) then
set_domain_dns(address, fwd_dns)
set_domain_ipset(address, setflag_4 .. "psw2_vps," .. setflag_6 .. "psw2_vps6")
end
end
process_address(t.address)
process_address(t.download_address)
end)
end
if list1 and next(list1) then
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.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
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
api.remove(TMP_DNSMASQ_PATH)
fs.symlink(CACHE_DNS_PATH, TMP_DNSMASQ_PATH)
if DNSMASQ_CONF_FILE ~= "nil" then
local conf_lines = {}
tinsert(conf_lines, string.format("conf-dir=%s", TMP_DNSMASQ_PATH))
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 FLAG == "acl_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
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
local var = nil
if arg[2] then
var = jsonc.parse(arg[2])
end
func(var or {})
end
end
@@ -0,0 +1,6 @@
if #arg > 0 then
local api = require "luci.passwall2.api"
local str = arg[1]
table.remove(arg, 1)
print(api.i18n.translatef(str, unpack(arg)))
end
File diff suppressed because it is too large. Load diff
+50
View File
@@ -0,0 +1,50 @@
#!/bin/sh
# dhcp.leases to hosts
. /usr/share/passwall2/utils.sh
LOCK_FILE=${LOCK_PATH}/${CONFIG}_lease2hosts.lock
LEASE_FILE=$(uci -q get dhcp.@dnsmasq[0].leasefile || echo "/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
+32
View File
@@ -0,0 +1,32 @@
#!/bin/sh
. /usr/share/passwall2/utils.sh
LOCK_FILE=${LOCK_PATH}/${CONFIG}_monitor.lock
ENABLED=$(config_n_get @global[0] enabled 0)
[ "$ENABLED" != 1 ] && return 1
ENABLED=$(config_n_get @global_delay[0] start_daemon 0)
[ "$ENABLED" != 1 ] && return 1
sleep 58s
while [ "$ENABLED" -eq 1 ]; do
[ -f "$LOCK_FILE" ] && {
sleep 6s
continue
}
touch $LOCK_FILE
[ -d ${TMP_SCRIPT_FUNC_PATH} ] && {
for filename in $(ls ${TMP_SCRIPT_FUNC_PATH} | grep -v "^_"); do
cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename})
cmd_check=$(echo $cmd | awk -F '>' '{print $1}')
escape_cmd="$(echo $cmd_check | sed 's/[^a-zA-Z0-9]/\\&/g')"
icount=$(busybox pgrep -f "${escape_cmd}" | wc -l)
if [ $icount = 0 ]; then
log 0 "${cmd} crashed, restarting."
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
fi
done
}
rm -f $LOCK_FILE
sleep 58s
done
File diff suppressed because it is too large. Load diff
+253
View File
@@ -0,0 +1,253 @@
#!/usr/bin/lua
local api = require "luci.passwall2.api"
local appname = api.appname
local fs = api.fs
local log = api.log
local sys = api.sys
local uci, uci_get, uci_set, uci_del, uci_foreach, uci_save = api.uci, api.uci_get_c, api.uci_set_c, api.uci_del_c, api.uci_foreach_c, api.uci_save_c
local arg1 = arg[1]
local arg2 = arg[2]
local arg3 = arg[3]
local reboot = 0
local geoip_update = "0"
local geosite_update = "0"
local geoip_url = uci_get("@global_rules[0]", "geoip_url") or "https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat"
local geosite_url = uci_get("@global_rules[0]", "geosite_url") or "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
local asset_location = uci_get("@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/"
asset_location = asset_location:match("/$") and asset_location or (asset_location .. "/")
local backup_path = "/tmp/bak_v2ray/"
if arg3 == "cron" then
arg2 = nil
end
-- curl
local function curl(url, file)
local http_code = 0
local header_str = ""
local args = {
"-skL",
"--retry 3",
"--connect-timeout 3",
"--max-time 300",
"--speed-limit 51200 --speed-time 15",
"-H 'Accept: */*'",
'-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"',
"--dump-header -",
"-w '\\n%{http_code}'"
}
if file then
args[#args + 1] = "-o " .. file
end
local return_code, result = api.curl_auto(url, nil, args)
if result and result ~= "" then
local body, code = result:match("^(.-)%s*([0-9]+)$")
if code then
http_code = tonumber(code) or 0
header_str = body
else
http_code = tonumber(result:match("(%d+)%s*$")) or 0
end
end
if header_str ~= "" then
header_str = header_str:gsub("\r", "")
end
return http_code, header_str
end
local function non_file_check(file_path, header_content)
local remote_file_size = nil
local local_file_size = tonumber(fs.stat(file_path, "size") or 0)
if local_file_size == 0 then
log(2, api.i18n.translate("Downloaded file is empty or an error occurred while reading it."))
return true
end
if header_content and header_content ~= "" then
for size in header_content:gmatch("[Cc]ontent%-[Ll]ength:%s*(%d+)") do
local s = tonumber(size)
if s and s > 0 then
remote_file_size = s
end
end
end
if remote_file_size and remote_file_size ~= local_file_size then
log(2, api.i18n.translatef("Download file size verification error. Original file size: %sB. Downloaded file size: %sB.", remote_file_size, local_file_size))
return true
end
return false
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:gsub("(%W)", "%%%1")), down_filename .. ".sha256sum")
local sha_path = tmp_path .. ".sha256sum"
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("(%x+)%s+.+", "%1 " .. 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(1, api.i18n.translatef("%s version is the same and does not need to be updated.", geo_type))
return 0
end
end
end
local sret_tmp, header = curl(url, tmp_path)
if sret_tmp == 200 and non_file_check(tmp_path, header) then
log(1, api.i18n.translatef("%s an error occurred during the file download process. Please try downloading again.", geo_type))
os.remove(tmp_path)
sret_tmp, header = curl(url, tmp_path)
if sret_tmp == 200 and non_file_check(tmp_path, header) then
sret_tmp = 0
log(1, api.i18n.translatef("%s an error occurred while downloading the file. Please check your network or the download link and try again!", 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 && mv -f %s %s", backup_path, asset_path, backup_path))
sys.call(string.format("mkdir -p %s && mv -f %s %s", asset_location, tmp_path, asset_path))
reboot = 1
log(1, api.i18n.translatef("%s update success.", geo_type))
else
log(1, api.i18n.translatef("%s update failed, please try again later or change URL.", geo_type))
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(1, api.i18n.translatef("%s version is the same and does not need to be updated.", geo_type))
return 0
end
sys.call(string.format("mkdir -p %s && mv -f %s %s", backup_path, asset_path, backup_path))
sys.call(string.format("mkdir -p %s && mv -f %s %s", asset_location, tmp_path, asset_path))
reboot = 1
log(1, api.i18n.translatef("%s update success.", geo_type))
end
else
log(1, api.i18n.translatef("%s update failed, please try again later or change URL.", geo_type))
return 1
end
return 0
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
local function remove_tmp_geofile(name)
os.remove("/tmp/" .. name .. ".dat")
os.remove("/tmp/" .. name .. ".dat.sha256sum")
end
if arg2 then
string.gsub(arg2, '[^' .. "," .. ']+', function(w)
if w == "geoip" then
geoip_update = "1"
end
if w == "geosite" then
geosite_update = "1"
end
end)
else
geoip_update = uci_get("@global_rules[0]", "geoip_update") or "1"
geosite_update = uci_get("@global_rules[0]", "geosite_update") or "1"
end
if geoip_update == "0" and geosite_update == "0" then
os.exit(0)
end
local function check_instance(action)
local rule_lock = "/var/lock/" .. appname .. "_rule_update.lock"
local sub_lock = "/var/lock/" .. appname .. "_subscribe.lock"
if action == "start" then
math.randomseed(os.time() + math.floor(os.clock() * 1000))
api.nixio.nanosleep(0, math.random(100, 1000) * 1000000)
if fs.access(rule_lock) then
log(0, api.i18n.translatef("[Rule update] instance is running; please try again later.") .. "\n")
os.exit(0)
else
luci.sys.call("touch " .. rule_lock)
end
elseif action == "end" then
luci.sys.call("rm -f " .. rule_lock)
return
end
if fs.access(sub_lock) then
log(0, api.i18n.translatef("[Subscription] instance is running; [Rule Update] queue and wait.") .. "\n")
end
while fs.access(sub_lock) do
api.nixio.nanosleep(2, 0)
end
end
check_instance("start")
log(0, api.i18n.translate("Start updating the rules..."))
local function safe_call(func, err_msg)
xpcall(func, function(e)
log(1, e)
log(1, debug.traceback())
log(1, err_msg)
end)
end
if geoip_update == "1" then
log(1, api.i18n.translatef("%s Start updating...", "geoip"))
safe_call(fetch_geoip, api.i18n.translatef("%s update error!", "geoip"))
remove_tmp_geofile("geoip")
end
if geosite_update == "1" then
log(1, api.i18n.translatef("%s Start updating...", "geosite"))
safe_call(fetch_geosite, api.i18n.translatef("%s update error!", "geosite"))
remove_tmp_geofile("geosite")
end
uci_set("@global_rules[0]", "geoip_update", geoip_update)
uci_set("@global_rules[0]", "geosite_update", geosite_update)
uci_save(true)
if reboot == 1 then
if arg3 == "cron" then
if not fs.access(api.LOCK_PREFIX .. ".lock") then
sys.call("touch %s_cron.lock" % api.LOCK_PREFIX)
end
end
log(1, api.i18n.translate("Restart the service and apply the new rules."))
uci_set("@global[0]", "flush_set", "1")
uci_save(true, true)
end
log(0, api.i18n.translate("The rules have been updated..."))
check_instance("end")
@@ -0,0 +1,224 @@
#!/bin/sh
. /usr/share/passwall2/utils.sh
APP_FILE=${APP_PATH}/app.sh
flag=0
check_process() {
while busybox pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null; do
sleep 6s
done
}
test_url() {
local url="$1"
local try="${2:-1}"
local timeout="${3:-2}"
local extra_params="$4"
local repeat="$5"
if [ -z "$curl_retry_all_errors" ]; then
if /usr/bin/curl --help all | grep -q "\-\-retry-all-errors"; then
curl_retry_all_errors=1
fi
fi
[ "$curl_retry_all_errors" = "1" ] && extra_params="--retry-all-errors ${extra_params}"
local max_time=$((timeout * (try + 1) + try + 3))
curl_test() {
/usr/bin/curl -skIL -o /dev/null ${extra_params} --max-time ${max_time} --connect-timeout ${timeout} --retry ${try} --retry-delay 1 -w "%{http_code}" "$url"
}
local status=$(curl_test)
[ "$status" = "204" ] && status=200
if [ "$status" = "200" ] && [ "$repeat" = "1" ]; then
sleep 3s
status=$(curl_test)
[ "$status" = "204" ] && status=200
fi
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}" ] && {
check_process
local _tmp_port=$(get_new_port 48800 tcp,udp)
NO_REC_PROCESS=1 $APP_FILE 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
sleep 2s
local curlx="socks5h://127.0.0.1:${_tmp_port}"
local _proxy_status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x $curlx" 1)
# Kill the SS plugin process
local pid_file="${TMP_PATH}/test_node_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
busybox pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf ${TMP_PATH}/test_node_${node_id}*.*
if [ "${_proxy_status}" -eq 200 ]; then
return 0
fi
}
return 1
}
try_switch_backup() {
local b_nodes="$1"
local now_node="$2"
local total tried
local new_node msg
local first_node found node
# Only one backup node, alternate with the primary node.
if [ "$backup_node_num" -eq 1 ]; then
b_nodes="$b_nodes $main_node"
fi
total=$(printf "%s\n" "$b_nodes" | wc -w)
tried=0
while [ "$tried" -lt "$total" ]; do
new_node=""
first_node=""
found=""
for node in $b_nodes; do
[ -z "$first_node" ] && first_node="$node" # Record first node.
[ "$found" = "1" ] && { new_node="$node"; break; } # Find the current node and then get the next one.
[ "$node" = "$now_node" ] && found=1 # flag the found current node.
done
# If the current node is not found, or if it is the last node, select the first node.
[ -z "$new_node" ] && new_node="$first_node"
local node_role node_type node_remarks
if [ "$new_node" = "$main_node" ]; then
node_role="$(i18n "main node")"
else
node_role="$(i18n "next backup node")"
fi
node_type=$(config_n_get $new_node type)
node_remarks=$(config_n_get $new_node remarks)
log_i18n 0 "Socks switch detection: Port [%s] try %s [%s:%s]." "${socks_port}" "${node_role}" "${node_type}" "${node_remarks}"
if test_node ${new_node}; then
check_process
log_i18n 0 "Socks switch detection: Port [%s] [%s:[%s]] normal, switch to this node!" "${socks_port}" "${node_type}" "${node_remarks}"
NO_REC_PROCESS=1 $APP_FILE socks_node_switch flag=${id} new_node=${new_node}
[ $? -eq 0 ] && {
log_i18n 0 "Socks switch detection: Port [%s] node switch complete!" "${socks_port}"
}
return 0
fi
log_i18n 0 "Socks switch detection: Port [%s] [%s:[%s]] abnormal." "${socks_port}" "${node_type}" "${node_remarks}"
now_node="$new_node"
tried=$((tried + 1))
done
log_i18n 0 "Socks switch detection: Port [%s] all nodes are unavailable!" "${socks_port}"
return 1
}
test_auto_switch() {
flag=$((flag + 1))
local b_nodes=$1
local now_node=$2
[ -z "$now_node" ] && {
if [ -n "$(get_cache_var "${id}")" ]; then
now_node=$(get_cache_var "${id}")
else
#log_i18n 0 "Socks switch detection: Port [%s] Unknown error." "${socks_port}"
return 1
fi
}
[ $flag -le 1 ] && main_node=$now_node
local status=$(test_proxy)
if [ "$status" = "2" ]; then
log_i18n 0 "Socks switch detection: Port [%s] Unable to connect to the network. Please check if the network is working properly!" "${socks_port}"
return 2
fi
# Check if the main node is usable
if [ "$restore_switch" = "1" ] && [ -n "$main_node" ] && [ "$now_node" != "$main_node" ]; then
test_node ${main_node}
[ $? -eq 0 ] && {
check_process
# The main node is working properly; switch to the main node.
log_i18n 0 "Socks switch detection: Port [%s] Primary node [%s: [%s]] is normal. Switch to the primary node!" "${socks_port}" "$(config_n_get $main_node type)" "$(config_n_get $main_node remarks)"
NO_REC_PROCESS=1 $APP_FILE socks_node_switch flag=${id} new_node=${main_node}
[ $? -eq 0 ] && {
log_i18n 0 "Socks switch detection: Port [%s] node switch complete!" "${socks_port}"
}
return 0
}
fi
[ "$status" = "0" ] && {
#log_i18n 0 "Socks switch detection: Port [%s] [%s:[%s]] normal." "${socks_port}" "$(config_n_get $now_node type)" "$(config_n_get $now_node remarks)"
return 0
}
log_i18n 0 "Socks switch detection: Port [%s] [%s:[%s]] abnormal." "${socks_port}" "$(config_n_get $now_node type)" "$(config_n_get $now_node remarks)"
try_switch_backup "$b_nodes" "$now_node"
return $?
}
start() {
id=$1
LOCK_FILE=${LOCK_PATH}/${CONFIG}_socks_auto_switch_${id}.lock
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=$(lua_api "get_socks_backup_nodes(\"${id}\")")
if [ -n "$backup_node" ]; then
backup_node_num=$(printf "%s\n" "$backup_node" | wc -w)
log_i18n 0 "Socks switch detection: Port [%s] Number of backup nodes: %s" "${socks_port}" "${backup_node_num}"
if [ "$backup_node_num" -eq 1 ]; then
[ "$main_node" = "$backup_node" ] && return
elif [ "$backup_node_num" -gt 1 ]; then
[ "$restore_switch" != "1" ] && {
[ -z "$(echo $backup_node | grep -F "$main_node")" ] && backup_node="${backup_node} ${main_node}"
}
fi
else
log_i18n 0 "Socks switch detection: Port [%s] Number of backup nodes: %s" "${socks_port}" "0"
return
fi
while [ -n "$backup_node" ]; do
[ -f "$LOCK_FILE" ] && {
sleep 6s
continue
}
check_process
touch $LOCK_FILE
test_auto_switch "$backup_node"
rm -f $LOCK_FILE
sleep ${delay}
done
}
start $@
File diff suppressed because it is too large. Load diff
+69
View File
@@ -0,0 +1,69 @@
#!/bin/sh
## Loop update script
. /usr/share/passwall2/utils.sh
LOCK_FILE=${LOCK_PATH}/${CONFIG}_tasks.lock
CFG_UPDATE_INT=0
exec 99>"$LOCK_FILE"
flock -n 99
if [ "$?" != 0 ]; then
exit 0
fi
while true
do
if [ "$CFG_UPDATE_INT" -ne 0 ]; then
restart_week_mode=$(config_n_get @global_delay[0] restart_week_mode)
restart_interval_mode=$(config_n_get @global_delay[0] 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
rules_update_week_mode=$(config_n_get @global_rules[0] update_week_mode)
rules_update_interval_mode=$(config_n_get @global_rules[0] update_interval_mode)
rules_update_interval_mode=$(expr "$rules_update_interval_mode" \* 60)
if [ -n "$rules_update_week_mode" ]; then
[ "$rules_update_week_mode" = "8" ] && {
[ "$(expr "$CFG_UPDATE_INT" % "$rules_update_interval_mode")" -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
sub_update_week_mode=$(config_n_get $item update_week_mode)
if [ -n "$sub_update_week_mode" ] && [ "$sub_update_week_mode" = "8" ]; then
sub_update_interval_mode=$(config_n_get $item update_interval_mode)
[ -n "$sub_update_interval_mode" ] && {
echo "$item" >> $TMP_SUB_PATH/${sub_update_week_mode}_${sub_update_interval_mode}
}
fi
done
[ -d "${TMP_SUB_PATH}" ] && {
for name in $(ls ${TMP_SUB_PATH}); do
sub_update_week_mode=$(echo $name | awk -F '_' '{print $1}')
sub_update_interval_mode=$(echo $name | awk -F '_' '{print $2}')
sub_update_interval_mode=$(expr "$sub_update_interval_mode" \* 60)
cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g')
[ "$sub_update_week_mode" = "8" ] && {
[ "$(expr "$CFG_UPDATE_INT" % "$sub_update_interval_mode")" -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
+73
View File
@@ -0,0 +1,73 @@
#!/bin/sh
. /usr/share/passwall2/utils.sh
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}" ] && {
local _tmp_port=$(get_new_port 48900 tcp,udp)
NO_REC_PROCESS=1 /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}"
sleep 2s
local probeUrl=$(config_n_get @global_other[0] url_test_url https://www.google.com/generate_204)
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}")
# End the SS plugin process
local pid_file="${TMP_PATH}/url_test_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
busybox pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf ${TMP_PATH}/*url_test_${node_id}*.*
}
echo $result
}
arg1=$1
shift
case $arg1 in
test_url)
test_url $@
;;
url_test_node)
url_test_node $@
;;
esac
+481
View File
@@ -0,0 +1,481 @@
#!/bin/sh
# Copyright (C) 2022-2025 xiaorouji
# Copyright (C) 2026 Openwrt-Passwall Organization
CONFIG=passwall2
APP_PATH=/usr/share/${CONFIG}
TMP_PATH=/tmp/etc/${CONFIG}
TMP_PATH2=${TMP_PATH}_tmp
LOCK_PATH=/var/lock
LOG_FILE=/tmp/log/${CONFIG}.log
TMP_ACL_PATH=${TMP_PATH}/acl
TMP_BIN_PATH=${TMP_PATH}/bin
TMP_IFACE_PATH=${TMP_PATH}/iface
TMP_ROUTE_PATH=${TMP_PATH}/route
TMP_SCRIPT_FUNC_PATH=${TMP_PATH}/script_func
TMP_PROCESS_LIST_PATH=${TMP_PATH}/process_list
. /lib/functions/network.sh
config_get_type() {
local ret=$(uci -q get "${CONFIG}.${1}" 2>/dev/null)
echo "${ret:=$2}"
}
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}}"
}
eval_set_val() {
for i in $@; do
for j in $i; do
eval $j
done
done
}
eval_unset_val() {
for i in $@; do
for j in $i; do
eval unset $j
done
done
}
eval_cache_var() {
[ -s "$TMP_PATH/var" ] && eval $(cat "$TMP_PATH/var")
}
get_cache_var() {
local key="${1}"
[ -n "${key}" ] && [ -s "$TMP_PATH/var" ] && {
echo $(cat $TMP_PATH/var | grep "^${key}=" | awk -F '=' '{print $2}' | tail -n 1 | awk -F'"' '{print $2}')
}
}
del_cache_var() {
local key="${1}"
[ -n "${key}" ] && [ -f "${TMP_PATH}/var" ] && {
sed -i "/${key}=/d" $TMP_PATH/var >/dev/null 2>&1
}
}
set_cache_var() {
local key="${1}"
shift 1
[ -n "${key}" ] && {
del_cache_var ${key}
local val="$@"
[ -n "${val}" ] && {
[ ! -d $TMP_PATH ] && mkdir -p $TMP_PATH
echo "${key}=\"${val}\"" >> $TMP_PATH/var
eval ${key}=\"${val}\"
}
}
}
echolog() {
echo -e "$*" >>$LOG_FILE
}
echolog_date() {
local d="$(date "+%Y-%m-%d %H:%M:%S")"
echolog "$d: $*"
}
i18n() {
echo "$(lua ${APP_PATH}/i18n.lua "$@")"
}
log() {
local num="$1"
shift
local content="$@"
local indent=""
if [ "$num" -ge 1 ]; then
for i in $(seq 1 ${num}); do
indent="${indent} "
done
echolog_date "${indent}- ${content}"
else
echolog_date "${content}"
fi
}
log_i18n() {
local num="$1"
shift
log ${num} "$(i18n "$@")"
}
clean_log() {
logsnum=$(cat $LOG_FILE 2>/dev/null | wc -l)
[ "$logsnum" -gt 1000 ] && {
echo "" > $LOG_FILE
log_i18n 0 "Log file is too long, clear it!"
}
}
lua_api() {
local func=${1}
[ -z "${func}" ] && {
echo ""
return
}
echo $(lua -e "local api = require 'luci.passwall2.api' print(api.${func})")
}
check_host() {
local f=${1}
a=$(echo $f | grep "\/")
[ -n "$a" ] && return 1
# Determine if it contains Chinese characters.
local tmp=$(echo -n $f | awk '{print gensub(/[!-~]/,"","g",$0)}')
[ -n "$tmp" ] && return 1
return 0
}
first_type() {
[ "${1#/}" != "$1" ] && [ -x "$1" ] && echo "$1" && return
for p in "/bin/$1" "/usr/bin/$1" "${TMP_BIN_PATH:-/tmp}/$1"; do
[ -x "$p" ] && echo "$p" && return
done
command -v "$1" 2>/dev/null || command -v "$2" 2>/dev/null
}
get_enabled_anonymous_secs() {
uci -q show "${CONFIG}" | grep "${1}\[.*\.enabled='1'" | cut -d '.' -sf2
}
get_geoip() {
local geo_output_path="$TMP_PATH2/geo_output"
mkdir -p ${geo_output_path}
local geoip_code="$1"
local geoip_type_flag=""
local output_path="${geo_output_path}/geoip-${geoip_code}-$2"
[ ! -s "${output_path}" ] && {
local geoip_path="$(config_n_get @global_rules[0] v2ray_location_asset)"
geoip_path="${geoip_path%*/}/geoip.dat"
local bin="$(first_type $(config_n_get @global_app[0] geoview_file) geoview)"
[ -n "$bin" ] && [ -s "$geoip_path" ] || { echo ""; return; }
case "$2" in
"ipv4") geoip_type_flag="-ipv6=false" ;;
"ipv6") geoip_type_flag="-ipv4=false" ;;
esac
"$bin" -input "$geoip_path" -list "$geoip_code" $geoip_type_flag -lowmem=true -output ${output_path}
}
[ -s "${output_path}" ] && cat "${output_path}"
}
get_host_ip() {
local host=$2
local count=$3
[ -z "$count" ] && count=3
local isip=""
local ip=""
if [ "$1" == "ipv6" ]; then
isip=$(echo $host | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}")
if [ -n "$isip" ]; then
ip=$(echo "$host" | tr -d '[]')
fi
else
isip=$(echo $host | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}")
[ -n "$isip" ] && ip=$isip
fi
[ -z "$isip" ] && {
local t=4
[ "$1" == "ipv6" ] && t=6
local vpsrip=$(resolveip -$t -t $count $host | awk 'NR==1{print}')
ip=$vpsrip
}
[ -n "$ip" ] && echo "$ip"
}
get_node_host_ip() {
local ip
local address=$(config_n_get $1 address)
[ -n "$address" ] && {
local use_ipv6=$(config_n_get $1 use_ipv6)
local network_type="ipv4"
[ "$use_ipv6" == "1" ] && network_type="ipv6"
ip=$(get_host_ip $network_type $address)
}
echo $ip
}
get_ip_port_from() {
local __host=${1}; shift 1
local __ipv=${1}; shift 1
local __portv=${1}; shift 1
local __ucipriority=${1}; shift 1
local val1 val2
if [ -n "${__ucipriority}" ]; then
val2=$(config_n_get ${__host} port $(echo $__host | sed -n 's/^.*[:#]\([0-9]*\)$/\1/p'))
val1=$(config_n_get ${__host} address "${__host%%${val2:+[:#]${val2}*}}")
else
val2=$(echo $__host | sed -n 's/^.*[:#]\([0-9]*\)$/\1/p')
val1="${__host%%${val2:+[:#]${val2}*}}"
fi
eval "${__ipv}=\"$val1\"; ${__portv}=\"$val2\""
}
host_from_url(){
local f=${1}
## Remove protocol part of url ##
f="${f##http://}"
f="${f##https://}"
f="${f##ftp://}"
f="${f##sftp://}"
## Remove username and/or username:password part of URL ##
f="${f##*:*@}"
f="${f##*@}"
## Remove rest of urls ##
f="${f%%/*}"
echo "${f%%:*}"
}
hosts_foreach() {
local __hosts
eval "__hosts=\$${1}"; shift 1
local __func=${1}; shift 1
local __default_port=${1}; shift 1
local __ret=1
[ -z "${__hosts}" ] && return 0
local __ip __port
for __host in $(echo $__hosts | sed 's/[ ,]/\n/g'); do
get_ip_port_from "$__host" "__ip" "__port"
eval "$__func \"${__host}\" \"\${__ip}\" \"\${__port:-${__default_port}}\" \"$@\""
__ret=$?
[ ${__ret} -ge ${ERROR_NO_CATCH:-1} ] && return ${__ret}
done
}
check_port_exists() {
local port=$1
local protocol=$2
[ -n "$protocol" ] || protocol="tcp,udp"
local result=
if [ "$protocol" = "tcp" ]; then
result=$(netstat -tln | grep -c ":$port ")
elif [ "$protocol" = "udp" ]; then
result=$(netstat -uln | grep -c ":$port ")
elif [ "$protocol" = "tcp,udp" ]; then
result=$(netstat -tuln | grep -c ":$port ")
fi
echo "${result}"
}
get_new_port() {
local default_start_port=2001
local min_port=1025
local max_port=49151
local port=$1 #Required parameter; please pass "auto" if you want it to be automatic.
local last_get_new_port_auto
if [ "$1" == "auto" ]; then
last_get_new_port_auto=$(get_cache_var "last_get_new_port_auto")
if [ -n "$last_get_new_port_auto" ]; then
port=$last_get_new_port_auto
port=$(expr $port + 1)
else
port=$default_start_port
fi
fi
[ "$port" -lt $min_port -o "$port" -gt $max_port ] && port=$default_start_port
local protocol=$(echo $2 | tr 'A-Z' 'a-z')
local result=$(check_port_exists $port $protocol)
[ -n "$(get_cache_var "get_port_${port}")" ] && {
# exist, continue get.
# Make the following result logic true.
result=1
}
if [ "$result" != 0 ]; then
local temp=
if [ "$port" -lt $max_port ]; then
# If the port is smaller than the maximum port, then increment by 1 and continue.
temp=$(expr $port + 1)
elif [ "$port" -gt $min_port ]; then
# If the port is greater than the minimum port, then decrement by 1 and continue.
temp=$(expr $port - 1)
else
# Otherwise, reassign the default starting port.
temp=$default_start_port
fi
# Recursion, until it obtains an unused port.
get_new_port $temp $protocol
else
# Set cache, mark this port as already obtained, so it should not be obtained again.
set_cache_var "get_port_${port}" "1"
[ "$1" == "auto" ] && set_cache_var "last_get_new_port_auto" "$port"
echo $port
fi
}
has_1_65535() {
local val="$1"
val=${val//:/-}
case ",$val," in
*,1-65535,*) return 0 ;;
*) return 1 ;;
esac
}
add_ip2route() {
local ip=$(get_host_ip "ipv4" $1)
[ -z "$ip" ] && {
log 1 "$(i18n "Unable to resolve [%s], route table addition failed!" "${1}")"
return 1
}
local remarks="${1}"
[ "$remarks" != "$ip" ] && remarks="${1}(${ip})"
local gateway device
network_get_gateway gateway "$2"
network_get_device device "$2"
[ -z "${device}" ] && device=$(ubus call "network.interface.$2" status 2>/dev/null | jsonfilter -e '@.device' 2>/dev/null)
[ -z "${device}" ] && [ -d "/sys/class/net/$2" ] && device="$2"
[ -z "${device}" ] && device="$2"
if [ -n "${gateway}" ]; then
route add -host ${ip} gw ${gateway} dev ${device} >/dev/null 2>&1
echo "$ip" >> $TMP_ROUTE_PATH/${device}
log 1 "$(i18n "[%s] was successfully added to the routing table of interface [%s]!" "${remarks}" "${device}")"
else
log 1 "$(i18n "Adding [%s] to the [%s] routing table failed! The reason is that the [%s] gateway cannot be found." "${remarks}" "${device}" "${device}")"
fi
}
delete_ip2route() {
[ -d "${TMP_ROUTE_PATH}" ] && {
for interface in $(ls ${TMP_ROUTE_PATH}); do
for ip in $(cat ${TMP_ROUTE_PATH}/${interface}); do
route del -host ${ip} dev ${interface} >/dev/null 2>&1
done
done
}
}
ln_run() {
local queue_run=${1}
local file_func=${2}
local ln_name=${3}
local output=${4}
shift 4;
if [ "${file_func%%/*}" != "${file_func}" ]; then
[ ! -L "${file_func}" ] && {
ln -s "${file_func}" "${TMP_BIN_PATH}/${ln_name}" >/dev/null 2>&1
file_func="${TMP_BIN_PATH}/${ln_name}"
}
[ -x "${file_func}" ] || log 1 "$(i18n "%s does not have execute permissions and cannot be started: %s %s" "$(readlink ${file_func})" "${file_func}" "$*")"
fi
#echo "${file_func} $*" >&2
[ -n "${file_func}" ] || {
log 1 "$(i18n "%s not found, unable to start..." "${ln_name}")"
return 1
}
[ "${queue_run}" == "1" ] && {
mkdir -p $TMP_PROCESS_LIST_PATH
process_count=$(ls $TMP_PROCESS_LIST_PATH | grep -v "^_" | wc -l)
process_count=$((process_count + 1))
echo "${file_func:-log 1 "${ln_name}"} $@ >${output}" > $TMP_PROCESS_LIST_PATH/$process_count
return
}
${file_func:-log 1 "${ln_name}"} "$@" >${output} 2>&1 &
[ -n "$NO_REC_PROCESS" ] && return
process_count=$(ls $TMP_SCRIPT_FUNC_PATH | grep -v "^_" | wc -l)
process_count=$((process_count + 1))
echo "${file_func:-log 1 "${ln_name}"} $@ >${output}" > $TMP_SCRIPT_FUNC_PATH/$process_count
}
run_process_queue() {
[ -d ${TMP_PROCESS_LIST_PATH} ] && {
mkdir -p ${TMP_SCRIPT_FUNC_PATH}
for filename in $(ls ${TMP_PROCESS_LIST_PATH}); do
cmd=$(cat ${TMP_PROCESS_LIST_PATH}/${filename})
cmd_check=$(echo $cmd | awk -F '>' '{print $1}')
icount=$(busybox pgrep -f "$(echo $cmd_check)" | wc -l)
if [ $icount = 0 ]; then
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
fi
mv -f ${TMP_PROCESS_LIST_PATH}/${filename} ${TMP_SCRIPT_FUNC_PATH}/queued_${filename}
done
}
rm -rf ${TMP_PROCESS_LIST_PATH}
}
kill_all() {
kill -9 $(pidof "$@") >/dev/null 2>&1
}
get_wan_ips() {
local family="$1"
local NET_ADDR
local iface
local INTERFACES=$(ubus call network.interface dump | jsonfilter -e \
'@.interface[!(@.interface ~ /lan/) && !(@.l3_device ~ /\./) && @.route[0]].interface')
for iface in $INTERFACES; do
local addr
if [ "$family" = "ip6" ]; then
network_get_ipaddr6 addr "$iface"
case "$addr" in
""|fe80*) continue ;;
esac
else
network_get_ipaddr addr "$iface"
case "$addr" in
""|"0.0.0.0") continue ;;
esac
fi
case " $NET_ADDR " in
*" $addr "*) ;;
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$addr" ;;
esac
done
echo "$NET_ADDR"
}
get_local_ips() {
local family="$1"
local ALL_IPS WAN_IPS ip NET_ADDR
if [ "$family" = "ip6" ]; then
ALL_IPS=$(ip -o -6 addr show scope global | awk '{print $4}' | cut -d/ -f1)
WAN_IPS=$(get_wan_ips ip6)
else
ALL_IPS=$(ip -o -4 addr show scope global | awk '{print $4}' | cut -d/ -f1)
WAN_IPS=$(get_wan_ips ip4)
fi
# Supplementary loop (not included in scope global)
[ "$family" = "ip6" ] && ALL_IPS="$ALL_IPS ::1"
[ "$family" != "ip6" ] && ALL_IPS="$ALL_IPS 127.0.0.1"
for ip in $ALL_IPS; do
case "$ip" in
""|0.0.0.0|::) continue ;;
esac
case " $WAN_IPS " in
*" $ip "*) continue ;;
esac
case " $NET_ADDR " in
*" $ip "*) ;;
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$ip" ;;
esac
done
for ip in $NET_ADDR; do
echo "$ip"
done
}
@@ -0,0 +1,11 @@
{
"luci-app-passwall2": {
"description": "Grant UCI access for luci-app-passwall2",
"read": {
"uci": [ "passwall2", "passwall2_server" ]
},
"write": {
"uci": [ "passwall2", "passwall2_server" ]
}
}
}
@@ -0,0 +1,4 @@
{
"config": "passwall2_server",
"init": "passwall2_server"
}
@@ -0,0 +1,4 @@
{
"config": "passwall2",
"init": "passwall2"
}