update 2026-05-18 23:19:08

This commit is contained in:
action 2026-05-18 23:19:09 +08:00
parent e6696f2057
commit 53eda83da8
35 changed files with 620 additions and 281 deletions

View File

@ -262,6 +262,13 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = "2"
o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.")
o = s:option(Value, _n("tolerance"), translate("Failure Tolerance (%)"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" })
o.datatype = "uinteger"
o.default = "10"
o.placeholder = "10"
o.description = translate("The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate.")
end -- [[ 负载均衡 End ]]
if load_iface_options then -- [[ 自定义接口 Start ]]

View File

@ -1072,7 +1072,13 @@ function gen_config(var)
type = _node.balancingStrategy,
settings = {
expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2,
maxRTT = "1s"
maxRTT = "1s",
tolerance = (function(t)
t = tonumber(t) or 0
if t < 1 then return nil end
if t > 100 then t = 100 end
return t / 100
end)(_node.tolerance)
}
}
else

View File

@ -535,6 +535,12 @@ msgstr "优选节点数量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。"
msgid "Failure Tolerance (%)"
msgstr "测速失败率容忍度(%"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "最多可接受的测速失败率,例如 1 表示允许 1% 的测速失败率。"
msgid "Shunt"
msgstr "分流"

View File

@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.5.1
PKG_VERSION:=26.5.18
PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION)

View File

@ -52,7 +52,7 @@ function index()
entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true
entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true
entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true
entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Watch Logs"), 999).leaf = true
entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Runtime Logs"), 999).leaf = true
--[[ Server ]]
entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true
@ -452,13 +452,17 @@ function delete_select_nodes()
if t["node"] == w then
uci:delete(appname, t[".name"])
end
local changed = false
local auto_switch_node_list = uci:get(appname, t[".name"], "autoswitch_backup_node") or {}
for i = #auto_switch_node_list, 1, -1 do
if w == auto_switch_node_list[i] then
table.remove(auto_switch_node_list, i)
changed = true
end
end
uci:set_list(appname, t[".name"], "autoswitch_backup_node", auto_switch_node_list)
if changed then
uci:set_list(appname, t[".name"], "autoswitch_backup_node", auto_switch_node_list)
end
end)
uci:foreach(appname, "haproxy_config", function(t)
if t["lbss"] == w then

View File

@ -21,27 +21,50 @@ local function _n(name)
end
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("port"), translate("Port"))
o.datatype = "port"
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("hop"), translate("Port hopping range"))
o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o.datatype = "or(uinteger,portrange)"
o.placeholder = "30"
o.default = "30"
o.rewrite_option = o.option
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("obfs"), translate("Obfs Password"))
o = s:option(Flag, _n("realms"), translate("Realms"))
o.default = "0"
o.rewrite_option = o.option
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(Value, _n("auth_password"), translate("Auth Password"))
o.password = true
o.rewrite_option = o.option
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o.rewrite_option = o.option
o = s:option(Value, _n("obfs_password"), translate("Obfs Password"))
o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o = s:option(Flag, _n("fast_open"), translate("Fast Open"))
o.default = "0"

View File

@ -164,28 +164,66 @@ if load_balancing_options then -- [[ Load balancing Start ]]
-- Fallback Node
o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node"))
o.group = {"",""}
o:value("", translate("Close(Not use)"))
o:value("_direct", translate("Direct Connection"))
o:depends({ [_n("protocol")] = "_balancing" })
o.template = appname .. "/cbi/nodes_listvalue"
o.group = {""}
local function check_fallback_chain(fb)
for k, v in pairs(fallback_list) do
if v.fallback == fb then
fallback_list[k] = nil
check_fallback_chain(v.id)
-- Maximum number of fallback nesting layers
local MAX_FALLBACK_DEPTH = 3
-- Check if a loop will form.
local function will_loop(start_id, target_id, depth)
depth = depth or 0
-- Recursion stops after the maximum depth is exceeded.
if depth >= MAX_FALLBACK_DEPTH then
return false
end
for _, v in ipairs(fallback_list) do
if v.id == target_id then
local fb = v.fallback
-- No fallback
if not fb or fb == "" or fb == "_direct" then
return false
end
-- Loopback detected
if fb == start_id then
return true
end
-- Continue recursive checking
return will_loop(start_id, fb, depth + 1)
end
end
return false
end
-- Check the fallback chain and remove the balancer node that would form a closed loop.
if is_balancer then
check_fallback_chain(arg[1])
-- Get fallback chain depth
local function get_fallback_depth(id, depth)
depth = depth or 0
if depth >= MAX_FALLBACK_DEPTH then
return depth
end
for _, v in ipairs(fallback_list) do
if v.id == id then
local fb = v.fallback
if not fb or fb == "" or fb == "_direct" then
return depth
end
return get_fallback_depth(fb, depth + 1)
end
end
return depth
end
for i, v in ipairs(fallback_list) do
o:value(v.id, v.remark)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
for _, v in ipairs(fallback_list) do
local depth = get_fallback_depth(v.id)
-- Once the maximum number of nested doll layers is exceeded, further selection of the balancer is not allowed.
if depth < MAX_FALLBACK_DEPTH
and not will_loop(arg[1], v.id)
then
o:value(v.id, v.remark)
o.group[#o.group + 1] = (v.group and v.group ~= "") and v.group or translate("default")
end
end
for k1, v1 in pairs(node_list) do
if k1 == "socks_list" or k1 == "normal_list" then
if k1 == "socks_list" or k1 == "normal_list" or k1 == "urltest_list" then
for i, v in ipairs(v1) do
o:value(v.id, v.remark)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
@ -221,6 +259,13 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = "2"
o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.")
o = s:option(Value, _n("tolerance"), translate("Failure Tolerance (%)"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" })
o.datatype = "uinteger"
o.default = "10"
o.placeholder = "10"
o.description = translate("The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate.")
end -- [[ Load balancing End ]]
if load_iface_options then -- [[ Custom Interface Start ]]
@ -299,11 +344,9 @@ o.placeholder = "30"
o.default = "30"
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
@ -313,9 +356,11 @@ o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
o:depends({ [_n("protocol")] = "hysteria2"})

View File

@ -43,6 +43,8 @@ local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zer
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
local singbox_version = api.get_app_version("sing-box"):match("[^v]+")
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o:value("socks", "Socks")
o:value("http", "HTTP")
@ -388,19 +390,32 @@ end
if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_hop"), translate("Port hopping range"))
o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(Value, _n("hysteria2_hop_interval"), translate("Hop Interval(Second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o = s:option(Value, _n("hysteria2_hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o.datatype = "or(uinteger,portrange)"
o.placeholder = "30"
o.default = "30"
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o.default = "0"
if api.compare_versions(singbox_version, ">=", "1.14.0") then
o:depends({ [_n("protocol")] = "hysteria2"})
else
o:depends({ [_n("protocol")] = "__hide"})
end
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
@ -410,8 +425,20 @@ if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)")
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection"))
o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"})
end
@ -506,7 +533,7 @@ o.default = "0"
o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ [_n("protocol")] = "naive" })
o = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
@ -818,10 +845,14 @@ local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then
for i, v in ipairs(protocols) do
if not v:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = v })
s.fields[_n("port")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v })
local depends_condition = { [_n("protocol")] = v }
if v == "hysteria2" then
depends_condition[_n("hysteria2_realms")] = false
end
s.fields[_n("address")]:depends(depends_condition)
s.fields[_n("port")]:depends(depends_condition)
s.fields[_n("domain_resolver")]:depends(depends_condition)
s.fields[_n("domain_strategy")]:depends(depends_condition)
end
end
end
@ -836,6 +867,7 @@ if not load_shunt_options then
o:value("1", translate("Preproxy Node"))
end
o:value("2", translate("Landing Node"))
o:depends({ [_n("hysteria2_realms")] = false })
o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy."))
o1:depends({ [_n("chain_proxy")] = "1" })

