update 2026-05-18 23:19:08

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

No files matched your search

@@ -262,6 +262,13 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = "2" o.default = "2"
o.placeholder = "2" o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.") 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 ]] end -- [[ 负载均衡 End ]]
if load_iface_options then -- [[ 自定义接口 Start ]] if load_iface_options then -- [[ 自定义接口 Start ]]
@@ -1072,7 +1072,13 @@ function gen_config(var)
type = _node.balancingStrategy, type = _node.balancingStrategy,
settings = { settings = {
expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2, 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 else
+6
View File
@@ -535,6 +535,12 @@ msgstr "优选节点数量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。" 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" msgid "Shunt"
msgstr "分流" msgstr "分流"
+1 -1
View File
@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2 PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.5.1 PKG_VERSION:=26.5.18
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION) PKG_PO_VERSION:=$(PKG_VERSION)
@@ -52,7 +52,7 @@ function index()
entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true 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"}, 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, "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 ]] --[[ Server ]]
entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true 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 if t["node"] == w then
uci:delete(appname, t[".name"]) uci:delete(appname, t[".name"])
end end
local changed = false
local auto_switch_node_list = uci:get(appname, t[".name"], "autoswitch_backup_node") or {} local auto_switch_node_list = uci:get(appname, t[".name"], "autoswitch_backup_node") or {}
for i = #auto_switch_node_list, 1, -1 do for i = #auto_switch_node_list, 1, -1 do
if w == auto_switch_node_list[i] then if w == auto_switch_node_list[i] then
table.remove(auto_switch_node_list, i) table.remove(auto_switch_node_list, i)
changed = true
end end
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) end)
uci:foreach(appname, "haproxy_config", function(t) uci:foreach(appname, "haproxy_config", function(t)
if t["lbss"] == w then if t["lbss"] == w then
@@ -21,27 +21,50 @@ local function _n(name)
end end
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) 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 = s:option(Value, _n("port"), translate("Port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("hop"), translate("Port hopping range")) 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.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o.rewrite_option = o.option 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 = 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.datatype = "or(uinteger,portrange)"
o.placeholder = "30" o.placeholder = "30"
o.default = "30" o.default = "30"
o.rewrite_option = o.option 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.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 = s:option(Value, _n("auth_password"), translate("Auth Password"))
o.password = true o.password = true
o.rewrite_option = o.option 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 = s:option(Flag, _n("fast_open"), translate("Fast Open"))
o.default = "0" o.default = "0"
@@ -164,28 +164,66 @@ if load_balancing_options then -- [[ Load balancing Start ]]
-- Fallback Node -- Fallback Node
o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node")) o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node"))
o.group = {"",""}
o:value("", translate("Close(Not use)")) o:value("", translate("Close(Not use)"))
o:value("_direct", translate("Direct Connection"))
o:depends({ [_n("protocol")] = "_balancing" }) o:depends({ [_n("protocol")] = "_balancing" })
o.template = appname .. "/cbi/nodes_listvalue" o.template = appname .. "/cbi/nodes_listvalue"
o.group = {""} -- Maximum number of fallback nesting layers
local function check_fallback_chain(fb) local MAX_FALLBACK_DEPTH = 3
for k, v in pairs(fallback_list) do -- Check if a loop will form.
if v.fallback == fb then local function will_loop(start_id, target_id, depth)
fallback_list[k] = nil depth = depth or 0
check_fallback_chain(v.id) -- 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
end end
return false
end end
-- Check the fallback chain and remove the balancer node that would form a closed loop. -- Get fallback chain depth
if is_balancer then local function get_fallback_depth(id, depth)
check_fallback_chain(arg[1]) 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 end
for i, v in ipairs(fallback_list) do for _, v in ipairs(fallback_list) do
o:value(v.id, v.remark) local depth = get_fallback_depth(v.id)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default") -- 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 end
for k1, v1 in pairs(node_list) do 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 for i, v in ipairs(v1) do
o:value(v.id, v.remark) o:value(v.id, v.remark)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default") 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.default = "2"
o.placeholder = "2" o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.") 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 ]] end -- [[ Load balancing End ]]
if load_iface_options then -- [[ Custom Interface Start ]] if load_iface_options then -- [[ Custom Interface Start ]]
@@ -299,11 +344,9 @@ o.placeholder = "30"
o.default = "30" o.default = "30"
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o:depends({ [_n("protocol")] = "hysteria2" }) o.password = true
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(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable")) 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 = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o.password = true o:depends({ [_n("protocol")] = "hysteria2" })
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 = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ [_n("protocol")] = "hysteria2"})
@@ -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_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 = s:option(ListValue, _n("protocol"), translate("Protocol"))
o:value("socks", "Socks") o:value("socks", "Socks")
o:value("http", "HTTP") o:value("http", "HTTP")
@@ -388,19 +390,32 @@ end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_hop"), translate("Port hopping range")) 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.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.datatype = "or(uinteger,portrange)"
o.placeholder = "30" o.placeholder = "30"
o.default = "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 = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o:depends({ [_n("protocol")] = "hysteria2" }) 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 = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("protocol")] = "hysteria2" }) 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 = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable")) 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 = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o.password = true 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"}) o:depends({ [_n("protocol")] = "hysteria2"})
end end
@@ -506,7 +533,7 @@ o.default = "0"
o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false }) o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria" }) 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:depends({ [_n("protocol")] = "naive" })
o = s:option(TextValue, _n("ech_config"), translate("ECH Config")) 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 if #protocols > 0 then
for i, v in ipairs(protocols) do for i, v in ipairs(protocols) do
if not v:find("^_") then if not v:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = v }) local depends_condition = { [_n("protocol")] = v }
s.fields[_n("port")]:depends({ [_n("protocol")] = v }) if v == "hysteria2" then
s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v }) depends_condition[_n("hysteria2_realms")] = false
s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v }) 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 end
end end
@@ -836,6 +867,7 @@ if not load_shunt_options then
o:value("1", translate("Preproxy Node")) o:value("1", translate("Preproxy Node"))
end end
o:value("2", translate("Landing Node")) 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 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy."))
o1:depends({ [_n("chain_proxy")] = "1" }) o1:depends({ [_n("chain_proxy")] = "1" })
@@ -22,17 +22,38 @@ o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = 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.rewrite_option = o.option
o:depends({ [_n("custom")] = false }) 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 = s:option(Value, _n("auth_password"), translate("Auth Password"))
o.password = true o.password = true
o.rewrite_option = o.option o.rewrite_option = o.option
o:depends({ [_n("custom")] = false }) 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 = s:option(Flag, _n("udp"), translate("UDP"))
o.default = "1" o.default = "1"
o.rewrite_option = o.option o.rewrite_option = o.option
@@ -129,6 +129,14 @@ o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true o.password = true
o:depends({ [_n("protocol")] = "hysteria2"}) 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 = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" }) 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 = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) 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 ]] ---- [[ TLS ]]
o = s:option(Flag, _n("tls"), translate("TLS")) o = s:option(Flag, _n("tls"), translate("TLS"))
o.default = 0 o.default = 0
@@ -58,6 +58,7 @@ o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false }) o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("auth"), translate("Auth")) o = s:option(Flag, _n("auth"), translate("Auth"))
@@ -157,10 +158,29 @@ if singbox_tags:find("with_quic") then
end end
if singbox_tags:find("with_quic") then 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 = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true o.password = true
o:depends({ [_n("protocol")] = "hysteria2"}) 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 = 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.default = 0
o:depends({ [_n("protocol")] = "hysteria2" }) 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 = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) 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 end
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol")) 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")] = "naive" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" }) 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 = s:option(TextValue, _n("ech_key"), translate("ECH Key"))
o.default = "" o.default = ""
@@ -31,7 +31,11 @@ local type_table = {}
for filename in fs.dir(types_dir) do for filename in fs.dir(types_dir) do
table.insert(type_table, filename) table.insert(type_table, filename)
end 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 for index, value in ipairs(type_table) do
local p_func = loadfile(types_dir .. value) local p_func = loadfile(types_dir .. value)
+42 -7
View File
@@ -377,11 +377,13 @@ function is_special_node(e)
end end
function is_ip(val) function is_ip(val)
val = trim(val):lower()
local str = val:match("%[(.-)%]") or val local str = val:match("%[(.-)%]") or val
return datatypes.ipaddr(str) or false return datatypes.ipaddr(str) or false
end end
function is_ipv6(val) function is_ipv6(val)
val = trim(val):lower()
local str = val:match("%[(.-)%]") or val local str = val:match("%[(.-)%]") or val
return datatypes.ip6addr(str) or false return datatypes.ip6addr(str) or false
end end
@@ -497,9 +499,10 @@ function get_valid_nodes()
end end
end end
local port = e.port or e.hysteria_hop or e.hysteria2_hop 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 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 if (e.type == "sing-box" or e.type == "Xray") and e.protocol then
local protocol = e.protocol local protocol = e.protocol
if protocol == "vmess" then if protocol == "vmess" then
@@ -526,10 +529,13 @@ function get_valid_nodes()
type_name = type_name .. " " .. protocol type_name = type_name .. " " .. protocol
end end
if is_ipv6(address) then address = get_ipv6_full(address) 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}) e["remark"] = trim("%s:[%s]" % {type_name, e.remarks})
if show_node_info == "1" then if show_node_info == "1" then
port = port:gsub(":", "-") port = (port or ""):gsub(":", "-")
e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port}) if not is_realm then
e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port})
end
end end
e.node_type = "normal" e.node_type = "normal"
if not e.group or e.group == "" then if not e.group or e.group == "" then
@@ -619,7 +625,10 @@ function get_node_remarks(n)
end end
type_name = type_name .. " " .. protocol type_name = type_name .. " " .. protocol
end 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
end end
return remarks return remarks
@@ -1204,7 +1213,7 @@ function to_move(app_name,file)
} }
end 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 if flag == 0 then
sys.call("/etc/init.d/passwall2 stop") sys.call("/etc/init.d/passwall2 stop")
end end
@@ -1587,4 +1596,30 @@ function get_dnsmasq_server_domain()
end end
end end
return dnsmasq_server_t 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
@@ -5,15 +5,24 @@ local jsonc = api.jsonc
function gen_config_server(node) function gen_config_server(node)
local config = { 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 = { tls = {
cert = node.tls_certificateFile, cert = node.tls_certificateFile,
key = node.tls_keyFile, key = node.tls_keyFile,
}, },
obfs = (node.hysteria2_obfs) and { obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = "salamander", type = "salamander",
salamander = { salamander = {
password = node.hysteria2_obfs password = node.hysteria2_obfs_password
} }
} or nil, } or nil,
auth = { auth = {
@@ -26,6 +35,9 @@ function gen_config_server(node)
} or nil, } or nil,
ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false, ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false,
disableUDP = (node.hysteria2_udp == "0") 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 return config
end end
@@ -51,14 +63,23 @@ function gen_config(var)
if api.is_ipv6(server_host) then if api.is_ipv6(server_host) then
server_host = api.get_ipv6_full(server_host) server_host = api.get_ipv6_full(server_host)
end end
local server = server_host .. ":" .. server_port
if (node.hysteria2_hop) then local server = server_host .. (server_port and ":" .. server_port or "")
server = server .. "," .. string.gsub(node.hysteria2_hop, ":", "-")
if node.hysteria2_hop then
server = server .. (server_port and "," or ":") .. string.gsub(node.hysteria2_hop, ":", "-")
end end
local config = { 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 = { transport = {
type = "udp", type = "udp",
udp = node.hysteria2_hop and (function() udp = node.hysteria2_hop and (function()
@@ -83,10 +104,10 @@ function gen_config(var)
return udp return udp
end)() or nil end)() or nil
}, },
obfs = (node.hysteria2_obfs) and { obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = "salamander", type = "salamander",
salamander = { salamander = {
password = node.hysteria2_obfs password = node.hysteria2_obfs_password
} }
} or nil, } or nil,
auth = node.hysteria2_auth_password, auth = node.hysteria2_auth_password,
@@ -134,8 +134,8 @@ function gen_outbound(flag, node, tag, proxy_table)
local run_socks_instance = true local run_socks_instance = true
if proxy_table ~= nil and type(proxy_table) == "table" then if proxy_table ~= nil and type(proxy_table) == "table" then
proxy_tag = proxy_table.tag or nil proxy_tag = proxy_table.tag or nil
fragment = proxy_table.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 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 run_socks_instance = proxy_table.run_socks_instance
end end
@@ -250,7 +250,7 @@ function gen_outbound(flag, node, tag, proxy_table)
enabled = true, enabled = true,
disable_sni = (node.tls_disable_sni == "1") and true or false, -- Do not send the server name in ClientHello. 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. 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. 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", --max_version = "1.3",
fragment = fragment, fragment = fragment,
@@ -506,6 +506,7 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range) table.insert(server_ports, range)
end end
end end
result.server_port = nil
end end
protocol_table = { protocol_table = {
server_ports = next(server_ports) and server_ports or nil, 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, obfs = node.hysteria_obfs,
auth = (node.hysteria_auth_type == "base64") and node.hysteria_auth_password or nil, 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, 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_conn = tonumber(node.hysteria_recv_window_conn), --1.14 将变更为 stream_receive_window
recv_window = tonumber(node.hysteria_recv_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, disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 将变更为 disable_path_mtu_discovery
tls = tls tls = tls
} }
end end
@@ -545,13 +546,12 @@ function gen_outbound(flag, node, tag, proxy_table)
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s", heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
tls = tls tls = tls
} }
if node.tuic_alpn and node.tuic_alpn ~= "default" then node.tuic_alpn = (node.tuic_alpn and node.tuic_alpn ~= "default") and node.tuic_alpn or "h3"
local alpn = {} local alpn = {}
string.gsub(node.tuic_alpn, '[^,]+', function(w) string.gsub(node.tuic_alpn, '[^,]+', function(w)
table.insert(alpn, w) table.insert(alpn, w)
end) end)
if #alpn > 0 then protocol_table.tls.alpn = alpn end if #alpn > 0 then protocol_table.tls.alpn = alpn end
end
end end
if node.protocol == "hysteria2" then if node.protocol == "hysteria2" then
@@ -563,6 +563,10 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range) table.insert(server_ports, range)
end end
end end
result.server_port = nil
end
if node.hysteria2_realms then
server_ports = {}
end end
local interval, interval_max local interval, interval_max
if next(server_ports) then if next(server_ports) then
@@ -595,7 +599,29 @@ function gen_outbound(flag, node, tag, proxy_table)
password = node.hysteria2_obfs_password password = node.hysteria2_obfs_password
} or nil, } or nil,
password = node.hysteria2_auth_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 end
@@ -881,10 +907,10 @@ function gen_config_server(node)
auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil, 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_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, 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, 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, disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 to disable_path_mtu_discover
tls = tls tls = tls
} }
end end
@@ -928,7 +954,17 @@ function gen_config_server(node)
} }
}, },
ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false, 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 end
@@ -1312,7 +1348,6 @@ function gen_config(var)
else else
ut_nodes = _node.urltest_node ut_nodes = _node.urltest_node
end end
if #ut_nodes == 0 then return nil end
local valid_nodes = {} local valid_nodes = {}
for i = 1, #(ut_nodes or {}) do for i = 1, #(ut_nodes or {}) do
local ut_node_id = ut_nodes[i] local ut_node_id = ut_nodes[i]
@@ -2051,7 +2086,7 @@ function gen_config(var)
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w) string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w) sys.call("ipset -q -F " .. w)
end) 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}'") local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'")
string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call("ipset -q -F " .. w) sys.call("ipset -q -F " .. w)
@@ -2070,7 +2105,7 @@ function gen_config(var)
end) end)
local family = "inet" local family = "inet"
local table_name = "passwall2" 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}'") 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) 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)) 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 if not value.detour and value.server then
value.detour = "direct" value.detour = "direct"
end 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 value.domain_resolver = nil
end end
for k, v in pairs(config.outbounds[index]) do for k, v in pairs(config.outbounds[index]) do
@@ -794,6 +794,9 @@ function gen_config_server(node)
config.outbounds[index][k] = nil config.outbounds[index][k] = nil
end end
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 end
return config return config
@@ -1049,10 +1052,10 @@ function gen_config(var)
if #valid_nodes == 0 then return nil end if #valid_nodes == 0 then return nil end
-- fallback node -- fallback node
local fallback_node_tag = nil
local fallback_node_id = _node.fallback_node local fallback_node_id = _node.fallback_node
if not fallback_node_id or fallback_node_id == "" then fallback_node_id = nil end fallback_node_id = (fallback_node_id and fallback_node_id ~= "") and fallback_node_id or nil
if fallback_node_id then 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 local is_new_node = true
for _, outbound in ipairs(outbounds) do for _, outbound in ipairs(outbounds) do
if string.sub(outbound.tag, 1, #fallback_node_id) == fallback_node_id then if string.sub(outbound.tag, 1, #fallback_node_id) == fallback_node_id then
@@ -1082,7 +1085,13 @@ function gen_config(var)
type = _node.balancingStrategy, type = _node.balancingStrategy,
settings = { settings = {
expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2, 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 else
@@ -1264,9 +1273,9 @@ function gen_config(var)
interface = node.iface interface = node.iface
} }
}, },
settings = { settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check
finalRules = {{ action = "allow" }} 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)) sys.call(string.format("mkdir -p %s && touch %s/%s", api.TMP_IFACE_PATH, api.TMP_IFACE_PATH, node.iface))
end end
@@ -1806,7 +1815,7 @@ function gen_config(var)
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w) string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w) sys.call("ipset -q -F " .. w)
end) 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}'") local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'")
string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call("ipset -q -F " .. w) sys.call("ipset -q -F " .. w)
@@ -1825,7 +1834,7 @@ function gen_config(var)
end) end)
local family = "inet" local family = "inet"
local table_name = "passwall2" 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}'") 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) 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)) 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 end
table.insert(outbounds, { 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 = { local config = {
@@ -35,22 +35,48 @@ local appname = api.appname
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
//<![CDATA[ //<![CDATA[
var shunt_list = JSON.parse('<%=self.shunt_list%>'); const shunt_list = JSON.parse('<%=self.shunt_list%>');
function go() { function to_edit_node(btn) {
var _status = document.getElementsByClassName('_status'); if (!btn) return;
for (var i = 0; i < _status.length; i++) { const idReg = /^cbid\..*\..*node$/;
var id = _status[i].getAttribute("socks_id"); 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")%>', { XHR.get('<%=api.url("socks_status")%>', {
index: i, index: i,
id: id id: id
}, },
function(x, result) { function(x, result) {
var index = result.index; const index = result.index;
var div = ''; let div = '';
var div1 = '<font style="font-weight:bold;" color="green">✓</font>&nbsp'; const div1 = '<font style="font-weight:bold;" color="green">✓</font>&nbsp';
var div2 = '<font style="font-weight:bold;" color="red">X</font>&nbsp'; const div2 = '<font style="font-weight:bold;" color="red">X</font>&nbsp';
if (result.socks_status) { if (result.socks_status) {
div += div1; div += div1;
} else { } else {
@@ -67,83 +93,112 @@ local appname = api.appname
} }
); );
} }
var global_id = null; let global_id = null;
var global = document.getElementById("cbi-<%=appname%>-global"); const global = document.getElementById("cbi-<%=appname%>-global");
if (global) { if (global) {
var node = global.getElementsByClassName("cbi-section-node")[0]; // Add an "Edit" placeholder to the routing rule node in advance to ensure left alignment of the node list.
var node_id = node.getAttribute("id"); document.querySelectorAll('.td[data-widget="nodes_listvalue"][data-name="_node"] > div').forEach(container => {
global_id = node_id; if (!container.querySelector('.shunt-node-placeholder')) {
var all_node = node.querySelectorAll("[id]"); const placeholder = document.createElement('div');
var reg1 = /^cbid\..*node\.main$/; 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++) { await delay(100);
var el = all_node[i];
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; if (!reg1.test(el.id)) continue;
var node_select = el; const node_select = el;
if (!node_select) continue; if (!node_select) continue;
var cbid = el.id.replace(/\.main$/, ""); const cbid = el.id.replace(/\.main$/, "");
var hidden_select = document.getElementById(cbid); const hidden_select = document.getElementById(cbid);
var node_select_value = hidden_select ? hidden_select.options[0].value : ""; 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) { if (!node_select_value || node_select_value.indexOf("_default") === 0 || node_select_value.indexOf("_direct") === 0 || node_select_value.indexOf("_blackhole") === 0) {
continue; continue;
} }
var to_url = '<%=api.url("node_config")%>/' + node_select_value; let html = '<a href="#" onclick="return to_edit_node(this);"><%:Edit%></a>';
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>';
var m = cbid.match(/\.node$/); const m = cbid.match(/\.node$/);
if (m) { if (m) {
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_redir_log")%>?id=default&name=' + 'global' + '\', \'_blank\')"><%:Log%></a>'; html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_redir_log")%>?id=default&name=' + 'global' + '\', \'_blank\')"><%:Log%></a>';
} }
node_select.insertAdjacentHTML("beforeend", html = '<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">' + html + '</div>'
'<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) { if (socks) {
var socks_enabled_dom = document.getElementById(global_id + "-socks_enabled"); const socks_enabled_dom = document.getElementById(global_id + "-socks_enabled");
socks_enabled_dom.parentNode.removeChild(socks_enabled_dom); const descr = socks.getElementsByClassName("cbi-section-descr")[0];
var descr = socks.getElementsByClassName("cbi-section-descr")[0]; descr.replaceWith(socks_enabled_dom);
descr.outerHTML = socks_enabled_dom.outerHTML; const rows = socks.getElementsByClassName("cbi-section-table-row");
rows = socks.getElementsByClassName("cbi-section-table-row"); for (let i = 0; i < rows.length; i++) {
for (var i = 0; i < rows.length; i++) {
try { try {
var row = rows[i]; const row = rows[i];
var id = row.id; const id = row.id;
if (!id) continue; if (!id) continue;
var dom_id = id + "-node"; let dom_id = id + "-node";
var cbid = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), "."); const cbid = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
dom_id = cbid + ".main"; dom_id = cbid + ".main";
var node_select = document.getElementById(dom_id); const node_select = document.getElementById(dom_id);
if (!node_select) continue; if (!node_select) continue;
var hidden_select = document.getElementById(cbid);; let html = '<a href="#" onclick="return to_edit_node(this);"><%:Edit%></a>';
var node_select_value = hidden_select ? hidden_select.options[0].value : ""; html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_socks_log")%>?name=' + id.replace("cbi-<%=appname%>-", "") + '\', \'_blank\')"><%:Log%></a>';
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>';
node_select.insertAdjacentHTML("afterend", node_select.insertAdjacentHTML("afterend",
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">' '<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>' + html + '</div>'
); );
}
} catch(err) { } 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(() => { document.addEventListener("DOMContentLoaded", () => setTimeout(() => {
const hiddenSelect = document.getElementById("cbid.<%=appname%>.<%=self.global_cfgid%>.node"); const hiddenSelect = document.getElementById("cbid.<%=appname%>.<%=self.global_cfgid%>.node");
let o_val = hiddenSelect.value let o_val = hiddenSelect.value
@@ -184,8 +239,6 @@ local appname = api.appname
shunt_taboption.appendChild(shunt_option_list); shunt_taboption.appendChild(shunt_option_list);
} }
} }
go();
}, 100)); }, 100));
//setTimeout("go()", 1000);
//]]> //]]>
</script> </script>
@@ -602,10 +602,10 @@ local current_node = map:get(section)
if (v_type === "Hysteria2") { if (v_type === "Hysteria2") {
v_password = opt.get("hysteria2_auth_password"); 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 != "") { if (dom_obfs && dom_obfs.value != "") {
params += "&obfs=" + "salamander"; params += "&obfs=" + "salamander";
params += opt.query("obfs-password", "hysteria2_obfs"); params += opt.query("obfs-password", "hysteria2_obfs_password");
} }
params += opt.query("mport", "hysteria2_hop"); params += opt.query("mport", "hysteria2_hop");
} else { } else {
@@ -1621,7 +1621,8 @@ local current_node = map:get(section)
dom_prefix = "hysteria2_" dom_prefix = "hysteria2_"
opt.set(dom_prefix + 'auth_password', decodeURIComponent(password)); opt.set(dom_prefix + 'auth_password', decodeURIComponent(password));
if (queryParam["obfs-password"] || queryParam["obfs_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 || ""); 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_congestion_control', queryParam.congestion_control || 'cubic');
opt.set(dom_prefix + 'tuic_udp_relay_mode', queryParam.udp_relay_mode || 'native'); 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_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'tls_disable_sni', queryParam.disable_sni === "1"); opt.set(dom_prefix + 'tls_disable_sni', queryParam.disable_sni === "1");
opt.set( opt.set(
@@ -911,7 +911,11 @@ table td, .table .td {
} else { } else {
port_s = o["hysteria_hop"] || o["hysteria2_hop"]; 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; return str;
} }
+8 -2
View File
@@ -46,8 +46,8 @@ msgstr "لیست قوانین"
msgid "Access control" msgid "Access control"
msgstr "کنترل دسترسی" msgstr "کنترل دسترسی"
msgid "Watch Logs" msgid "Runtime Logs"
msgstr "مشاهده گزارش‌ها" msgstr "گزارش‌های زمان اجرا"
msgid "Node Config" msgid "Node Config"
msgstr "پیکربندی گره" msgstr "پیکربندی گره"
@@ -437,6 +437,12 @@ msgstr "تعداد گره‌های ترجیحی"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "توزیع‌کننده بار تعداد بهینه‌ای از گره‌ها را انتخاب می‌کند و ترافیک به طور تصادفی بین آن‌ها توزیع می‌شود." 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" msgid "Shunt"
msgstr "شنت (Shunt)" msgstr "شنت (Shunt)"
+8 -2
View File
@@ -46,8 +46,8 @@ msgstr "Список правил"
msgid "Access control" msgid "Access control"
msgstr "Контроль доступа" msgstr "Контроль доступа"
msgid "Watch Logs" msgid "Runtime Logs"
msgstr "Просмотр журнала" msgstr "Журналы выполнения"
msgid "Node Config" msgid "Node Config"
msgstr "Параметры узла" msgstr "Параметры узла"
@@ -433,6 +433,12 @@ msgstr "Количество предпочтительных узлов"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "Балансировщик выбирает указанное количество наилучших узлов и распределяет трафик между ними случайным образом." 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" msgid "Shunt"
msgstr "Shunt (разделение трафика)" msgstr "Shunt (разделение трафика)"
+8 -2
View File
@@ -46,8 +46,8 @@ msgstr "规则列表"
msgid "Access control" msgid "Access control"
msgstr "访问控制" msgstr "访问控制"
msgid "Watch Logs" msgid "Runtime Logs"
msgstr "查看日志" msgstr "运行日志"
msgid "Node Config" msgid "Node Config"
msgstr "节点配置" msgstr "节点配置"
@@ -433,6 +433,12 @@ msgstr "优选节点数量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。" 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" msgid "Shunt"
msgstr "分流" msgstr "分流"
+8 -2
View File
@@ -46,8 +46,8 @@ msgstr "規則列表"
msgid "Access control" msgid "Access control"
msgstr "訪問控制" msgstr "訪問控制"
msgid "Watch Logs" msgid "Runtime Logs"
msgstr "查看日誌" msgstr "運行日誌"
msgid "Node Config" msgid "Node Config"
msgstr "節點配置" msgstr "節點配置"
@@ -433,6 +433,12 @@ msgstr "優選節點數量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "負載均衡器選出最優節點的個數,流量將在這几個節點中随機分配。" 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" msgid "Shunt"
msgstr "分流" msgstr "分流"
@@ -12,7 +12,7 @@
if [ "$ACTION" = "ifupdate" ]; then if [ "$ACTION" = "ifupdate" ]; then
USE_TABLES=$(get_cache_var "USE_TABLES") 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 exit 0
fi fi
@@ -84,7 +84,7 @@ run_xray() {
json_add_string "loglevel" "${loglevel}" json_add_string "loglevel" "${loglevel}"
[ -n "$flag" ] && { [ -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}" json_add_string "flag" "${flag}"
} }
[ -n "$socks_address" ] && [ -n "$socks_port" ] && { [ -n "$socks_address" ] && [ -n "$socks_port" ] && {
@@ -112,12 +112,12 @@ run_xray() {
local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf 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 [ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf
if [ "${nftflag}" = "1" ]; then if [ "${nftflag}" = "1" ]; then
local direct_nftset4="passwall2_${node}_white" local direct_nftset4="psw2_${node}_white"
local direct_nftset6="passwall2_${node}_white6" local direct_nftset6="psw2_${node}_white6"
local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}" local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}"
else else
local direct_ipset4="passwall2_${node}_white" local direct_ipset4="psw2_${node}_white"
local direct_ipset6="passwall2_${node}_white6" local direct_ipset6="psw2_${node}_white6"
local direct_ipset="${direct_ipset4},${direct_ipset6}" local direct_ipset="${direct_ipset4},${direct_ipset6}"
fi 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} 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}" json_add_string "loglevel" "${loglevel}"
[ -n "$flag" ] && { [ -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}" json_add_string "flag" "${flag}"
} }
[ -n "$socks_address" ] && [ -n "$socks_port" ] && { [ -n "$socks_address" ] && [ -n "$socks_port" ] && {
@@ -251,12 +251,12 @@ run_singbox() {
local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf 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 [ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf
if [ "${nftflag}" = "1" ]; then if [ "${nftflag}" = "1" ]; then
local direct_nftset4="passwall2_${node}_white" local direct_nftset4="psw2_${node}_white"
local direct_nftset6="passwall2_${node}_white6" local direct_nftset6="psw2_${node}_white6"
local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}" local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}"
else else
local direct_ipset4="passwall2_${node}_white" local direct_ipset4="psw2_${node}_white"
local direct_ipset6="passwall2_${node}_white6" local direct_ipset6="psw2_${node}_white6"
local direct_ipset="${direct_ipset4},${direct_ipset6}" local direct_ipset="${direct_ipset4},${direct_ipset6}"
fi 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} 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
fi fi
if [ -n "${error_msg}" ] && [ "$(config_n_get $node hysteria2_realms)" = "1" ]; then
unset error_msg
fi
[ -n "${error_msg}" ] && { [ -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}")" [ "$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 return 1
@@ -555,7 +559,7 @@ socks_node_switch() {
[ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1 [ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1
done 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 for prefix in "" "HTTP_" "HTTP2"; do
rm -rf "$TMP_PATH/${prefix}SOCKS_${flag}"* rm -rf "$TMP_PATH/${prefix}SOCKS_${flag}"*
done done
@@ -791,7 +795,7 @@ start_socks() {
# Auto switch logic # Auto switch logic
local enable_autoswitch=$(config_n_get $id enable_autoswitch 0) 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 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}/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 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 rm -rf /tmp/lock/${CONFIG}_tasks.lock
} }
start_crontab() { start_crontab() {
if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then
start_daemon=$(config_t_get global_delay start_daemon 0) 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 fi
[ -f "/tmp/lock/${CONFIG}_cron.lock" ] && { [ -f "/tmp/lock/${CONFIG}_cron.lock" ] && {
@@ -982,11 +986,11 @@ run_ipset_chinadns_ng() {
[ -n "${ipset}" ] && { [ -n "${ipset}" ] && {
set_names=$ipset set_names=$ipset
vps_set_names="passwall2_vps,passwall2_vps6" vps_set_names="psw2_vps,psw2_vps6"
} }
[ -n "${nftset}" ] && { [ -n "${nftset}" ] && {
set_names=$(echo ${nftset} | awk -F, '{printf "%s,%s", substr($1,3), substr($2,3)}' | sed 's/#/@/g') 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 cat <<-EOF > $config_file
bind-addr 127.0.0.1 bind-addr 127.0.0.1
@@ -1162,7 +1166,7 @@ acl_app() {
} }
start() { 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!" #log_i18n 0 "The program has started. Please stop it and then restart it!"
stop stop
} }
@@ -1241,8 +1245,8 @@ stop() {
kill -9 "$pid" >/dev/null 2>&1 kill -9 "$pid" >/dev/null 2>&1
fi fi
done done
pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1 busybox 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 -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 V2RAY_LOCATION_ASSET
unset XRAY_LOCATION_ASSET unset XRAY_LOCATION_ASSET
unset SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN unset SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN
@@ -8,7 +8,8 @@ listen_port=$2
server_address=$3 server_address=$3
server_port=$4 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 exit 0
} }
@@ -274,7 +274,7 @@ function add_rule(var)
if address == "engage.cloudflareclient.com" then return end if address == "engage.cloudflareclient.com" then return end
if datatypes.hostname(address) then if datatypes.hostname(address) then
set_domain_dns(address, fwd_dns) 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
end end
process_address(t.address) process_address(t.address)
@@ -3,17 +3,17 @@
DIR="$(cd "$(dirname "$0")" && pwd)" DIR="$(cd "$(dirname "$0")" && pwd)"
MY_PATH=$DIR/iptables.sh MY_PATH=$DIR/iptables.sh
UTILS_PATH=$DIR/utils.sh UTILS_PATH=$DIR/utils.sh
IPSET_LOCAL="passwall2_local" IPSET_LOCAL="psw2_local"
IPSET_PROXY_LAN="passwall2_proxy_lan" IPSET_PROXY_LAN="psw2_proxy_lan"
IPSET_LAN="passwall2_lan" IPSET_LAN="psw2_lan"
IPSET_VPS="passwall2_vps" IPSET_VPS="psw2_vps"
IPSET_WAN="passwall2_wan" IPSET_WAN="psw2_wan"
IPSET_LOCAL6="passwall2_local6" IPSET_LOCAL6="psw2_local6"
IPSET_PROXY_LAN6="passwall2_proxy_lan6" IPSET_PROXY_LAN6="psw2_proxy_lan6"
IPSET_LAN6="passwall2_lan6" IPSET_LAN6="psw2_lan6"
IPSET_VPS6="passwall2_vps6" IPSET_VPS6="psw2_vps6"
IPSET_WAN6="passwall2_wan6" IPSET_WAN6="psw2_wan6"
FWMARK="0x50535732" FWMARK="0x50535732"
@@ -194,8 +194,8 @@ gen_shunt_list() {
for shunt_id in $shunt_ids; do for shunt_id in $shunt_ids; do
local shunt_node=$(config_n_get ${node} "${shunt_id}") local shunt_node=$(config_n_get ${node} "${shunt_id}")
[ -n "$shunt_node" ] && { [ -n "$shunt_node" ] && {
local ipset_v4="passwall2_${node}_${shunt_id}" local ipset_v4="psw2_${node}_${shunt_id}"
local ipset_v6="passwall2_${node}_${shunt_id}6" local ipset_v6="psw2_${node}_${shunt_id}6"
ipset -! create $ipset_v4 nethash maxelem 1048576 ipset -! create $ipset_v4 nethash maxelem 1048576
ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576 ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576
local outbound="redirect" local outbound="redirect"
@@ -219,22 +219,14 @@ gen_shunt_list() {
} }
local direct_ipset4=$(get_cache_var "node_${node}_direct_ipset4") local direct_ipset4=$(get_cache_var "node_${node}_direct_ipset4")
[ -n "${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" _SHUNT_LIST4="${_SHUNT_LIST4} ${direct_ipset4}:direct"
} }
local direct_ipset6=$(get_cache_var "node_${node}_direct_ipset6") local direct_ipset6=$(get_cache_var "node_${node}_direct_ipset6")
[ -n "${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" _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_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\"
[ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\" [ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\"
@@ -618,6 +610,8 @@ filter_direct_node_list() {
} }
update_wan_sets() { update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4) local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && { [ -n "$WAN_IP" ] && {
ipset -F "$IPSET_WAN" ipset -F "$IPSET_WAN"
@@ -641,13 +635,13 @@ add_firewall_rule() {
ipset -! create $IPSET_LOCAL nethash maxelem 1048576 ipset -! create $IPSET_LOCAL nethash maxelem 1048576
ipset -! create $IPSET_PROXY_LAN nethash maxelem 1048576 ipset -! create $IPSET_PROXY_LAN nethash maxelem 1048576
ipset -! create $IPSET_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_WAN nethash maxelem 1048576
ipset -! create $IPSET_LOCAL6 nethash family inet6 maxelem 1048576 ipset -! create $IPSET_LOCAL6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_PROXY_LAN6 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_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 ipset -! create $IPSET_WAN6 nethash family inet6 maxelem 1048576
get_local_ips ip4 | sed "s/^/add $IPSET_LOCAL /" | ipset -! -R get_local_ips ip4 | sed "s/^/add $IPSET_LOCAL /" | ipset -! -R
@@ -1025,7 +1019,7 @@ del_firewall_rule() {
flush_ipset() { flush_ipset() {
log_i18n 0 "Clear %s." "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} destroy_ipset ${_name}
done done
} }
@@ -1105,7 +1099,7 @@ start() {
} }
stop() { stop() {
[ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh [ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH"
del_firewall_rule del_firewall_rule
destroy_ipset $IPSET_PROXY_LAN destroy_ipset $IPSET_PROXY_LAN
destroy_ipset $IPSET_PROXY_LAN6 destroy_ipset $IPSET_PROXY_LAN6
@@ -18,7 +18,7 @@ while [ "$ENABLED" -eq 1 ]; do
for filename in $(ls ${TMP_SCRIPT_FUNC_PATH} | grep -v "^_"); do for filename in $(ls ${TMP_SCRIPT_FUNC_PATH} | grep -v "^_"); do
cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename}) cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename})
cmd_check=$(echo $cmd | awk -F '>' '{print $1}') 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 if [ $icount = 0 ]; then
#echo "${cmd} crashed, restarting." >> /tmp/log/passwall2.log #echo "${cmd} crashed, restarting." >> /tmp/log/passwall2.log
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 & eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
@@ -4,17 +4,17 @@ DIR="$(cd "$(dirname "$0")" && pwd)"
MY_PATH=$DIR/nftables.sh MY_PATH=$DIR/nftables.sh
UTILS_PATH=$DIR/utils.sh UTILS_PATH=$DIR/utils.sh
NFTABLE_NAME="inet passwall2" NFTABLE_NAME="inet passwall2"
NFTSET_LOCAL="passwall2_local" NFTSET_LOCAL="psw2_local"
NFTSET_PROXY_LAN="passwall2_proxy_lan" NFTSET_PROXY_LAN="psw2_proxy_lan"
NFTSET_LAN="passwall2_lan" NFTSET_LAN="psw2_lan"
NFTSET_VPS="passwall2_vps" NFTSET_VPS="psw2_vps"
NFTSET_WAN="passwall2_wan" NFTSET_WAN="psw2_wan"
NFTSET_LOCAL6="passwall2_local6" NFTSET_LOCAL6="psw2_local6"
NFTSET_PROXY_LAN6="passwall2_proxy_lan6" NFTSET_PROXY_LAN6="psw2_proxy_lan6"
NFTSET_LAN6="passwall2_lan6" NFTSET_LAN6="psw2_lan6"
NFTSET_VPS6="passwall2_vps6" NFTSET_VPS6="psw2_vps6"
NFTSET_WAN6="passwall2_wan6" NFTSET_WAN6="psw2_wan6"
FWMARK="0x50535732" FWMARK="0x50535732"
@@ -231,8 +231,8 @@ gen_shunt_list() {
for shunt_id in $shunt_ids; do for shunt_id in $shunt_ids; do
local shunt_node=$(config_n_get ${node} "${shunt_id}") local shunt_node=$(config_n_get ${node} "${shunt_id}")
[ -n "$shunt_node" ] && { [ -n "$shunt_node" ] && {
local nftset_v4="passwall2_${node}_${shunt_id}" local nftset_v4="psw2_${node}_${shunt_id}"
local nftset_v6="passwall2_${node}_${shunt_id}6" local nftset_v6="psw2_${node}_${shunt_id}6"
gen_nftset $nftset_v4 ipv4_addr 0 0 gen_nftset $nftset_v4 ipv4_addr 0 0
gen_nftset $nftset_v6 ipv6_addr 0 0 gen_nftset $nftset_v6 ipv6_addr 0 0
local outbound="redirect" local outbound="redirect"
@@ -263,14 +263,6 @@ gen_shunt_list() {
gen_nftset $direct_nftset6 ipv6_addr 0 0 gen_nftset $direct_nftset6 ipv6_addr 0 0
_SHUNT_LIST6="${_SHUNT_LIST6} ${direct_nftset6}:direct" _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_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\"
[ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\" [ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\"
@@ -681,6 +673,8 @@ mwan3_start() {
} }
update_wan_sets() { update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4) local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && { [ -n "$WAN_IP" ] && {
nft flush set $NFTABLE_NAME $NFTSET_WAN nft flush set $NFTABLE_NAME $NFTSET_WAN
@@ -1073,7 +1067,7 @@ del_firewall_rule() {
flush_nftset() { flush_nftset() {
log_i18n 0 "Clear %s." "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} destroy_nftset ${_name}
done done
} }
@@ -1116,7 +1110,7 @@ start() {
} }
stop() { stop() {
[ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh [ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH"
del_firewall_rule del_firewall_rule
destroy_nftset $NFTSET_PROXY_LAN destroy_nftset $NFTSET_PROXY_LAN
destroy_nftset $NFTSET_PROXY_LAN6 destroy_nftset $NFTSET_PROXY_LAN6
@@ -6,7 +6,7 @@ APP_FILE=${APP_PATH}/app.sh
flag=0 flag=0
check_process() { 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 sleep 6s
done done
} }
@@ -63,7 +63,7 @@ test_node() {
# Kill the SS plugin process # Kill the SS plugin process
local pid_file="/tmp/etc/${CONFIG}/test_node_${node_id}_plugin.pid" 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 [ -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}*.* rm -rf /tmp/etc/${CONFIG}/test_node_${node_id}*.*
if [ "${_proxy_status}" -eq 200 ]; then if [ "${_proxy_status}" -eq 200 ]; then
return 0 return 0
@@ -1688,22 +1688,19 @@ local function processData(szType, content, add_mode, group, sub_cfg)
local insecure = params.allowinsecure or params.insecure 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.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.hysteria2_hop = params.mport 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 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 local is_singbox = sub_hysteria2_type == "sing-box" and has_singbox
result.type = is_singbox and 'sing-box' or 'Xray' result.type = is_singbox and 'sing-box' or 'Xray'
result.protocol = "hysteria2" 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.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 result.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil
elseif has_hysteria2 then elseif has_hysteria2 then
result.type = "Hysteria2" result.type = "Hysteria2"
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs = params["obfs-password"] or params["obfs_password"]
end
else 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")) 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 return nil
@@ -1761,7 +1758,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end end
result.tls_serverName = params.sni result.tls_serverName = params.sni
result.tls_disable_sni = params.disable_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_congestion_control = params.congestion_control or "cubic"
result.tuic_udp_relay_mode = params.udp_relay_mode or "native" result.tuic_udp_relay_mode = params.udp_relay_mode or "native"
local insecure = params.allowinsecure or params.insecure or params.allow_insecure 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 end
end end
-- First priority: Type + Notes + IP + Port -- First priority: Type + Notes + IP + Port + Group
if not server then if not server then
for index, node in pairs(nodes) do 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 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 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 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) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("First Matching node:") .. " " .. node.remarks)
end end
@@ -2065,12 +2062,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- Second priority: Type + IP + Port -- Second priority: Type + IP + Port + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.type and config.currentNode.address and config.currentNode.port then 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 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 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) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Second Matching node:") .. " " .. node.remarks)
end end
@@ -2081,12 +2078,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- Third priority: IP + Port -- Third priority: IP + Port + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.address and config.currentNode.port then if config.currentNode.address and config.currentNode.port then
if node.address and node.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 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) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Third Matching node:") .. " " .. node.remarks)
end end
@@ -2097,12 +2094,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- Fourth priority: IP -- Fourth priority: IP + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.address then if config.currentNode.address then
if node.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 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) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fourth Matching node:") .. " " .. node.remarks)
end end
@@ -2113,12 +2110,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- Fifth priority: remarks -- Fifth priority: remarks + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.remarks then if config.currentNode.remarks then
if node.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 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) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fifth Matching node:") .. " " .. node.remarks)
end end
@@ -22,7 +22,7 @@ do
stop_interval_mode=$(expr "$stop_interval_mode" \* 60) stop_interval_mode=$(expr "$stop_interval_mode" \* 60)
if [ -n "$stop_week_mode" ]; then if [ -n "$stop_week_mode" ]; then
[ "$stop_week_mode" = "8" ] && { [ "$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 fi
@@ -31,7 +31,7 @@ do
start_interval_mode=$(expr "$start_interval_mode" \* 60) start_interval_mode=$(expr "$start_interval_mode" \* 60)
if [ -n "$start_week_mode" ]; then if [ -n "$start_week_mode" ]; then
[ "$start_week_mode" = "8" ] && { [ "$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 fi
@@ -40,7 +40,7 @@ do
restart_interval_mode=$(expr "$restart_interval_mode" \* 60) restart_interval_mode=$(expr "$restart_interval_mode" \* 60)
if [ -n "$restart_week_mode" ]; then if [ -n "$restart_week_mode" ]; then
[ "$restart_week_mode" = "8" ] && { [ "$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 fi
@@ -50,7 +50,7 @@ do
hourupdate=$(expr "$hourupdate" \* 60) hourupdate=$(expr "$hourupdate" \* 60)
if [ "$autoupdate" = "1" ]; then if [ "$autoupdate" = "1" ]; then
[ "$weekupdate" = "8" ] && { [ "$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 fi
@@ -73,7 +73,7 @@ do
hour_update=$(expr "$hour_update" \* 60) hour_update=$(expr "$hour_update" \* 60)
cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g') cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g')
[ "$week_update" = "8" ] && { [ "$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 done
@@ -49,11 +49,13 @@ url_test_node() {
local _tmp_port=$(get_new_port 48900 tcp,udp) 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 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 sleep 2s
local curlx="socks5h://127.0.0.1:${_tmp_port}" local probeUrl=$(config_t_get global_other url_test_url https://www.google.com/generate_204)
local url=$(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}")
result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "${url}") # End the SS plugin process
pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 local pid_file="/tmp/etc/${CONFIG}/url_test_${node_id}_plugin.pid"
rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.json [ -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 echo $result
} }
@@ -412,7 +412,7 @@ run_process_queue() {
for filename in $(ls ${TMP_PROCESS_LIST_PATH}); do for filename in $(ls ${TMP_PROCESS_LIST_PATH}); do
cmd=$(cat ${TMP_PROCESS_LIST_PATH}/${filename}) cmd=$(cat ${TMP_PROCESS_LIST_PATH}/${filename})
cmd_check=$(echo $cmd | awk -F '>' '{print $1}') 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 if [ $icount = 0 ]; then
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 & eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
fi fi