View File

@ -22,17 +22,38 @@ o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("obfs"), translate("Obfs Password"))
o = s:option(Flag, _n("realms"), translate("Realms"))
o.default = "0"
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(Value, _n("auth_password"), translate("Auth Password"))
o.password = true
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("obfs_password"), translate("Obfs Password"))
o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o = s:option(Flag, _n("udp"), translate("UDP"))
o.default = "1"
o.rewrite_option = o.option

View File

@ -129,6 +129,14 @@ o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"))
o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" })
@ -139,14 +147,6 @@ o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwid
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
---- [[ TLS ]]
o = s:option(Flag, _n("tls"), translate("TLS"))
o.default = 0

View File

@ -58,6 +58,7 @@ o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("auth"), translate("Auth"))
@ -157,10 +158,29 @@ if singbox_tags:find("with_quic") then
end
if singbox_tags:find("with_quic") then
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm"))
o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" })
@ -170,14 +190,6 @@ if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
end
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol"))
@ -324,7 +336,7 @@ o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "naive" })
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(TextValue, _n("ech_key"), translate("ECH Key"))
o.default = ""

View File

@ -31,7 +31,11 @@ local type_table = {}
for filename in fs.dir(types_dir) do
table.insert(type_table, filename)
end
table.sort(type_table)
table.sort(type_table, function(a, b)
if a == "socks.lua" then return true end
if b == "socks.lua" then return false end
return a < b
end)
for index, value in ipairs(type_table) do
local p_func = loadfile(types_dir .. value)

View File

@ -377,11 +377,13 @@ function is_special_node(e)
end
function is_ip(val)
val = trim(val):lower()
local str = val:match("%[(.-)%]") or val
return datatypes.ipaddr(str) or false
end
function is_ipv6(val)
val = trim(val):lower()
local str = val:match("%[(.-)%]") or val
return datatypes.ip6addr(str) or false
end
@ -497,9 +499,10 @@ function get_valid_nodes()
end
end
local port = e.port or e.hysteria_hop or e.hysteria2_hop
if port and e.address then
local is_realm = (e.type == "Hysteria2" or e.protocol == 'hysteria2') and e.hysteria2_realms or nil
if (port and e.address) or is_realm then
local address = e.address
if is_ip(address) or datatypes.hostname(address) then
if is_ip(address) or datatypes.hostname(address) or is_realm then
if (e.type == "sing-box" or e.type == "Xray") and e.protocol then
local protocol = e.protocol
if protocol == "vmess" then
@ -526,10 +529,13 @@ function get_valid_nodes()
type_name = type_name .. " " .. protocol
end
if is_ipv6(address) then address = get_ipv6_full(address) end
type_name = is_realm and type_name .. " Realm" or type_name
e["remark"] = trim("%s[%s]" % {type_name, e.remarks})
if show_node_info == "1" then
port = port:gsub(":", "-")
e["remark"] = trim("%s[%s] %s:%s" % {type_name, e.remarks, address, port})
port = (port or ""):gsub(":", "-")
if not is_realm then
e["remark"] = trim("%s[%s] %s:%s" % {type_name, e.remarks, address, port})
end
end
e.node_type = "normal"
if not e.group or e.group == "" then
@ -619,7 +625,10 @@ function get_node_remarks(n)
end
type_name = type_name .. " " .. protocol
end
remarks = trim("%s[%s]" % {type_name, n.remarks})
if (n.type == "Hysteria2" or n.protocol == 'hysteria2') and n.hysteria2_realms then
type_name = type_name .. " Realm"
end
remarks = trim("%s[%s]" % {type_name, n.remarks})
end
end
return remarks
@ -1204,7 +1213,7 @@ function to_move(app_name,file)
}
end
local flag = sys.call('pgrep -af "passwall2/.*'.. app_name ..'" >/dev/null')
local flag = sys.call('busybox pgrep -af "passwall2/.*'.. app_name ..'" >/dev/null')
if flag == 0 then
sys.call("/etc/init.d/passwall2 stop")
end
@ -1587,4 +1596,30 @@ function get_dnsmasq_server_domain()
end
end
return dnsmasq_server_t
end
end
function parse_realm_uri(uri)
if type(uri) ~= "string" then return nil end
-- realm://token@server/realm_id?query
local token, server_url, realm_id, query = trim(uri):match("^realm://([^@]+)@([^/]+)/([^?]*)%??(.*)$")
if not token or not server_url or not realm_id then return nil end
realm_id = realm_id:gsub("/+$", "")
local realm = {
token = token,
server_url = server_url,
realm_id = realm_id
}
-- 解析 query 中的 stun=
if query and query ~= "" then
local stun_servers = {}
for key, value in query:gmatch("([^&=?]+)=([^&]+)") do
if key == "stun" and value ~= "" then
stun_servers[#stun_servers + 1] = value
end
end
if #stun_servers > 0 then
realm.stun_servers = stun_servers
end
end
return realm
end

View File

@ -5,15 +5,24 @@ local jsonc = api.jsonc
function gen_config_server(node)
local config = {
listen = ":" .. node.port,
listen = (function()
if node.hysteria2_realms and node.hysteria2_realm_url then
local url = node.hysteria2_realm_url:gsub("/+$", "")
if node.port then
url = url .. (url:find("?") and "&lport=" or "?lport=") .. node.port
end
return url
end
return ":" .. (node.port or "0")
end)(),
tls = {
cert = node.tls_certificateFile,
key = node.tls_keyFile,
},
obfs = (node.hysteria2_obfs) and {
obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = "salamander",
salamander = {
password = node.hysteria2_obfs
password = node.hysteria2_obfs_password
}
} or nil,
auth = {
@ -26,6 +35,9 @@ function gen_config_server(node)
} or nil,
ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false,
disableUDP = (node.hysteria2_udp == "0") and true or false,
realm = (node.hysteria2_realms and node.hysteria2_realm_stun) and {
stunServers = node.hysteria2_realm_stun
} or nil
}
return config
end
@ -51,14 +63,23 @@ function gen_config(var)
if api.is_ipv6(server_host) then
server_host = api.get_ipv6_full(server_host)
end
local server = server_host .. ":" .. server_port
if (node.hysteria2_hop) then
server = server .. "," .. string.gsub(node.hysteria2_hop, ":", "-")
local server = server_host .. (server_port and ":" .. server_port or "")
if node.hysteria2_hop then
server = server .. (server_port and "," or ":") .. string.gsub(node.hysteria2_hop, ":", "-")
end
local config = {
server = server,
server = (function()
if node.hysteria2_realms and node.hysteria2_realm_url then
return node.hysteria2_realm_url:gsub("/+$", "")
end
return server
end)(),
realm = (node.hysteria2_realms and node.hysteria2_realm_stun) and {
stunServers = node.hysteria2_realm_stun
} or nil,
transport = {
type = "udp",
udp = node.hysteria2_hop and (function()
@ -83,10 +104,10 @@ function gen_config(var)
return udp
end)() or nil
},
obfs = (node.hysteria2_obfs) and {
obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = "salamander",
salamander = {
password = node.hysteria2_obfs
password = node.hysteria2_obfs_password
}
} or nil,
auth = node.hysteria2_auth_password,

View File

@ -134,8 +134,8 @@ function gen_outbound(flag, node, tag, proxy_table)
local run_socks_instance = true
if proxy_table ~= nil and type(proxy_table) == "table" then
proxy_tag = proxy_table.tag or nil
fragment = proxy_table.fragment or nil
record_fragment = proxy_table.record_fragment or nil
fragment = (proxy_table.fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil
record_fragment = (proxy_table.record_fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil
run_socks_instance = proxy_table.run_socks_instance
end
@ -250,7 +250,7 @@ function gen_outbound(flag, node, tag, proxy_table)
enabled = true,
disable_sni = (node.tls_disable_sni == "1") and true or false, -- Do not send the server name in ClientHello.
server_name = node.tls_serverName, -- Used to verify the hostname on the returned certificate, unless the settings are insecure. It is also included in ClientHello to support virtual hosts, unless it is an IP address.
insecure = (node.tls_allowInsecure == "1") and true or false, -- Accepts any server certificate.
insecure = node.tls_allowInsecure == "1" or (node.tls_pinSHA256 and node.tls_pinSHA256 ~= ""), -- Accepts any server certificate. (Also compatible with xray's pinnedPeerCertSha256)
alpn = alpn, -- A list of supported application layer protocols, arranged in order of priority. If both peers support ALPN, the protocol selected will be one of these protocols; otherwise, the connection will fail.
--max_version = "1.3",
fragment = fragment,
@ -506,6 +506,7 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range)
end
end
result.server_port = nil
end
protocol_table = {
server_ports = next(server_ports) and server_ports or nil,
@ -519,9 +520,9 @@ function gen_outbound(flag, node, tag, proxy_table)
obfs = node.hysteria_obfs,
auth = (node.hysteria_auth_type == "base64") and node.hysteria_auth_password or nil,
auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil,
recv_window_conn = tonumber(node.hysteria_recv_window_conn),
recv_window = tonumber(node.hysteria_recv_window),
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false,
recv_window_conn = tonumber(node.hysteria_recv_window_conn), --1.14 将变更为 stream_receive_window
recv_window = tonumber(node.hysteria_recv_window), --1.14 将变更为 connection_receive_window
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 将变更为 disable_path_mtu_discovery
tls = tls
}
end
@ -545,13 +546,12 @@ function gen_outbound(flag, node, tag, proxy_table)
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
tls = tls
}
if node.tuic_alpn and node.tuic_alpn ~= "default" then
local alpn = {}
string.gsub(node.tuic_alpn, '[^,]+', function(w)
table.insert(alpn, w)
end)
if #alpn > 0 then protocol_table.tls.alpn = alpn end
end
node.tuic_alpn = (node.tuic_alpn and node.tuic_alpn ~= "default") and node.tuic_alpn or "h3"
local alpn = {}
string.gsub(node.tuic_alpn, '[^,]+', function(w)
table.insert(alpn, w)
end)
if #alpn > 0 then protocol_table.tls.alpn = alpn end
end
if node.protocol == "hysteria2" then
@ -563,6 +563,10 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range)
end
end
result.server_port = nil
end
if node.hysteria2_realms then
server_ports = {}
end
local interval, interval_max
if next(server_ports) then
@ -595,7 +599,29 @@ function gen_outbound(flag, node, tag, proxy_table)
password = node.hysteria2_obfs_password
} or nil,
password = node.hysteria2_auth_password or nil,
tls = tls
idle_timeout = (function(t)
if not version_ge_1_14_0 then return nil end
t = tonumber(tostring(t or "30"):match("^%d+"))
return (t and t >= 4 and t <= 120) and t or 30
end)(node.hysteria2_idle_timeout),
keep_alive_period = (function(t)
if not version_ge_1_14_0 then return nil end
t = tonumber(tostring(t or "0"):match("^%d+"))
return (t and t >= 2 and t <= 60) and t or nil
end)(node.hysteria2_keep_alive_period),
disable_path_mtu_discovery = version_ge_1_14_0 and (tonumber(node.hysteria2_disable_mtu_discovery) == 1) or nil,
tls = tls,
realm = node.hysteria2_realms and (function()
result.server = nil
result.server_port = nil
local realm = api.parse_realm_uri(node.hysteria2_realm_url)
if realm then
realm.server_url = realm.server_url and "https://" .. realm.server_url or nil
realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun
return realm
end
return nil
end)() or nil
}
end
@ -881,10 +907,10 @@ function gen_config_server(node)
auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil,
}
},
recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil,
recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil,
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil,
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false,
recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, --1.14 to stream_receive_window
recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, --1.14 to connection_receive_window
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, --1.14 to max_concurrent_streams
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 to disable_path_mtu_discover
tls = tls
}
end
@ -928,7 +954,17 @@ function gen_config_server(node)
}
},
ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false,
tls = tls
tls = tls,
realm = node.hysteria2_realms and (function()
local realm = api.parse_realm_uri(node.hysteria2_realm_url)
if realm then
realm.server_url = realm.server_url and "https://" .. realm.server_url or nil
realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun
realm.stun_domain_resolver = "direct"
return realm
end
return nil
end)() or nil
}
end
@ -1312,7 +1348,6 @@ function gen_config(var)
else
ut_nodes = _node.urltest_node
end
if #ut_nodes == 0 then return nil end
local valid_nodes = {}
for i = 1, #(ut_nodes or {}) do
local ut_node_id = ut_nodes[i]
@ -2051,7 +2086,7 @@ function gen_config(var)
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w)
end)
local ipset_prefix_name = "passwall2_" .. node_id .. "_"
local ipset_prefix_name = "psw2_" .. node_id .. "_"
local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'")
string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call("ipset -q -F " .. w)
@ -2070,7 +2105,7 @@ function gen_config(var)
end)
local family = "inet"
local table_name = "passwall2"
local nftset_prefix_name = "passwall2_" .. node_id .. "_"
local nftset_prefix_name = "psw2_" .. node_id .. "_"
local nftset_list = sys.exec("nft -a list sets | grep -E '" .. nftset_prefix_name .. "' | awk -F 'set ' '{print $2}' | awk '{print $1}'")
string.gsub(nftset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, w))
@ -2138,7 +2173,7 @@ function gen_config(var)
if not value.detour and value.server then
value.detour = "direct"
end
if value.server and not api.datatypes.hostname(value.server) then
if (value.server and not api.datatypes.hostname(value.server)) and not value.realm then
value.domain_resolver = nil
end
for k, v in pairs(config.outbounds[index]) do

View File

@ -794,6 +794,9 @@ function gen_config_server(node)
config.outbounds[index][k] = nil
end
end
if value.protocol == "freedom" and api.compare_versions(xray_version, "<", "26.5.3") then -- Todo is to remove it
value.settings = nil
end
end
return config
@ -1049,10 +1052,10 @@ function gen_config(var)
if #valid_nodes == 0 then return nil end
-- fallback node
local fallback_node_tag = nil
local fallback_node_id = _node.fallback_node
if not fallback_node_id or fallback_node_id == "" then fallback_node_id = nil end
if fallback_node_id then
fallback_node_id = (fallback_node_id and fallback_node_id ~= "") and fallback_node_id or nil
local fallback_node_tag = (fallback_node_id == "_direct") and "direct" or "blackhole"
if fallback_node_id and fallback_node_id ~= "_direct" then
local is_new_node = true
for _, outbound in ipairs(outbounds) do
if string.sub(outbound.tag, 1, #fallback_node_id) == fallback_node_id then
@ -1082,7 +1085,13 @@ function gen_config(var)
type = _node.balancingStrategy,
settings = {
expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2,
maxRTT = "1s"
maxRTT = "1s",
tolerance = (function(t)
t = tonumber(t) or 0
if t < 1 then return nil end
if t > 100 then t = 100 end
return t / 100
end)(_node.tolerance)
}
}
else
@ -1264,9 +1273,9 @@ function gen_config(var)
interface = node.iface
}
},
settings = {
settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check
finalRules = {{ action = "allow" }}
}
} or nil
}
sys.call(string.format("mkdir -p %s && touch %s/%s", api.TMP_IFACE_PATH, api.TMP_IFACE_PATH, node.iface))
end
@ -1806,7 +1815,7 @@ function gen_config(var)
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w)
end)
local ipset_prefix_name = "passwall2_" .. node_id .. "_"
local ipset_prefix_name = "psw2_" .. node_id .. "_"
local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'")
string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call("ipset -q -F " .. w)
@ -1825,7 +1834,7 @@ function gen_config(var)
end)
local family = "inet"
local table_name = "passwall2"
local nftset_prefix_name = "passwall2_" .. node_id .. "_"
local nftset_prefix_name = "psw2_" .. node_id .. "_"
local nftset_list = sys.exec("nft -a list sets | grep -E '" .. nftset_prefix_name .. "' | awk -F 'set ' '{print $2}' | awk '{print $1}'")
string.gsub(nftset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, w))
@ -2080,7 +2089,12 @@ function gen_proto_config(var)
end
table.insert(outbounds, {
protocol = "freedom", tag = "direct", settings = {finalRules = {{ action = "allow" }}}, sockopt = {mark = 255}
protocol = "freedom",
tag = "direct",
settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check
finalRules = {{ action = "allow" }}
} or nil,
sockopt = {mark = 255}
})
local config = {

View File

@ -35,22 +35,48 @@ local appname = api.appname
</style>
<script type="text/javascript">
//<![CDATA[
var shunt_list = JSON.parse('<%=self.shunt_list%>');
const shunt_list = JSON.parse('<%=self.shunt_list%>');
function go() {
var _status = document.getElementsByClassName('_status');
for (var i = 0; i < _status.length; i++) {
var id = _status[i].getAttribute("socks_id");
function to_edit_node(btn) {
if (!btn) return;
const idReg = /^cbid\..*\..*node$/;
let hidden_select = null;
const container = btn.closest('#cbi-<%=appname%>-global') || btn.closest('#cbi-<%=appname%>-socks');
if (!container) return null;
const selects = container.querySelectorAll('select[id^="cbid."][id*="."][id$="node"]');
for (const sel of selects) {
if ( idReg.test(sel.id) && getComputedStyle(sel).display === "none" && (sel.compareDocumentPosition(btn) & Node.DOCUMENT_POSITION_FOLLOWING)) {
hidden_select = sel;
}
}
if (!hidden_select) return;
let node_select_value = hidden_select?.options[0]?.value;
if (!node_select_value || node_select_value.indexOf("_default") === 0 || node_select_value.indexOf("_direct") === 0 || node_select_value.indexOf("_blackhole") === 0) {
return;
}
let to_url = '<%=api.url("node_config")%>/' + node_select_value;
if (node_select_value.indexOf("Socks_") === 0) {
to_url = '<%=api.url("socks_config")%>/' + node_select_value.substring("Socks_".length);
}
location.href = to_url;
}
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function go() {
const _status = document.getElementsByClassName('_status');
for (let i = 0; i < _status.length; i++) {
const id = _status[i].getAttribute("socks_id");
XHR.get('<%=api.url("socks_status")%>', {
index: i,
id: id
},
function(x, result) {
var index = result.index;
var div = '';
var div1 = '<font style="font-weight:bold;" color="green"></font>&nbsp';
var div2 = '<font style="font-weight:bold;" color="red">X</font>&nbsp';
const index = result.index;
let div = '';
const div1 = '<font style="font-weight:bold;" color="green"></font>&nbsp';
const div2 = '<font style="font-weight:bold;" color="red">X</font>&nbsp';
if (result.socks_status) {
div += div1;
} else {
@ -67,83 +93,112 @@ local appname = api.appname
}
);
}
var global_id = null;
var global = document.getElementById("cbi-<%=appname%>-global");
let global_id = null;
const global = document.getElementById("cbi-<%=appname%>-global");
if (global) {
var node = global.getElementsByClassName("cbi-section-node")[0];
var node_id = node.getAttribute("id");
global_id = node_id;
var all_node = node.querySelectorAll("[id]");
var reg1 = /^cbid\..*node\.main$/;
// Add an "Edit" placeholder to the routing rule node in advance to ensure left alignment of the node list.
document.querySelectorAll('.td[data-widget="nodes_listvalue"][data-name="_node"] > div').forEach(container => {
if (!container.querySelector('.shunt-node-placeholder')) {
const placeholder = document.createElement('div');
placeholder.className = 'shunt-node-placeholder';
placeholder.style.cssText = 'display:inline-flex; width:40px; flex-shrink:0;';
container.appendChild(placeholder);
}
});
for (var i = 0; i < all_node.length; i++) {
var el = all_node[i];
await delay(100);
const node = global.getElementsByClassName("cbi-section-node")[0];
const node_id = node.getAttribute("id");
global_id = node_id;
const all_node = node.querySelectorAll("[id]");
const reg1 = /^cbid\..*node\.main$/;
//const reg1 = /^cbid\..*\.(node)\.main$/;
for (let i = 0; i < all_node.length; i++) {
const el = all_node[i];
if (!reg1.test(el.id)) continue;
var node_select = el;
const node_select = el;
if (!node_select) continue;
var cbid = el.id.replace(/\.main$/, "");
var hidden_select = document.getElementById(cbid);
var node_select_value = hidden_select ? hidden_select.options[0].value : "";
const cbid = el.id.replace(/\.main$/, "");
const hidden_select = document.getElementById(cbid);
const node_select_value = hidden_select?.options[0]?.value;
if (!node_select_value || node_select_value.indexOf("_default") === 0 || node_select_value.indexOf("_direct") === 0 || node_select_value.indexOf("_blackhole") === 0) {
continue;
}
var to_url = '<%=api.url("node_config")%>/' + node_select_value;
if (node_select_value.indexOf("Socks_") === 0) {
to_url = '<%=api.url("socks_config")%>/' + node_select_value.substring("Socks_".length);
}
var html = '<a href="#" onclick="location.href=\'' + to_url + '\'"><%:Edit%></a>';
let html = '<a href="#" onclick="return to_edit_node(this);"><%:Edit%></a>';
var m = cbid.match(/\.node$/);
const m = cbid.match(/\.node$/);
if (m) {
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_redir_log")%>?id=default&name=' + 'global' + '\', \'_blank\')"><%:Log%></a>';
}
node_select.insertAdjacentHTML("beforeend",
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>'
);
html = '<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">' + html + '</div>'
const placeholder = node_select.parentElement.querySelector('.shunt-node-placeholder');
if (placeholder) {
placeholder.innerHTML = html;
} else {
node_select.insertAdjacentHTML("beforeend", html);
}
}
}
var socks = document.getElementById("cbi-<%=appname%>-socks");
const socks = document.getElementById("cbi-<%=appname%>-socks");
if (socks) {
var socks_enabled_dom = document.getElementById(global_id + "-socks_enabled");
socks_enabled_dom.parentNode.removeChild(socks_enabled_dom);
var descr = socks.getElementsByClassName("cbi-section-descr")[0];
descr.outerHTML = socks_enabled_dom.outerHTML;
rows = socks.getElementsByClassName("cbi-section-table-row");
for (var i = 0; i < rows.length; i++) {
const socks_enabled_dom = document.getElementById(global_id + "-socks_enabled");
const descr = socks.getElementsByClassName("cbi-section-descr")[0];
descr.replaceWith(socks_enabled_dom);
const rows = socks.getElementsByClassName("cbi-section-table-row");
for (let i = 0; i < rows.length; i++) {
try {
var row = rows[i];
var id = row.id;
const row = rows[i];
const id = row.id;
if (!id) continue;
var dom_id = id + "-node";
var cbid = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
let dom_id = id + "-node";
const cbid = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
dom_id = cbid + ".main";
var node_select = document.getElementById(dom_id);
const node_select = document.getElementById(dom_id);
if (!node_select) continue;
var hidden_select = document.getElementById(cbid);;
var node_select_value = hidden_select ? hidden_select.options[0].value : "";
if (node_select_value && node_select_value != "") {
var to_url = '<%=api.url("node_config")%>/' + node_select_value;
var html = '<a href="#" onclick="location.href=\'' + to_url + '\'"><%:Edit%></a>';
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_socks_log")%>?name=' + id.replace("cbi-<%=appname%>-", "") + '\', \'_blank\')"><%:Log%></a>';
let html = '<a href="#" onclick="return to_edit_node(this);"><%:Edit%></a>';
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_socks_log")%>?name=' + id.replace("cbi-<%=appname%>-", "") + '\', \'_blank\')"><%:Log%></a>';
node_select.insertAdjacentHTML("afterend",
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>'
);
}
node_select.insertAdjacentHTML("afterend",
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>'
);
} catch(err) {
}
}
}
}
(function () {
let lastCount = 0;
let stableTimes = 0;
const STABLE_THRESHOLD = 5;
function waitStable() {
const list = document.querySelectorAll('[id^="cbid.<%=appname%>."][id$="node.main"]');
const count = list.length;
if (count === lastCount && count > 0) {
stableTimes++;
} else {
stableTimes = 0;
lastCount = count;
}
if (stableTimes >= STABLE_THRESHOLD) {
go();
return;
}
requestAnimationFrame(waitStable);
}
waitStable();
})();
document.addEventListener("DOMContentLoaded", () => setTimeout(() => {
const hiddenSelect = document.getElementById("cbid.<%=appname%>.<%=self.global_cfgid%>.node");
let o_val = hiddenSelect.value
@ -184,8 +239,6 @@ local appname = api.appname
shunt_taboption.appendChild(shunt_option_list);
}
}
go();
}, 100));
//setTimeout("go()", 1000);
//]]>
</script>

View File

@ -602,10 +602,10 @@ local current_node = map:get(section)
if (v_type === "Hysteria2") {
v_password = opt.get("hysteria2_auth_password");
var dom_obfs = opt.get("hysteria2_obfs");
var dom_obfs = opt.get("hysteria2_obfs_password");
if (dom_obfs && dom_obfs.value != "") {
params += "&obfs=" + "salamander";
params += opt.query("obfs-password", "hysteria2_obfs");
params += opt.query("obfs-password", "hysteria2_obfs_password");
}
params += opt.query("mport", "hysteria2_hop");
} else {
@ -1621,7 +1621,8 @@ local current_node = map:get(section)
dom_prefix = "hysteria2_"
opt.set(dom_prefix + 'auth_password', decodeURIComponent(password));
if (queryParam["obfs-password"] || queryParam["obfs_password"]) {
opt.set(dom_prefix + 'obfs', queryParam["obfs-password"] || queryParam["obfs_password"]);
opt.set(dom_prefix + 'obfs_type', "salamander");
opt.set(dom_prefix + 'obfs_password', queryParam["obfs-password"] || queryParam["obfs_password"]);
}
opt.set(dom_prefix + 'hop', queryParam.mport || "");
}
@ -1678,7 +1679,7 @@ local current_node = map:get(section)
}
opt.set(dom_prefix + 'tuic_congestion_control', queryParam.congestion_control || 'cubic');
opt.set(dom_prefix + 'tuic_udp_relay_mode', queryParam.udp_relay_mode || 'native');
opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'h3');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'tls_disable_sni', queryParam.disable_sni === "1");
opt.set(

View File

@ -911,7 +911,11 @@ table td, .table .td {
} else {
port_s = o["hysteria_hop"] || o["hysteria2_hop"];
}
str += type + "" + remarks;
if ((o.type === "Hysteria2" || o.protocol === 'hysteria2') && o.hysteria2_realms) {
str += type + " Realm" + remarks;
} else {
str += type + "" + remarks;
}
return str;
}

View File

@ -46,8 +46,8 @@ msgstr "لیست قوانین"
msgid "Access control"
msgstr "کنترل دسترسی"
msgid "Watch Logs"
msgstr "مشاهده گزارش‌ها"
msgid "Runtime Logs"
msgstr "گزارش‌های زمان اجرا"
msgid "Node Config"
msgstr "پیکربندی گره"
@ -437,6 +437,12 @@ msgstr "تعداد گره‌های ترجیحی"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "توزیع‌کننده بار تعداد بهینه‌ای از گره‌ها را انتخاب می‌کند و ترافیک به طور تصادفی بین آن‌ها توزیع می‌شود."
msgid "Failure Tolerance (%)"
msgstr "تحمل شکست (%)"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "حداکثر نرخ شکست قابل قبول در تست سرعت. برای مثال، عدد ۱ به معنای مجاز بودن نرخ شکست ۱٪ است.
msgid "Shunt"
msgstr "شنت (Shunt)"

View File

@ -46,8 +46,8 @@ msgstr "Список правил"
msgid "Access control"
msgstr "Контроль доступа"
msgid "Watch Logs"
msgstr "Просмотр журнала"
msgid "Runtime Logs"
msgstr "Журналы выполнения"
msgid "Node Config"
msgstr "Параметры узла"
@ -433,6 +433,12 @@ msgstr "Количество предпочтительных узлов"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "Балансировщик выбирает указанное количество наилучших узлов и распределяет трафик между ними случайным образом."
msgid "Failure Tolerance (%)"
msgstr "Допуск на отказ (%)"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "Максимально допустимый процент отказов при проверке скорости. Например, 1 означает допустимый процент отказов в 1%."
msgid "Shunt"
msgstr "Shunt (разделение трафика)"

View File

@ -46,8 +46,8 @@ msgstr "规则列表"
msgid "Access control"
msgstr "访问控制"
msgid "Watch Logs"
msgstr "查看日志"
msgid "Runtime Logs"
msgstr "运行日志"
msgid "Node Config"
msgstr "节点配置"
@ -433,6 +433,12 @@ msgstr "优选节点数量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。"
msgid "Failure Tolerance (%)"
msgstr "测速失败率容忍度(%"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "最多可接受的测速失败率,例如 1 表示允许 1% 的测速失败率。"
msgid "Shunt"
msgstr "分流"

View File

@ -46,8 +46,8 @@ msgstr "規則列表"
msgid "Access control"
msgstr "訪問控制"
msgid "Watch Logs"
msgstr "查看日誌"
msgid "Runtime Logs"
msgstr "運行日誌"
msgid "Node Config"
msgstr "節點配置"
@ -433,6 +433,12 @@ msgstr "優選節點數量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "負載均衡器選出最優節點的個數,流量將在這几個節點中随機分配。"
msgid "Failure Tolerance (%)"
msgstr "測速失敗率容忍度(%"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "最多可接受的測速失敗率,例如 1 表示允許 1% 的測速失敗率。"
msgid "Shunt"
msgstr "分流"

View File

@ -12,7 +12,7 @@
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 &
[ -n "$USE_TABLES" ] && { ${APP_PATH}/${USE_TABLES}.sh update_wan_sets >/dev/null 2>&1 & }
exit 0
fi

View File

@ -84,7 +84,7 @@ run_xray() {
json_add_string "loglevel" "${loglevel}"
[ -n "$flag" ] && {
pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1
busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1
json_add_string "flag" "${flag}"
}
[ -n "$socks_address" ] && [ -n "$socks_port" ] && {
@ -112,12 +112,12 @@ run_xray() {
local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf
[ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf
if [ "${nftflag}" = "1" ]; then
local direct_nftset4="passwall2_${node}_white"
local direct_nftset6="passwall2_${node}_white6"
local direct_nftset4="psw2_${node}_white"
local direct_nftset6="psw2_${node}_white6"
local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}"
else
local direct_ipset4="passwall2_${node}_white"
local direct_ipset6="passwall2_${node}_white6"
local direct_ipset4="psw2_${node}_white"
local direct_ipset6="psw2_${node}_white6"
local direct_ipset="${direct_ipset4},${direct_ipset6}"
fi
run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf}
@ -225,7 +225,7 @@ run_singbox() {
json_add_string "loglevel" "${loglevel}"
[ -n "$flag" ] && {
pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1
busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1
json_add_string "flag" "${flag}"
}
[ -n "$socks_address" ] && [ -n "$socks_port" ] && {
@ -251,12 +251,12 @@ run_singbox() {
local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf
[ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf
if [ "${nftflag}" = "1" ]; then
local direct_nftset4="passwall2_${node}_white"
local direct_nftset6="passwall2_${node}_white6"
local direct_nftset4="psw2_${node}_white"
local direct_nftset6="psw2_${node}_white6"
local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}"
else
local direct_ipset4="passwall2_${node}_white"
local direct_ipset6="passwall2_${node}_white6"
local direct_ipset4="psw2_${node}_white"
local direct_ipset6="psw2_${node}_white6"
local direct_ipset="${direct_ipset4},${direct_ipset6}"
fi
run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf}
@ -386,6 +386,10 @@ run_socks() {
fi
fi
if [ -n "${error_msg}" ] && [ "$(config_n_get $node hysteria2_realms)" = "1" ]; then
unset error_msg
fi
[ -n "${error_msg}" ] && {
[ "$bind" != "127.0.0.1" ] && log 1 "$(i18n "Socks node: [%s]%s, start failed %s:%s %s" "${remarks}" "${tmp}" "${bind}" "${socks_port}" "${error_msg}")"
return 1
@ -555,7 +559,7 @@ socks_node_switch() {
[ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1
done
pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1 && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1
busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1 && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1
for prefix in "" "HTTP_" "HTTP2"; do
rm -rf "$TMP_PATH/${prefix}SOCKS_${flag}"*
done
@ -791,7 +795,7 @@ start_socks() {
# Auto switch logic
local enable_autoswitch=$(config_n_get $id enable_autoswitch 0)
[ "$enable_autoswitch" = "1" ] && $APP_PATH/socks_auto_switch.sh ${id} > /dev/null 2>&1 &
[ "$enable_autoswitch" = "1" ] && { $APP_PATH/socks_auto_switch.sh ${id} > /dev/null 2>&1 & }
done
}
}
@ -805,14 +809,14 @@ clean_crontab() {
sed -i "/$(echo "lua ${APP_PATH}/rule_update.lua log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1
sed -i "/$(echo "lua ${APP_PATH}/subscribe.lua start" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1
pgrep -af "${CONFIG}/" | awk '/tasks\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
busybox pgrep -af "${CONFIG}/" | awk '/tasks\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf /tmp/lock/${CONFIG}_tasks.lock
}
start_crontab() {
if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then
start_daemon=$(config_t_get global_delay start_daemon 0)
[ "$start_daemon" = "1" ] && $APP_PATH/monitor.sh > /dev/null 2>&1 &
[ "$start_daemon" = "1" ] && { $APP_PATH/monitor.sh > /dev/null 2>&1 & }
fi
[ -f "/tmp/lock/${CONFIG}_cron.lock" ] && {
@ -982,11 +986,11 @@ run_ipset_chinadns_ng() {
[ -n "${ipset}" ] && {
set_names=$ipset
vps_set_names="passwall2_vps,passwall2_vps6"
vps_set_names="psw2_vps,psw2_vps6"
}
[ -n "${nftset}" ] && {
set_names=$(echo ${nftset} | awk -F, '{printf "%s,%s", substr($1,3), substr($2,3)}' | sed 's/#/@/g')
vps_set_names="inet@passwall2@passwall2_vps,inet@passwall2@passwall2_vps6"
vps_set_names="inet@passwall2@psw2_vps,inet@passwall2@psw2_vps6"
}
cat <<-EOF > $config_file
bind-addr 127.0.0.1
@ -1162,7 +1166,7 @@ acl_app() {
}
start() {
pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && {
busybox pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && {
#log_i18n 0 "The program has started. Please stop it and then restart it!"
stop
}
@ -1241,8 +1245,8 @@ stop() {
kill -9 "$pid" >/dev/null 2>&1
fi
done
pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1
pgrep -af "${CONFIG}/" | awk '! /app\.sh|subscribe\.lua|rule_update\.lua|tasks\.sh|server_app\.lua|ujail/{print $1}' | xargs kill -9 >/dev/null 2>&1
busybox pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1
busybox pgrep -af "${CONFIG}/" | awk '! /app\.sh|subscribe\.lua|rule_update\.lua|tasks\.sh|server_app\.lua|ujail/{print $1}' | xargs kill -9 >/dev/null 2>&1
unset V2RAY_LOCATION_ASSET
unset XRAY_LOCATION_ASSET
unset SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN

View File

@ -8,7 +8,8 @@ listen_port=$2
server_address=$3
server_port=$4
pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null && {
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
}

View File

@ -274,7 +274,7 @@ function add_rule(var)
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 .. "passwall2_vps," .. setflag_6 .. "passwall2_vps6")
set_domain_ipset(address, setflag_4 .. "psw2_vps," .. setflag_6 .. "psw2_vps6")
end
end
process_address(t.address)

View File

@ -3,17 +3,17 @@
DIR="$(cd "$(dirname "$0")" && pwd)"
MY_PATH=$DIR/iptables.sh
UTILS_PATH=$DIR/utils.sh
IPSET_LOCAL="passwall2_local"
IPSET_PROXY_LAN="passwall2_proxy_lan"
IPSET_LAN="passwall2_lan"
IPSET_VPS="passwall2_vps"
IPSET_WAN="passwall2_wan"
IPSET_LOCAL="psw2_local"
IPSET_PROXY_LAN="psw2_proxy_lan"
IPSET_LAN="psw2_lan"
IPSET_VPS="psw2_vps"
IPSET_WAN="psw2_wan"
IPSET_LOCAL6="passwall2_local6"
IPSET_PROXY_LAN6="passwall2_proxy_lan6"
IPSET_LAN6="passwall2_lan6"
IPSET_VPS6="passwall2_vps6"
IPSET_WAN6="passwall2_wan6"
IPSET_LOCAL6="psw2_local6"
IPSET_PROXY_LAN6="psw2_proxy_lan6"
IPSET_LAN6="psw2_lan6"
IPSET_VPS6="psw2_vps6"
IPSET_WAN6="psw2_wan6"
FWMARK="0x50535732"
@ -194,8 +194,8 @@ gen_shunt_list() {
for shunt_id in $shunt_ids; do
local shunt_node=$(config_n_get ${node} "${shunt_id}")
[ -n "$shunt_node" ] && {
local ipset_v4="passwall2_${node}_${shunt_id}"
local ipset_v6="passwall2_${node}_${shunt_id}6"
local ipset_v4="psw2_${node}_${shunt_id}"
local ipset_v6="psw2_${node}_${shunt_id}6"
ipset -! create $ipset_v4 nethash maxelem 1048576
ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576
local outbound="redirect"
@ -219,22 +219,14 @@ gen_shunt_list() {
}
local direct_ipset4=$(get_cache_var "node_${node}_direct_ipset4")
[ -n "${direct_ipset4}" ] && {
ipset -! create ${direct_ipset4} nethash maxelem 1048576 timeout 259200
ipset -! create ${direct_ipset4} iphash maxelem 1048576 timeout 259200
_SHUNT_LIST4="${_SHUNT_LIST4} ${direct_ipset4}:direct"
}
local direct_ipset6=$(get_cache_var "node_${node}_direct_ipset6")
[ -n "${direct_ipset6}" ] && {
ipset -! create ${direct_ipset6} nethash family inet6 maxelem 1048576 timeout 259200
ipset -! create ${direct_ipset6} iphash family inet6 maxelem 1048576 timeout 259200
_SHUNT_LIST6="${_SHUNT_LIST6} ${direct_ipset6}:direct"
}
[ "${preloading}" = "1" ] && [ -n "$default_node" ] && {
local ipset_v4="passwall2_${node}_default"
local ipset_v6="passwall2_${node}_default6"
ipset -! create $ipset_v4 nethash maxelem 1048576
ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576
_SHUNT_LIST4="${_SHUNT_LIST4} ${ipset_v4}:${default_outbound}"
_SHUNT_LIST6="${_SHUNT_LIST6} ${ipset_v6}:${default_outbound}"
}
}
[ -n "${_SHUNT_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\"
[ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\"
@ -618,6 +610,8 @@ filter_direct_node_list() {
}
update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && {
ipset -F "$IPSET_WAN"
@ -641,13 +635,13 @@ add_firewall_rule() {
ipset -! create $IPSET_LOCAL nethash maxelem 1048576
ipset -! create $IPSET_PROXY_LAN nethash maxelem 1048576
ipset -! create $IPSET_LAN nethash maxelem 1048576
ipset -! create $IPSET_VPS nethash maxelem 1048576
ipset -! create $IPSET_VPS iphash maxelem 1048576
ipset -! create $IPSET_WAN nethash maxelem 1048576
ipset -! create $IPSET_LOCAL6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_PROXY_LAN6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_LAN6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_VPS6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_VPS6 iphash family inet6 maxelem 1048576
ipset -! create $IPSET_WAN6 nethash family inet6 maxelem 1048576
get_local_ips ip4 | sed "s/^/add $IPSET_LOCAL /" | ipset -! -R
@ -1025,7 +1019,7 @@ del_firewall_rule() {
flush_ipset() {
log_i18n 0 "Clear %s." "IPSet"
for _name in $(ipset list | grep "Name: " | grep "passwall2_" | awk '{print $2}'); do
for _name in $(ipset list | grep "Name: " | grep "psw2_" | awk '{print $2}'); do
destroy_ipset ${_name}
done
}
@ -1105,7 +1099,7 @@ start() {
}
stop() {
[ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh
[ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH"
del_firewall_rule
destroy_ipset $IPSET_PROXY_LAN
destroy_ipset $IPSET_PROXY_LAN6

View File

@ -18,7 +18,7 @@ while [ "$ENABLED" -eq 1 ]; do
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}')
icount=$(pgrep -f "$(echo $cmd_check)" | wc -l)
icount=$(busybox pgrep -f "$(echo $cmd_check)" | wc -l)
if [ $icount = 0 ]; then
#echo "${cmd} crashed, restarting." >> /tmp/log/passwall2.log
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &

View File

@ -4,17 +4,17 @@ DIR="$(cd "$(dirname "$0")" && pwd)"
MY_PATH=$DIR/nftables.sh
UTILS_PATH=$DIR/utils.sh
NFTABLE_NAME="inet passwall2"
NFTSET_LOCAL="passwall2_local"
NFTSET_PROXY_LAN="passwall2_proxy_lan"
NFTSET_LAN="passwall2_lan"
NFTSET_VPS="passwall2_vps"
NFTSET_WAN="passwall2_wan"
NFTSET_LOCAL="psw2_local"
NFTSET_PROXY_LAN="psw2_proxy_lan"
NFTSET_LAN="psw2_lan"
NFTSET_VPS="psw2_vps"
NFTSET_WAN="psw2_wan"
NFTSET_LOCAL6="passwall2_local6"
NFTSET_PROXY_LAN6="passwall2_proxy_lan6"
NFTSET_LAN6="passwall2_lan6"
NFTSET_VPS6="passwall2_vps6"
NFTSET_WAN6="passwall2_wan6"
NFTSET_LOCAL6="psw2_local6"
NFTSET_PROXY_LAN6="psw2_proxy_lan6"
NFTSET_LAN6="psw2_lan6"
NFTSET_VPS6="psw2_vps6"
NFTSET_WAN6="psw2_wan6"
FWMARK="0x50535732"
@ -231,8 +231,8 @@ gen_shunt_list() {
for shunt_id in $shunt_ids; do
local shunt_node=$(config_n_get ${node} "${shunt_id}")
[ -n "$shunt_node" ] && {
local nftset_v4="passwall2_${node}_${shunt_id}"
local nftset_v6="passwall2_${node}_${shunt_id}6"
local nftset_v4="psw2_${node}_${shunt_id}"
local nftset_v6="psw2_${node}_${shunt_id}6"
gen_nftset $nftset_v4 ipv4_addr 0 0
gen_nftset $nftset_v6 ipv6_addr 0 0
local outbound="redirect"
@ -263,14 +263,6 @@ gen_shunt_list() {
gen_nftset $direct_nftset6 ipv6_addr 0 0
_SHUNT_LIST6="${_SHUNT_LIST6} ${direct_nftset6}:direct"
}
[ "${preloading}" = "1" ] && [ -n "$default_node" ] && {
local nftset_v4="passwall2_${node}_default"
local nftset_v6="passwall2_${node}_default6"
gen_nftset $nftset_v4 ipv4_addr 0 0
gen_nftset $nftset_v6 ipv6_addr 0 0
_SHUNT_LIST4="${_SHUNT_LIST4} ${nftset_v4}:${default_outbound}"
_SHUNT_LIST6="${_SHUNT_LIST6} ${nftset_v6}:${default_outbound}"
}
}
[ -n "${_SHUNT_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\"
[ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\"
@ -681,6 +673,8 @@ mwan3_start() {
}
update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && {
nft flush set $NFTABLE_NAME $NFTSET_WAN
@ -1073,7 +1067,7 @@ del_firewall_rule() {
flush_nftset() {
log_i18n 0 "Clear %s." "NFTSet"
for _name in $(nft -a list sets | grep -E "passwall2_" | awk -F 'set ' '{print $2}' | awk '{print $1}'); do
for _name in $(nft -a list sets | grep -E "psw2_" | awk -F 'set ' '{print $2}' | awk '{print $1}'); do
destroy_nftset ${_name}
done
}
@ -1116,7 +1110,7 @@ start() {
}
stop() {
[ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh
[ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH"
del_firewall_rule
destroy_nftset $NFTSET_PROXY_LAN
destroy_nftset $NFTSET_PROXY_LAN6

View File

@ -6,7 +6,7 @@ APP_FILE=${APP_PATH}/app.sh
flag=0
check_process() {
while pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null; do
while busybox pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null; do
sleep 6s
done
}
@ -63,7 +63,7 @@ test_node() {
# Kill the SS plugin process
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
busybox 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

View File

@ -1688,22 +1688,19 @@ local function processData(szType, content, add_mode, group, sub_cfg)
local insecure = params.allowinsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.hysteria2_hop = params.mport
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs_type = "salamander"
result.hysteria2_obfs_password = params["obfs-password"] or params["obfs_password"]
end
if (sub_hysteria2_type == "sing-box" and has_singbox) or (sub_hysteria2_type == "xray" and has_xray) then
local is_singbox = sub_hysteria2_type == "sing-box" and has_singbox
result.type = is_singbox and 'sing-box' or 'Xray'
result.protocol = "hysteria2"
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs_type = "salamander"
result.hysteria2_obfs_password = params["obfs-password"] or params["obfs_password"]
end
result.use_finalmask = (params.fm and params.fm ~= "") and "1" or nil
result.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil
elseif has_hysteria2 then
result.type = "Hysteria2"
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs = params["obfs-password"] or params["obfs_password"]
end
else
log(2, i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "Hysteria2", "Hysteria2"))
return nil
@ -1761,7 +1758,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end
result.tls_serverName = params.sni
result.tls_disable_sni = params.disable_sni
result.tuic_alpn = params.alpn or "default"
result.tuic_alpn = params.alpn or "h3"
result.tuic_congestion_control = params.congestion_control or "cubic"
result.tuic_udp_relay_mode = params.udp_relay_mode or "native"
local insecure = params.allowinsecure or params.insecure or params.allow_insecure
@ -2049,12 +2046,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- First priority: Type + Notes + IP + Port
-- First priority: Type + Notes + IP + Port + Group
if not server then
for index, node in pairs(nodes) do
if config.currentNode.type and config.currentNode.remarks and config.currentNode.address and config.currentNode.port then
if node.type and node.remarks and node.address and node.port then
if node.type == config.currentNode.type and node.remarks == config.currentNode.remarks and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then
if node.type == config.currentNode.type and node.remarks == config.currentNode.remarks and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) and node.group == config.currentNode.group then
if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("First Matching node:") .. " " .. node.remarks)
end
@ -2065,12 +2062,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- Second priority: Type + IP + Port
-- Second priority: Type + IP + Port + Group
if not server then
for index, node in pairs(nodes) do
if config.currentNode.type and config.currentNode.address and config.currentNode.port then
if node.type and node.address and node.port then
if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then
if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) and node.group == config.currentNode.group then
if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Second Matching node:") .. " " .. node.remarks)
end
@ -2081,12 +2078,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- Third priority: IP + Port
-- Third priority: IP + Port + Group
if not server then
for index, node in pairs(nodes) do
if config.currentNode.address and config.currentNode.port then
if node.address and node.port then
if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port then
if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port and node.group == config.currentNode.group then
if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Third Matching node:") .. " " .. node.remarks)
end
@ -2097,12 +2094,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- Fourth priority: IP
-- Fourth priority: IP + Group
if not server then
for index, node in pairs(nodes) do
if config.currentNode.address then
if node.address then
if node.address == config.currentNode.address then
if node.address == config.currentNode.address and node.group == config.currentNode.group then
if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fourth Matching node:") .. " " .. node.remarks)
end
@ -2113,12 +2110,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- Fifth priority: remarks
-- Fifth priority: remarks + Group
if not server then
for index, node in pairs(nodes) do
if config.currentNode.remarks then
if node.remarks then
if node.remarks == config.currentNode.remarks then
if node.remarks == config.currentNode.remarks and node.group == config.currentNode.group then
if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fifth Matching node:") .. " " .. node.remarks)
end

View File

@ -22,7 +22,7 @@ do
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 &
[ "$(expr "$CFG_UPDATE_INT" % "$stop_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG stop > /dev/null 2>&1 & }
}
fi
@ -31,7 +31,7 @@ do
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 &
[ "$(expr "$CFG_UPDATE_INT" % "$start_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG start > /dev/null 2>&1 & }
}
fi
@ -40,7 +40,7 @@ do
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 &
[ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG restart > /dev/null 2>&1 & }
}
fi
@ -50,7 +50,7 @@ do
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 &
[ "$(expr "$CFG_UPDATE_INT" % "$hourupdate")" -eq 0 ] && { lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 & }
}
fi
@ -73,7 +73,7 @@ do
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 &
[ "$(expr "$CFG_UPDATE_INT" % "$hour_update")" -eq 0 ] && { lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & }
}
done

View File

@ -49,11 +49,13 @@ url_test_node() {
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
sleep 2s
local curlx="socks5h://127.0.0.1:${_tmp_port}"
local url=$(config_t_get global_other url_test_url https://www.google.com/generate_204)
result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "${url}")
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}*.json
local probeUrl=$(config_t_get global_other 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/etc/${CONFIG}/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/etc/${CONFIG}/*url_test_${node_id}*.*
}
echo $result
}

View File

@ -412,7 +412,7 @@ run_process_queue() {
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=$(pgrep -f "$(echo $cmd_check)" | wc -l)
icount=$(busybox pgrep -f "$(echo $cmd_check)" | wc -l)
if [ $icount = 0 ]; then
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
fi