diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua index 1ee689e7..e1afc603 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua @@ -262,6 +262,13 @@ if load_balancing_options then -- [[ Load balancing Start ]] o.default = "2" o.placeholder = "2" o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.") + + o = s:option(Value, _n("tolerance"), translate("Failure Tolerance (%)")) + o:depends({ [_n("balancingStrategy")] = "leastLoad" }) + o.datatype = "uinteger" + o.default = "10" + o.placeholder = "10" + o.description = translate("The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate.") end -- [[ 负载均衡 End ]] if load_iface_options then -- [[ 自定义接口 Start ]] diff --git a/luci-app-passwall/luasrc/passwall/util_xray.lua b/luci-app-passwall/luasrc/passwall/util_xray.lua index 4cc49322..7e04f06c 100644 --- a/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -1072,7 +1072,13 @@ function gen_config(var) type = _node.balancingStrategy, settings = { expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2, - maxRTT = "1s" + maxRTT = "1s", + tolerance = (function(t) + t = tonumber(t) or 0 + if t < 1 then return nil end + if t > 100 then t = 100 end + return t / 100 + end)(_node.tolerance) } } else diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po index a48e28c0..aa3f0662 100644 --- a/luci-app-passwall/po/zh-cn/passwall.po +++ b/luci-app-passwall/po/zh-cn/passwall.po @@ -535,6 +535,12 @@ msgstr "优选节点数量" msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。" +msgid "Failure Tolerance (%)" +msgstr "测速失败率容忍度(%)" + +msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate." +msgstr "最多可接受的测速失败率,例如 1 表示允许 1% 的测速失败率。" + msgid "Shunt" msgstr "分流" diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index 3d14cf26..cb2f0820 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 -PKG_VERSION:=26.5.1 +PKG_VERSION:=26.5.18 PKG_RELEASE:=1 PKG_PO_VERSION:=$(PKG_VERSION) diff --git a/luci-app-passwall2/luasrc/controller/passwall2.lua b/luci-app-passwall2/luasrc/controller/passwall2.lua index 373207ee..03d0587f 100644 --- a/luci-app-passwall2/luasrc/controller/passwall2.lua +++ b/luci-app-passwall2/luasrc/controller/passwall2.lua @@ -52,7 +52,7 @@ function index() entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true - entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Watch Logs"), 999).leaf = true + entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Runtime Logs"), 999).leaf = true --[[ Server ]] entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true @@ -452,13 +452,17 @@ function delete_select_nodes() if t["node"] == w then uci:delete(appname, t[".name"]) end + local changed = false local auto_switch_node_list = uci:get(appname, t[".name"], "autoswitch_backup_node") or {} for i = #auto_switch_node_list, 1, -1 do if w == auto_switch_node_list[i] then table.remove(auto_switch_node_list, i) + changed = true end end - uci:set_list(appname, t[".name"], "autoswitch_backup_node", auto_switch_node_list) + if changed then + uci:set_list(appname, t[".name"], "autoswitch_backup_node", auto_switch_node_list) + end end) uci:foreach(appname, "haproxy_config", function(t) if t["lbss"] == w then diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua index 9d05dbbb..f5c66225 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua @@ -21,27 +21,50 @@ local function _n(name) end o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) +o:depends({ [_n("realms")] = false }) o = s:option(Value, _n("port"), translate("Port")) o.datatype = "port" +o:depends({ [_n("realms")] = false }) o = s:option(Value, _n("hop"), translate("Port hopping range")) o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).") o.rewrite_option = o.option +o:depends({ [_n("realms")] = false }) o = s:option(Value, _n("hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5.")) o.datatype = "or(uinteger,portrange)" o.placeholder = "30" o.default = "30" o.rewrite_option = o.option +o:depends({ [_n("realms")] = false }) -o = s:option(Value, _n("obfs"), translate("Obfs Password")) +o = s:option(Flag, _n("realms"), translate("Realms")) +o.default = "0" o.rewrite_option = o.option +o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") +o.rewrite_option = o.option +o:depends({ [_n("realms")] = "1" }) + +o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN")) +o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } +o.rewrite_option = o.option +o:depends({ [_n("realms")] = "1" }) + o = s:option(Value, _n("auth_password"), translate("Auth Password")) o.password = true o.rewrite_option = o.option +o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type")) +o:value("", translate("Disable")) +o:value("salamander") +o.rewrite_option = o.option + +o = s:option(Value, _n("obfs_password"), translate("Obfs Password")) +o.rewrite_option = o.option +o:depends({ [_n("obfs_type")] = "salamander" }) + o = s:option(Flag, _n("fast_open"), translate("Fast Open")) o.default = "0" diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua index cba6733b..012f4952 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua @@ -164,28 +164,66 @@ if load_balancing_options then -- [[ Load balancing Start ]] -- Fallback Node o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node")) + o.group = {"",""} o:value("", translate("Close(Not use)")) + o:value("_direct", translate("Direct Connection")) o:depends({ [_n("protocol")] = "_balancing" }) o.template = appname .. "/cbi/nodes_listvalue" - o.group = {""} - local function check_fallback_chain(fb) - for k, v in pairs(fallback_list) do - if v.fallback == fb then - fallback_list[k] = nil - check_fallback_chain(v.id) + -- Maximum number of fallback nesting layers + local MAX_FALLBACK_DEPTH = 3 + -- Check if a loop will form. + local function will_loop(start_id, target_id, depth) + depth = depth or 0 + -- Recursion stops after the maximum depth is exceeded. + if depth >= MAX_FALLBACK_DEPTH then + return false + end + for _, v in ipairs(fallback_list) do + if v.id == target_id then + local fb = v.fallback + -- No fallback + if not fb or fb == "" or fb == "_direct" then + return false + end + -- Loopback detected + if fb == start_id then + return true + end + -- Continue recursive checking + return will_loop(start_id, fb, depth + 1) end end + return false end - -- Check the fallback chain and remove the balancer node that would form a closed loop. - if is_balancer then - check_fallback_chain(arg[1]) + -- Get fallback chain depth + local function get_fallback_depth(id, depth) + depth = depth or 0 + if depth >= MAX_FALLBACK_DEPTH then + return depth + end + for _, v in ipairs(fallback_list) do + if v.id == id then + local fb = v.fallback + if not fb or fb == "" or fb == "_direct" then + return depth + end + return get_fallback_depth(fb, depth + 1) + end + end + return depth end - for i, v in ipairs(fallback_list) do - o:value(v.id, v.remark) - o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default") + for _, v in ipairs(fallback_list) do + local depth = get_fallback_depth(v.id) + -- Once the maximum number of nested doll layers is exceeded, further selection of the balancer is not allowed. + if depth < MAX_FALLBACK_DEPTH + and not will_loop(arg[1], v.id) + then + o:value(v.id, v.remark) + o.group[#o.group + 1] = (v.group and v.group ~= "") and v.group or translate("default") + end end for k1, v1 in pairs(node_list) do - if k1 == "socks_list" or k1 == "normal_list" then + if k1 == "socks_list" or k1 == "normal_list" or k1 == "urltest_list" then for i, v in ipairs(v1) do o:value(v.id, v.remark) o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default") @@ -221,6 +259,13 @@ if load_balancing_options then -- [[ Load balancing Start ]] o.default = "2" o.placeholder = "2" o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.") + + o = s:option(Value, _n("tolerance"), translate("Failure Tolerance (%)")) + o:depends({ [_n("balancingStrategy")] = "leastLoad" }) + o.datatype = "uinteger" + o.default = "10" + o.placeholder = "10" + o.description = translate("The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate.") end -- [[ Load balancing End ]] if load_iface_options then -- [[ Custom Interface Start ]] @@ -299,11 +344,9 @@ o.placeholder = "30" o.default = "30" o:depends({ [_n("protocol")] = "hysteria2" }) -o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) -o:depends({ [_n("protocol")] = "hysteria2" }) - -o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) -o:depends({ [_n("protocol")] = "hysteria2" }) +o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) +o.password = true +o:depends({ [_n("protocol")] = "hysteria2"}) o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) o:value("", translate("Disable")) @@ -313,9 +356,11 @@ o:depends({ [_n("protocol")] = "hysteria2" }) o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) -o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) -o.password = true -o:depends({ [_n("protocol")] = "hysteria2"}) +o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) +o:depends({ [_n("protocol")] = "hysteria2" }) + +o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) +o:depends({ [_n("protocol")] = "hysteria2" }) o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)") o:depends({ [_n("protocol")] = "hysteria2"}) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua index f45ee300..26969a45 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua @@ -43,6 +43,8 @@ local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zer local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'") +local singbox_version = api.get_app_version("sing-box"):match("[^v]+") + o = s:option(ListValue, _n("protocol"), translate("Protocol")) o:value("socks", "Socks") o:value("http", "HTTP") @@ -388,19 +390,32 @@ end if singbox_tags:find("with_quic") then o = s:option(Value, _n("hysteria2_hop"), translate("Port hopping range")) o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).") - o:depends({ [_n("protocol")] = "hysteria2" }) + o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) - o = s:option(Value, _n("hysteria2_hop_interval"), translate("Hop Interval(Second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5.")) + o = s:option(Value, _n("hysteria2_hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5.")) o.datatype = "or(uinteger,portrange)" o.placeholder = "30" o.default = "30" - o:depends({ [_n("protocol")] = "hysteria2" }) + o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) - o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) - o:depends({ [_n("protocol")] = "hysteria2" }) + o = s:option(Flag, _n("hysteria2_realms"), translate("Realms")) + o.default = "0" + if api.compare_versions(singbox_version, ">=", "1.14.0") then + o:depends({ [_n("protocol")] = "hysteria2"}) + else + o:depends({ [_n("protocol")] = "__hide"}) + end - o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) - o:depends({ [_n("protocol")] = "hysteria2" }) + o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") + o:depends({ [_n("hysteria2_realms")] = "1" }) + + o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN")) + o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } + o:depends({ [_n("hysteria2_realms")] = "1" }) + + o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) + o.password = true + o:depends({ [_n("protocol")] = "hysteria2"}) o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) o:value("", translate("Disable")) @@ -410,8 +425,20 @@ if singbox_tags:find("with_quic") then o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) - o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) - o.password = true + o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) + o:depends({ [_n("protocol")] = "hysteria2" }) + + o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) + o:depends({ [_n("protocol")] = "hysteria2" }) + + o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)") + o:depends({ [_n("protocol")] = "hysteria2"}) + + o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)") + o:depends({ [_n("protocol")] = "hysteria2"}) + + o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection")) + o.default = "0" o:depends({ [_n("protocol")] = "hysteria2"}) end @@ -506,7 +533,7 @@ o.default = "0" o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false }) o:depends({ [_n("protocol")] = "tuic" }) o:depends({ [_n("protocol")] = "hysteria" }) -o:depends({ [_n("protocol")] = "hysteria2" }) +o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o:depends({ [_n("protocol")] = "naive" }) o = s:option(TextValue, _n("ech_config"), translate("ECH Config")) @@ -818,10 +845,14 @@ local protocols = s.fields[_n("protocol")].keylist if #protocols > 0 then for i, v in ipairs(protocols) do if not v:find("^_") then - s.fields[_n("address")]:depends({ [_n("protocol")] = v }) - s.fields[_n("port")]:depends({ [_n("protocol")] = v }) - s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v }) - s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v }) + local depends_condition = { [_n("protocol")] = v } + if v == "hysteria2" then + depends_condition[_n("hysteria2_realms")] = false + end + s.fields[_n("address")]:depends(depends_condition) + s.fields[_n("port")]:depends(depends_condition) + s.fields[_n("domain_resolver")]:depends(depends_condition) + s.fields[_n("domain_strategy")]:depends(depends_condition) end end end @@ -836,6 +867,7 @@ if not load_shunt_options then o:value("1", translate("Preproxy Node")) end o:value("2", translate("Landing Node")) + o:depends({ [_n("hysteria2_realms")] = false }) o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy.")) o1:depends({ [_n("chain_proxy")] = "1" }) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua index 6983d0d5..74f5a37d 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua @@ -22,17 +22,38 @@ o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(Value, _n("port"), translate("Listen Port")) o.datatype = "port" +o.rmempty = false o:depends({ [_n("custom")] = false }) -o = s:option(Value, _n("obfs"), translate("Obfs Password")) +o = s:option(Flag, _n("realms"), translate("Realms")) +o.default = "0" o.rewrite_option = o.option o:depends({ [_n("custom")] = false }) +o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") +o.rewrite_option = o.option +o:depends({ [_n("realms")] = "1" }) + +o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN")) +o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } +o.rewrite_option = o.option +o:depends({ [_n("realms")] = "1" }) + o = s:option(Value, _n("auth_password"), translate("Auth Password")) o.password = true o.rewrite_option = o.option o:depends({ [_n("custom")] = false }) +o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type")) +o:value("", translate("Disable")) +o:value("salamander") +o.rewrite_option = o.option +o:depends({ [_n("custom")] = false }) + +o = s:option(Value, _n("obfs_password"), translate("Obfs Password")) +o.rewrite_option = o.option +o:depends({ [_n("obfs_type")] = "salamander" }) + o = s:option(Flag, _n("udp"), translate("UDP")) o.default = "1" o.rewrite_option = o.option diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua index a20b03bb..3a4d9ba3 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua @@ -129,6 +129,14 @@ o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o.password = true o:depends({ [_n("protocol")] = "hysteria2"}) +o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) +o:value("", translate("Disable")) +o:value("salamander") +o:depends({ [_n("protocol")] = "hysteria2" }) + +o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) +o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) + o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control")) o.default = 0 o:depends({ [_n("protocol")] = "hysteria2" }) @@ -139,14 +147,6 @@ o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwid o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) -o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) -o:value("", translate("Disable")) -o:value("salamander") -o:depends({ [_n("protocol")] = "hysteria2" }) - -o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) -o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) - ---- [[ TLS ]] o = s:option(Flag, _n("tls"), translate("TLS")) o.default = 0 diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua index 3455bc8b..96beede8 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua @@ -58,6 +58,7 @@ o:depends({ [_n("custom")] = false }) o = s:option(Value, _n("port"), translate("Listen Port")) o.datatype = "port" +o.rmempty = false o:depends({ [_n("custom")] = false }) o = s:option(Flag, _n("auth"), translate("Auth")) @@ -157,10 +158,29 @@ if singbox_tags:find("with_quic") then end if singbox_tags:find("with_quic") then + o = s:option(Flag, _n("hysteria2_realms"), translate("Realms")) + o.default = "0" + o:depends({ [_n("protocol")] = "hysteria2"}) + + o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") + o:depends({ [_n("hysteria2_realms")] = "1" }) + + o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN")) + o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } + o:depends({ [_n("hysteria2_realms")] = "1" }) + o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o.password = true o:depends({ [_n("protocol")] = "hysteria2"}) + o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) + o:value("", translate("Disable")) + o:value("salamander") + o:depends({ [_n("protocol")] = "hysteria2" }) + + o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) + o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) + o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm")) o.default = 0 o:depends({ [_n("protocol")] = "hysteria2" }) @@ -170,14 +190,6 @@ if singbox_tags:find("with_quic") then o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) - - o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) - o:value("", translate("Disable")) - o:value("salamander") - o:depends({ [_n("protocol")] = "hysteria2" }) - - o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) - o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) end o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol")) @@ -324,7 +336,7 @@ o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false }) o:depends({ [_n("protocol")] = "naive" }) o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ [_n("protocol")] = "tuic" }) -o:depends({ [_n("protocol")] = "hysteria2" }) +o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o = s:option(TextValue, _n("ech_key"), translate("ECH Key")) o.default = "" diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua index eae4eb92..a5dc64a9 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua @@ -31,7 +31,11 @@ local type_table = {} for filename in fs.dir(types_dir) do table.insert(type_table, filename) end -table.sort(type_table) +table.sort(type_table, function(a, b) + if a == "socks.lua" then return true end + if b == "socks.lua" then return false end + return a < b +end) for index, value in ipairs(type_table) do local p_func = loadfile(types_dir .. value) diff --git a/luci-app-passwall2/luasrc/passwall2/api.lua b/luci-app-passwall2/luasrc/passwall2/api.lua index 5bcfcfbe..71a59821 100644 --- a/luci-app-passwall2/luasrc/passwall2/api.lua +++ b/luci-app-passwall2/luasrc/passwall2/api.lua @@ -377,11 +377,13 @@ function is_special_node(e) end function is_ip(val) + val = trim(val):lower() local str = val:match("%[(.-)%]") or val return datatypes.ipaddr(str) or false end function is_ipv6(val) + val = trim(val):lower() local str = val:match("%[(.-)%]") or val return datatypes.ip6addr(str) or false end @@ -497,9 +499,10 @@ function get_valid_nodes() end end local port = e.port or e.hysteria_hop or e.hysteria2_hop - if port and e.address then + local is_realm = (e.type == "Hysteria2" or e.protocol == 'hysteria2') and e.hysteria2_realms or nil + if (port and e.address) or is_realm then local address = e.address - if is_ip(address) or datatypes.hostname(address) then + if is_ip(address) or datatypes.hostname(address) or is_realm then if (e.type == "sing-box" or e.type == "Xray") and e.protocol then local protocol = e.protocol if protocol == "vmess" then @@ -526,10 +529,13 @@ function get_valid_nodes() type_name = type_name .. " " .. protocol end if is_ipv6(address) then address = get_ipv6_full(address) end + type_name = is_realm and type_name .. " Realm" or type_name e["remark"] = trim("%s:[%s]" % {type_name, e.remarks}) if show_node_info == "1" then - port = port:gsub(":", "-") - e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port}) + port = (port or ""):gsub(":", "-") + if not is_realm then + e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port}) + end end e.node_type = "normal" if not e.group or e.group == "" then @@ -619,7 +625,10 @@ function get_node_remarks(n) end type_name = type_name .. " " .. protocol end - remarks = trim("%s:[%s]" % {type_name, n.remarks}) + if (n.type == "Hysteria2" or n.protocol == 'hysteria2') and n.hysteria2_realms then + type_name = type_name .. " Realm" + end + remarks = trim("%s:[%s]" % {type_name, n.remarks}) end end return remarks @@ -1204,7 +1213,7 @@ function to_move(app_name,file) } end - local flag = sys.call('pgrep -af "passwall2/.*'.. app_name ..'" >/dev/null') + local flag = sys.call('busybox pgrep -af "passwall2/.*'.. app_name ..'" >/dev/null') if flag == 0 then sys.call("/etc/init.d/passwall2 stop") end @@ -1587,4 +1596,30 @@ function get_dnsmasq_server_domain() end end return dnsmasq_server_t -end \ No newline at end of file +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 diff --git a/luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua b/luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua index 1b8ef901..23fac98a 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua @@ -5,15 +5,24 @@ local jsonc = api.jsonc function gen_config_server(node) local config = { - listen = ":" .. node.port, + listen = (function() + if node.hysteria2_realms and node.hysteria2_realm_url then + local url = node.hysteria2_realm_url:gsub("/+$", "") + if node.port then + url = url .. (url:find("?") and "&lport=" or "?lport=") .. node.port + end + return url + end + return ":" .. (node.port or "0") + end)(), tls = { cert = node.tls_certificateFile, key = node.tls_keyFile, }, - obfs = (node.hysteria2_obfs) and { + obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and { type = "salamander", salamander = { - password = node.hysteria2_obfs + password = node.hysteria2_obfs_password } } or nil, auth = { @@ -26,6 +35,9 @@ function gen_config_server(node) } or nil, ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false, disableUDP = (node.hysteria2_udp == "0") and true or false, + realm = (node.hysteria2_realms and node.hysteria2_realm_stun) and { + stunServers = node.hysteria2_realm_stun + } or nil } return config end @@ -51,14 +63,23 @@ function gen_config(var) if api.is_ipv6(server_host) then server_host = api.get_ipv6_full(server_host) end - local server = server_host .. ":" .. server_port - if (node.hysteria2_hop) then - server = server .. "," .. string.gsub(node.hysteria2_hop, ":", "-") + local server = server_host .. (server_port and ":" .. server_port or "") + + if node.hysteria2_hop then + server = server .. (server_port and "," or ":") .. string.gsub(node.hysteria2_hop, ":", "-") end local config = { - server = server, + server = (function() + if node.hysteria2_realms and node.hysteria2_realm_url then + return node.hysteria2_realm_url:gsub("/+$", "") + end + return server + end)(), + realm = (node.hysteria2_realms and node.hysteria2_realm_stun) and { + stunServers = node.hysteria2_realm_stun + } or nil, transport = { type = "udp", udp = node.hysteria2_hop and (function() @@ -83,10 +104,10 @@ function gen_config(var) return udp end)() or nil }, - obfs = (node.hysteria2_obfs) and { + obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and { type = "salamander", salamander = { - password = node.hysteria2_obfs + password = node.hysteria2_obfs_password } } or nil, auth = node.hysteria2_auth_password, diff --git a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua index 0116accb..dad25b00 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua @@ -134,8 +134,8 @@ function gen_outbound(flag, node, tag, proxy_table) local run_socks_instance = true if proxy_table ~= nil and type(proxy_table) == "table" then proxy_tag = proxy_table.tag or nil - fragment = proxy_table.fragment or nil - record_fragment = proxy_table.record_fragment or nil + fragment = (proxy_table.fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil + record_fragment = (proxy_table.record_fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil run_socks_instance = proxy_table.run_socks_instance end @@ -250,7 +250,7 @@ function gen_outbound(flag, node, tag, proxy_table) enabled = true, disable_sni = (node.tls_disable_sni == "1") and true or false, -- Do not send the server name in ClientHello. server_name = node.tls_serverName, -- Used to verify the hostname on the returned certificate, unless the settings are insecure. It is also included in ClientHello to support virtual hosts, unless it is an IP address. - insecure = (node.tls_allowInsecure == "1") and true or false, -- Accepts any server certificate. + insecure = node.tls_allowInsecure == "1" or (node.tls_pinSHA256 and node.tls_pinSHA256 ~= ""), -- Accepts any server certificate. (Also compatible with xray's pinnedPeerCertSha256) alpn = alpn, -- A list of supported application layer protocols, arranged in order of priority. If both peers support ALPN, the protocol selected will be one of these protocols; otherwise, the connection will fail. --max_version = "1.3", fragment = fragment, @@ -506,6 +506,7 @@ function gen_outbound(flag, node, tag, proxy_table) table.insert(server_ports, range) end end + result.server_port = nil end protocol_table = { server_ports = next(server_ports) and server_ports or nil, @@ -519,9 +520,9 @@ function gen_outbound(flag, node, tag, proxy_table) obfs = node.hysteria_obfs, auth = (node.hysteria_auth_type == "base64") and node.hysteria_auth_password or nil, auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil, - recv_window_conn = tonumber(node.hysteria_recv_window_conn), - recv_window = tonumber(node.hysteria_recv_window), - disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, + recv_window_conn = tonumber(node.hysteria_recv_window_conn), --1.14 将变更为 stream_receive_window + recv_window = tonumber(node.hysteria_recv_window), --1.14 将变更为 connection_receive_window + disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 将变更为 disable_path_mtu_discovery tls = tls } end @@ -545,13 +546,12 @@ function gen_outbound(flag, node, tag, proxy_table) heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s", tls = tls } - if node.tuic_alpn and node.tuic_alpn ~= "default" then - local alpn = {} - string.gsub(node.tuic_alpn, '[^,]+', function(w) - table.insert(alpn, w) - end) - if #alpn > 0 then protocol_table.tls.alpn = alpn end - end + node.tuic_alpn = (node.tuic_alpn and node.tuic_alpn ~= "default") and node.tuic_alpn or "h3" + local alpn = {} + string.gsub(node.tuic_alpn, '[^,]+', function(w) + table.insert(alpn, w) + end) + if #alpn > 0 then protocol_table.tls.alpn = alpn end end if node.protocol == "hysteria2" then @@ -563,6 +563,10 @@ function gen_outbound(flag, node, tag, proxy_table) table.insert(server_ports, range) end end + result.server_port = nil + end + if node.hysteria2_realms then + server_ports = {} end local interval, interval_max if next(server_ports) then @@ -595,7 +599,29 @@ function gen_outbound(flag, node, tag, proxy_table) password = node.hysteria2_obfs_password } or nil, password = node.hysteria2_auth_password or nil, - tls = tls + idle_timeout = (function(t) + if not version_ge_1_14_0 then return nil end + t = tonumber(tostring(t or "30"):match("^%d+")) + return (t and t >= 4 and t <= 120) and t or 30 + end)(node.hysteria2_idle_timeout), + keep_alive_period = (function(t) + if not version_ge_1_14_0 then return nil end + t = tonumber(tostring(t or "0"):match("^%d+")) + return (t and t >= 2 and t <= 60) and t or nil + end)(node.hysteria2_keep_alive_period), + disable_path_mtu_discovery = version_ge_1_14_0 and (tonumber(node.hysteria2_disable_mtu_discovery) == 1) or nil, + tls = tls, + realm = node.hysteria2_realms and (function() + result.server = nil + result.server_port = nil + local realm = api.parse_realm_uri(node.hysteria2_realm_url) + if realm then + realm.server_url = realm.server_url and "https://" .. realm.server_url or nil + realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun + return realm + end + return nil + end)() or nil } end @@ -881,10 +907,10 @@ function gen_config_server(node) auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil, } }, - recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, - recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, - max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, - disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, + recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, --1.14 to stream_receive_window + recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, --1.14 to connection_receive_window + max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, --1.14 to max_concurrent_streams + disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 to disable_path_mtu_discover tls = tls } end @@ -928,7 +954,17 @@ function gen_config_server(node) } }, ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false, - tls = tls + tls = tls, + realm = node.hysteria2_realms and (function() + local realm = api.parse_realm_uri(node.hysteria2_realm_url) + if realm then + realm.server_url = realm.server_url and "https://" .. realm.server_url or nil + realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun + realm.stun_domain_resolver = "direct" + return realm + end + return nil + end)() or nil } end @@ -1312,7 +1348,6 @@ function gen_config(var) else ut_nodes = _node.urltest_node end - if #ut_nodes == 0 then return nil end local valid_nodes = {} for i = 1, #(ut_nodes or {}) do local ut_node_id = ut_nodes[i] @@ -2051,7 +2086,7 @@ function gen_config(var) string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w) sys.call("ipset -q -F " .. w) end) - local ipset_prefix_name = "passwall2_" .. node_id .. "_" + local ipset_prefix_name = "psw2_" .. node_id .. "_" local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'") string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w) sys.call("ipset -q -F " .. w) @@ -2070,7 +2105,7 @@ function gen_config(var) end) local family = "inet" local table_name = "passwall2" - local nftset_prefix_name = "passwall2_" .. node_id .. "_" + local nftset_prefix_name = "psw2_" .. node_id .. "_" local nftset_list = sys.exec("nft -a list sets | grep -E '" .. nftset_prefix_name .. "' | awk -F 'set ' '{print $2}' | awk '{print $1}'") string.gsub(nftset_list, '[^' .. "\r\n" .. ']+', function(w) sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, w)) @@ -2138,7 +2173,7 @@ function gen_config(var) if not value.detour and value.server then value.detour = "direct" end - if value.server and not api.datatypes.hostname(value.server) then + if (value.server and not api.datatypes.hostname(value.server)) and not value.realm then value.domain_resolver = nil end for k, v in pairs(config.outbounds[index]) do diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua index 27c7c9f0..15d67abb 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua @@ -794,6 +794,9 @@ function gen_config_server(node) config.outbounds[index][k] = nil end end + if value.protocol == "freedom" and api.compare_versions(xray_version, "<", "26.5.3") then -- Todo is to remove it + value.settings = nil + end end return config @@ -1049,10 +1052,10 @@ function gen_config(var) if #valid_nodes == 0 then return nil end -- fallback node - local fallback_node_tag = nil local fallback_node_id = _node.fallback_node - if not fallback_node_id or fallback_node_id == "" then fallback_node_id = nil end - if fallback_node_id then + fallback_node_id = (fallback_node_id and fallback_node_id ~= "") and fallback_node_id or nil + local fallback_node_tag = (fallback_node_id == "_direct") and "direct" or "blackhole" + if fallback_node_id and fallback_node_id ~= "_direct" then local is_new_node = true for _, outbound in ipairs(outbounds) do if string.sub(outbound.tag, 1, #fallback_node_id) == fallback_node_id then @@ -1082,7 +1085,13 @@ function gen_config(var) type = _node.balancingStrategy, settings = { expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2, - maxRTT = "1s" + maxRTT = "1s", + tolerance = (function(t) + t = tonumber(t) or 0 + if t < 1 then return nil end + if t > 100 then t = 100 end + return t / 100 + end)(_node.tolerance) } } else @@ -1264,9 +1273,9 @@ function gen_config(var) interface = node.iface } }, - settings = { + settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check finalRules = {{ action = "allow" }} - } + } or nil } sys.call(string.format("mkdir -p %s && touch %s/%s", api.TMP_IFACE_PATH, api.TMP_IFACE_PATH, node.iface)) end @@ -1806,7 +1815,7 @@ function gen_config(var) string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w) sys.call("ipset -q -F " .. w) end) - local ipset_prefix_name = "passwall2_" .. node_id .. "_" + local ipset_prefix_name = "psw2_" .. node_id .. "_" local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'") string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w) sys.call("ipset -q -F " .. w) @@ -1825,7 +1834,7 @@ function gen_config(var) end) local family = "inet" local table_name = "passwall2" - local nftset_prefix_name = "passwall2_" .. node_id .. "_" + local nftset_prefix_name = "psw2_" .. node_id .. "_" local nftset_list = sys.exec("nft -a list sets | grep -E '" .. nftset_prefix_name .. "' | awk -F 'set ' '{print $2}' | awk '{print $1}'") string.gsub(nftset_list, '[^' .. "\r\n" .. ']+', function(w) sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, w)) @@ -2080,7 +2089,12 @@ function gen_proto_config(var) end table.insert(outbounds, { - protocol = "freedom", tag = "direct", settings = {finalRules = {{ action = "allow" }}}, sockopt = {mark = 255} + protocol = "freedom", + tag = "direct", + settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check + finalRules = {{ action = "allow" }} + } or nil, + sockopt = {mark = 255} }) local config = { diff --git a/luci-app-passwall2/luasrc/view/passwall2/global/footer.htm b/luci-app-passwall2/luasrc/view/passwall2/global/footer.htm index 590e45a4..e1ba5c58 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/global/footer.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/global/footer.htm @@ -35,22 +35,48 @@ local appname = api.appname \ No newline at end of file diff --git a/luci-app-passwall2/luasrc/view/passwall2/node_config/link_share_man.htm b/luci-app-passwall2/luasrc/view/passwall2/node_config/link_share_man.htm index e6c4a5d2..655b0be6 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/node_config/link_share_man.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/node_config/link_share_man.htm @@ -602,10 +602,10 @@ local current_node = map:get(section) if (v_type === "Hysteria2") { v_password = opt.get("hysteria2_auth_password"); - var dom_obfs = opt.get("hysteria2_obfs"); + var dom_obfs = opt.get("hysteria2_obfs_password"); if (dom_obfs && dom_obfs.value != "") { params += "&obfs=" + "salamander"; - params += opt.query("obfs-password", "hysteria2_obfs"); + params += opt.query("obfs-password", "hysteria2_obfs_password"); } params += opt.query("mport", "hysteria2_hop"); } else { @@ -1621,7 +1621,8 @@ local current_node = map:get(section) dom_prefix = "hysteria2_" opt.set(dom_prefix + 'auth_password', decodeURIComponent(password)); if (queryParam["obfs-password"] || queryParam["obfs_password"]) { - opt.set(dom_prefix + 'obfs', queryParam["obfs-password"] || queryParam["obfs_password"]); + opt.set(dom_prefix + 'obfs_type', "salamander"); + opt.set(dom_prefix + 'obfs_password', queryParam["obfs-password"] || queryParam["obfs_password"]); } opt.set(dom_prefix + 'hop', queryParam.mport || ""); } @@ -1678,7 +1679,7 @@ local current_node = map:get(section) } opt.set(dom_prefix + 'tuic_congestion_control', queryParam.congestion_control || 'cubic'); opt.set(dom_prefix + 'tuic_udp_relay_mode', queryParam.udp_relay_mode || 'native'); - opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'default'); + opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'h3'); opt.set(dom_prefix + 'tls_serverName', queryParam.sni || ''); opt.set(dom_prefix + 'tls_disable_sni', queryParam.disable_sni === "1"); opt.set( diff --git a/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm b/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm index 09121425..2fdb2bcd 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm @@ -911,7 +911,11 @@ table td, .table .td { } else { port_s = o["hysteria_hop"] || o["hysteria2_hop"]; } - str += type + ":" + remarks; + if ((o.type === "Hysteria2" || o.protocol === 'hysteria2') && o.hysteria2_realms) { + str += type + " Realm:" + remarks; + } else { + str += type + ":" + remarks; + } return str; } diff --git a/luci-app-passwall2/po/fa/passwall2.po b/luci-app-passwall2/po/fa/passwall2.po index 36738e51..08faf7ad 100644 --- a/luci-app-passwall2/po/fa/passwall2.po +++ b/luci-app-passwall2/po/fa/passwall2.po @@ -46,8 +46,8 @@ msgstr "لیست قوانین" msgid "Access control" msgstr "کنترل دسترسی" -msgid "Watch Logs" -msgstr "مشاهده گزارش‌ها" +msgid "Runtime Logs" +msgstr "گزارش‌های زمان اجرا" msgid "Node Config" msgstr "پیکربندی گره" @@ -437,6 +437,12 @@ msgstr "تعداد گره‌های ترجیحی" msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgstr "توزیع‌کننده بار تعداد بهینه‌ای از گره‌ها را انتخاب می‌کند و ترافیک به طور تصادفی بین آن‌ها توزیع می‌شود." +msgid "Failure Tolerance (%)" +msgstr "تحمل شکست (%)" + +msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate." +msgstr "حداکثر نرخ شکست قابل قبول در تست سرعت. برای مثال، عدد ۱ به معنای مجاز بودن نرخ شکست ۱٪ است. + msgid "Shunt" msgstr "شنت (Shunt)" diff --git a/luci-app-passwall2/po/ru/passwall2.po b/luci-app-passwall2/po/ru/passwall2.po index ee82c72c..f871b6e3 100644 --- a/luci-app-passwall2/po/ru/passwall2.po +++ b/luci-app-passwall2/po/ru/passwall2.po @@ -46,8 +46,8 @@ msgstr "Список правил" msgid "Access control" msgstr "Контроль доступа" -msgid "Watch Logs" -msgstr "Просмотр журнала" +msgid "Runtime Logs" +msgstr "Журналы выполнения" msgid "Node Config" msgstr "Параметры узла" @@ -433,6 +433,12 @@ msgstr "Количество предпочтительных узлов" msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgstr "Балансировщик выбирает указанное количество наилучших узлов и распределяет трафик между ними случайным образом." +msgid "Failure Tolerance (%)" +msgstr "Допуск на отказ (%)" + +msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate." +msgstr "Максимально допустимый процент отказов при проверке скорости. Например, 1 означает допустимый процент отказов в 1%." + msgid "Shunt" msgstr "Shunt (разделение трафика)" diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po index baa0ec3e..4878542a 100644 --- a/luci-app-passwall2/po/zh-cn/passwall2.po +++ b/luci-app-passwall2/po/zh-cn/passwall2.po @@ -46,8 +46,8 @@ msgstr "规则列表" msgid "Access control" msgstr "访问控制" -msgid "Watch Logs" -msgstr "查看日志" +msgid "Runtime Logs" +msgstr "运行日志" msgid "Node Config" msgstr "节点配置" @@ -433,6 +433,12 @@ msgstr "优选节点数量" msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。" +msgid "Failure Tolerance (%)" +msgstr "测速失败率容忍度(%)" + +msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate." +msgstr "最多可接受的测速失败率,例如 1 表示允许 1% 的测速失败率。" + msgid "Shunt" msgstr "分流" diff --git a/luci-app-passwall2/po/zh-tw/passwall2.po b/luci-app-passwall2/po/zh-tw/passwall2.po index 87449b17..fceb5479 100644 --- a/luci-app-passwall2/po/zh-tw/passwall2.po +++ b/luci-app-passwall2/po/zh-tw/passwall2.po @@ -46,8 +46,8 @@ msgstr "規則列表" msgid "Access control" msgstr "訪問控制" -msgid "Watch Logs" -msgstr "查看日誌" +msgid "Runtime Logs" +msgstr "運行日誌" msgid "Node Config" msgstr "節點配置" @@ -433,6 +433,12 @@ msgstr "優選節點數量" msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgstr "負載均衡器選出最優節點的個數,流量將在這几個節點中随機分配。" +msgid "Failure Tolerance (%)" +msgstr "測速失敗率容忍度(%)" + +msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate." +msgstr "最多可接受的測速失敗率,例如 1 表示允許 1% 的測速失敗率。" + msgid "Shunt" msgstr "分流" diff --git a/luci-app-passwall2/root/etc/hotplug.d/iface/98-passwall2 b/luci-app-passwall2/root/etc/hotplug.d/iface/98-passwall2 index 06921225..f80354d1 100644 --- a/luci-app-passwall2/root/etc/hotplug.d/iface/98-passwall2 +++ b/luci-app-passwall2/root/etc/hotplug.d/iface/98-passwall2 @@ -12,7 +12,7 @@ if [ "$ACTION" = "ifupdate" ]; then USE_TABLES=$(get_cache_var "USE_TABLES") - [ -n "$USE_TABLES" ] && ${APP_PATH}/${USE_TABLES}.sh update_wan_sets >/dev/null 2>&1 & + [ -n "$USE_TABLES" ] && { ${APP_PATH}/${USE_TABLES}.sh update_wan_sets >/dev/null 2>&1 & } exit 0 fi diff --git a/luci-app-passwall2/root/usr/share/passwall2/app.sh b/luci-app-passwall2/root/usr/share/passwall2/app.sh index dbe5234a..d4b0cb03 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/app.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/app.sh @@ -84,7 +84,7 @@ run_xray() { json_add_string "loglevel" "${loglevel}" [ -n "$flag" ] && { - pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1 + busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1 json_add_string "flag" "${flag}" } [ -n "$socks_address" ] && [ -n "$socks_port" ] && { @@ -112,12 +112,12 @@ run_xray() { local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf [ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf if [ "${nftflag}" = "1" ]; then - local direct_nftset4="passwall2_${node}_white" - local direct_nftset6="passwall2_${node}_white6" + local direct_nftset4="psw2_${node}_white" + local direct_nftset6="psw2_${node}_white6" local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}" else - local direct_ipset4="passwall2_${node}_white" - local direct_ipset6="passwall2_${node}_white6" + local direct_ipset4="psw2_${node}_white" + local direct_ipset6="psw2_${node}_white6" local direct_ipset="${direct_ipset4},${direct_ipset6}" fi run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf} @@ -225,7 +225,7 @@ run_singbox() { json_add_string "loglevel" "${loglevel}" [ -n "$flag" ] && { - pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1 + busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1 json_add_string "flag" "${flag}" } [ -n "$socks_address" ] && [ -n "$socks_port" ] && { @@ -251,12 +251,12 @@ run_singbox() { local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf [ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf if [ "${nftflag}" = "1" ]; then - local direct_nftset4="passwall2_${node}_white" - local direct_nftset6="passwall2_${node}_white6" + local direct_nftset4="psw2_${node}_white" + local direct_nftset6="psw2_${node}_white6" local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}" else - local direct_ipset4="passwall2_${node}_white" - local direct_ipset6="passwall2_${node}_white6" + local direct_ipset4="psw2_${node}_white" + local direct_ipset6="psw2_${node}_white6" local direct_ipset="${direct_ipset4},${direct_ipset6}" fi run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf} @@ -386,6 +386,10 @@ run_socks() { fi fi + if [ -n "${error_msg}" ] && [ "$(config_n_get $node hysteria2_realms)" = "1" ]; then + unset error_msg + fi + [ -n "${error_msg}" ] && { [ "$bind" != "127.0.0.1" ] && log 1 "$(i18n "Socks node: [%s]%s, start failed %s:%s %s" "${remarks}" "${tmp}" "${bind}" "${socks_port}" "${error_msg}")" return 1 @@ -555,7 +559,7 @@ socks_node_switch() { [ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1 done - pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1 && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1 + busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1 && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1 for prefix in "" "HTTP_" "HTTP2"; do rm -rf "$TMP_PATH/${prefix}SOCKS_${flag}"* done @@ -791,7 +795,7 @@ start_socks() { # Auto switch logic local enable_autoswitch=$(config_n_get $id enable_autoswitch 0) - [ "$enable_autoswitch" = "1" ] && $APP_PATH/socks_auto_switch.sh ${id} > /dev/null 2>&1 & + [ "$enable_autoswitch" = "1" ] && { $APP_PATH/socks_auto_switch.sh ${id} > /dev/null 2>&1 & } done } } @@ -805,14 +809,14 @@ clean_crontab() { sed -i "/$(echo "lua ${APP_PATH}/rule_update.lua log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 sed -i "/$(echo "lua ${APP_PATH}/subscribe.lua start" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 - pgrep -af "${CONFIG}/" | awk '/tasks\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 + busybox pgrep -af "${CONFIG}/" | awk '/tasks\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 rm -rf /tmp/lock/${CONFIG}_tasks.lock } start_crontab() { if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then start_daemon=$(config_t_get global_delay start_daemon 0) - [ "$start_daemon" = "1" ] && $APP_PATH/monitor.sh > /dev/null 2>&1 & + [ "$start_daemon" = "1" ] && { $APP_PATH/monitor.sh > /dev/null 2>&1 & } fi [ -f "/tmp/lock/${CONFIG}_cron.lock" ] && { @@ -982,11 +986,11 @@ run_ipset_chinadns_ng() { [ -n "${ipset}" ] && { set_names=$ipset - vps_set_names="passwall2_vps,passwall2_vps6" + vps_set_names="psw2_vps,psw2_vps6" } [ -n "${nftset}" ] && { set_names=$(echo ${nftset} | awk -F, '{printf "%s,%s", substr($1,3), substr($2,3)}' | sed 's/#/@/g') - vps_set_names="inet@passwall2@passwall2_vps,inet@passwall2@passwall2_vps6" + vps_set_names="inet@passwall2@psw2_vps,inet@passwall2@psw2_vps6" } cat <<-EOF > $config_file bind-addr 127.0.0.1 @@ -1162,7 +1166,7 @@ acl_app() { } start() { - pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && { + busybox pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && { #log_i18n 0 "The program has started. Please stop it and then restart it!" stop } @@ -1241,8 +1245,8 @@ stop() { kill -9 "$pid" >/dev/null 2>&1 fi done - pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1 - pgrep -af "${CONFIG}/" | awk '! /app\.sh|subscribe\.lua|rule_update\.lua|tasks\.sh|server_app\.lua|ujail/{print $1}' | xargs kill -9 >/dev/null 2>&1 + busybox pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1 + busybox pgrep -af "${CONFIG}/" | awk '! /app\.sh|subscribe\.lua|rule_update\.lua|tasks\.sh|server_app\.lua|ujail/{print $1}' | xargs kill -9 >/dev/null 2>&1 unset V2RAY_LOCATION_ASSET unset XRAY_LOCATION_ASSET unset SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN diff --git a/luci-app-passwall2/root/usr/share/passwall2/haproxy_check.sh b/luci-app-passwall2/root/usr/share/passwall2/haproxy_check.sh index 79376d69..106b4cf0 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/haproxy_check.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/haproxy_check.sh @@ -8,7 +8,8 @@ listen_port=$2 server_address=$3 server_port=$4 -pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null && { +busybox pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null && { + # No detection during specific task execution exit 0 } diff --git a/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua b/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua index 44fa7fd8..5ddbb744 100644 --- a/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua +++ b/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua @@ -274,7 +274,7 @@ function add_rule(var) if address == "engage.cloudflareclient.com" then return end if datatypes.hostname(address) then set_domain_dns(address, fwd_dns) - set_domain_ipset(address, setflag_4 .. "passwall2_vps," .. setflag_6 .. "passwall2_vps6") + set_domain_ipset(address, setflag_4 .. "psw2_vps," .. setflag_6 .. "psw2_vps6") end end process_address(t.address) diff --git a/luci-app-passwall2/root/usr/share/passwall2/iptables.sh b/luci-app-passwall2/root/usr/share/passwall2/iptables.sh index 95888842..c3843150 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/iptables.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/iptables.sh @@ -3,17 +3,17 @@ DIR="$(cd "$(dirname "$0")" && pwd)" MY_PATH=$DIR/iptables.sh UTILS_PATH=$DIR/utils.sh -IPSET_LOCAL="passwall2_local" -IPSET_PROXY_LAN="passwall2_proxy_lan" -IPSET_LAN="passwall2_lan" -IPSET_VPS="passwall2_vps" -IPSET_WAN="passwall2_wan" +IPSET_LOCAL="psw2_local" +IPSET_PROXY_LAN="psw2_proxy_lan" +IPSET_LAN="psw2_lan" +IPSET_VPS="psw2_vps" +IPSET_WAN="psw2_wan" -IPSET_LOCAL6="passwall2_local6" -IPSET_PROXY_LAN6="passwall2_proxy_lan6" -IPSET_LAN6="passwall2_lan6" -IPSET_VPS6="passwall2_vps6" -IPSET_WAN6="passwall2_wan6" +IPSET_LOCAL6="psw2_local6" +IPSET_PROXY_LAN6="psw2_proxy_lan6" +IPSET_LAN6="psw2_lan6" +IPSET_VPS6="psw2_vps6" +IPSET_WAN6="psw2_wan6" FWMARK="0x50535732" @@ -194,8 +194,8 @@ gen_shunt_list() { for shunt_id in $shunt_ids; do local shunt_node=$(config_n_get ${node} "${shunt_id}") [ -n "$shunt_node" ] && { - local ipset_v4="passwall2_${node}_${shunt_id}" - local ipset_v6="passwall2_${node}_${shunt_id}6" + local ipset_v4="psw2_${node}_${shunt_id}" + local ipset_v6="psw2_${node}_${shunt_id}6" ipset -! create $ipset_v4 nethash maxelem 1048576 ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576 local outbound="redirect" @@ -219,22 +219,14 @@ gen_shunt_list() { } local direct_ipset4=$(get_cache_var "node_${node}_direct_ipset4") [ -n "${direct_ipset4}" ] && { - ipset -! create ${direct_ipset4} nethash maxelem 1048576 timeout 259200 + ipset -! create ${direct_ipset4} iphash maxelem 1048576 timeout 259200 _SHUNT_LIST4="${_SHUNT_LIST4} ${direct_ipset4}:direct" } local direct_ipset6=$(get_cache_var "node_${node}_direct_ipset6") [ -n "${direct_ipset6}" ] && { - ipset -! create ${direct_ipset6} nethash family inet6 maxelem 1048576 timeout 259200 + ipset -! create ${direct_ipset6} iphash family inet6 maxelem 1048576 timeout 259200 _SHUNT_LIST6="${_SHUNT_LIST6} ${direct_ipset6}:direct" } - [ "${preloading}" = "1" ] && [ -n "$default_node" ] && { - local ipset_v4="passwall2_${node}_default" - local ipset_v6="passwall2_${node}_default6" - ipset -! create $ipset_v4 nethash maxelem 1048576 - ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576 - _SHUNT_LIST4="${_SHUNT_LIST4} ${ipset_v4}:${default_outbound}" - _SHUNT_LIST6="${_SHUNT_LIST6} ${ipset_v6}:${default_outbound}" - } } [ -n "${_SHUNT_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\" [ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\" @@ -618,6 +610,8 @@ filter_direct_node_list() { } update_wan_sets() { + [ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH" + local WAN_IP=$(get_wan_ips ip4) [ -n "$WAN_IP" ] && { ipset -F "$IPSET_WAN" @@ -641,13 +635,13 @@ add_firewall_rule() { ipset -! create $IPSET_LOCAL nethash maxelem 1048576 ipset -! create $IPSET_PROXY_LAN nethash maxelem 1048576 ipset -! create $IPSET_LAN nethash maxelem 1048576 - ipset -! create $IPSET_VPS nethash maxelem 1048576 + ipset -! create $IPSET_VPS iphash maxelem 1048576 ipset -! create $IPSET_WAN nethash maxelem 1048576 ipset -! create $IPSET_LOCAL6 nethash family inet6 maxelem 1048576 ipset -! create $IPSET_PROXY_LAN6 nethash family inet6 maxelem 1048576 ipset -! create $IPSET_LAN6 nethash family inet6 maxelem 1048576 - ipset -! create $IPSET_VPS6 nethash family inet6 maxelem 1048576 + ipset -! create $IPSET_VPS6 iphash family inet6 maxelem 1048576 ipset -! create $IPSET_WAN6 nethash family inet6 maxelem 1048576 get_local_ips ip4 | sed "s/^/add $IPSET_LOCAL /" | ipset -! -R @@ -1025,7 +1019,7 @@ del_firewall_rule() { flush_ipset() { log_i18n 0 "Clear %s." "IPSet" - for _name in $(ipset list | grep "Name: " | grep "passwall2_" | awk '{print $2}'); do + for _name in $(ipset list | grep "Name: " | grep "psw2_" | awk '{print $2}'); do destroy_ipset ${_name} done } @@ -1105,7 +1099,7 @@ start() { } stop() { - [ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh + [ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH" del_firewall_rule destroy_ipset $IPSET_PROXY_LAN destroy_ipset $IPSET_PROXY_LAN6 diff --git a/luci-app-passwall2/root/usr/share/passwall2/monitor.sh b/luci-app-passwall2/root/usr/share/passwall2/monitor.sh index 80e61cae..e072465a 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/monitor.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/monitor.sh @@ -18,7 +18,7 @@ while [ "$ENABLED" -eq 1 ]; do for filename in $(ls ${TMP_SCRIPT_FUNC_PATH} | grep -v "^_"); do cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename}) cmd_check=$(echo $cmd | awk -F '>' '{print $1}') - icount=$(pgrep -f "$(echo $cmd_check)" | wc -l) + icount=$(busybox pgrep -f "$(echo $cmd_check)" | wc -l) if [ $icount = 0 ]; then #echo "${cmd} crashed, restarting." >> /tmp/log/passwall2.log eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 & diff --git a/luci-app-passwall2/root/usr/share/passwall2/nftables.sh b/luci-app-passwall2/root/usr/share/passwall2/nftables.sh index de4043fa..a8655ac3 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/nftables.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/nftables.sh @@ -4,17 +4,17 @@ DIR="$(cd "$(dirname "$0")" && pwd)" MY_PATH=$DIR/nftables.sh UTILS_PATH=$DIR/utils.sh NFTABLE_NAME="inet passwall2" -NFTSET_LOCAL="passwall2_local" -NFTSET_PROXY_LAN="passwall2_proxy_lan" -NFTSET_LAN="passwall2_lan" -NFTSET_VPS="passwall2_vps" -NFTSET_WAN="passwall2_wan" +NFTSET_LOCAL="psw2_local" +NFTSET_PROXY_LAN="psw2_proxy_lan" +NFTSET_LAN="psw2_lan" +NFTSET_VPS="psw2_vps" +NFTSET_WAN="psw2_wan" -NFTSET_LOCAL6="passwall2_local6" -NFTSET_PROXY_LAN6="passwall2_proxy_lan6" -NFTSET_LAN6="passwall2_lan6" -NFTSET_VPS6="passwall2_vps6" -NFTSET_WAN6="passwall2_wan6" +NFTSET_LOCAL6="psw2_local6" +NFTSET_PROXY_LAN6="psw2_proxy_lan6" +NFTSET_LAN6="psw2_lan6" +NFTSET_VPS6="psw2_vps6" +NFTSET_WAN6="psw2_wan6" FWMARK="0x50535732" @@ -231,8 +231,8 @@ gen_shunt_list() { for shunt_id in $shunt_ids; do local shunt_node=$(config_n_get ${node} "${shunt_id}") [ -n "$shunt_node" ] && { - local nftset_v4="passwall2_${node}_${shunt_id}" - local nftset_v6="passwall2_${node}_${shunt_id}6" + local nftset_v4="psw2_${node}_${shunt_id}" + local nftset_v6="psw2_${node}_${shunt_id}6" gen_nftset $nftset_v4 ipv4_addr 0 0 gen_nftset $nftset_v6 ipv6_addr 0 0 local outbound="redirect" @@ -263,14 +263,6 @@ gen_shunt_list() { gen_nftset $direct_nftset6 ipv6_addr 0 0 _SHUNT_LIST6="${_SHUNT_LIST6} ${direct_nftset6}:direct" } - [ "${preloading}" = "1" ] && [ -n "$default_node" ] && { - local nftset_v4="passwall2_${node}_default" - local nftset_v6="passwall2_${node}_default6" - gen_nftset $nftset_v4 ipv4_addr 0 0 - gen_nftset $nftset_v6 ipv6_addr 0 0 - _SHUNT_LIST4="${_SHUNT_LIST4} ${nftset_v4}:${default_outbound}" - _SHUNT_LIST6="${_SHUNT_LIST6} ${nftset_v6}:${default_outbound}" - } } [ -n "${_SHUNT_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\" [ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\" @@ -681,6 +673,8 @@ mwan3_start() { } update_wan_sets() { + [ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH" + local WAN_IP=$(get_wan_ips ip4) [ -n "$WAN_IP" ] && { nft flush set $NFTABLE_NAME $NFTSET_WAN @@ -1073,7 +1067,7 @@ del_firewall_rule() { flush_nftset() { log_i18n 0 "Clear %s." "NFTSet" - for _name in $(nft -a list sets | grep -E "passwall2_" | awk -F 'set ' '{print $2}' | awk '{print $1}'); do + for _name in $(nft -a list sets | grep -E "psw2_" | awk -F 'set ' '{print $2}' | awk '{print $1}'); do destroy_nftset ${_name} done } @@ -1116,7 +1110,7 @@ start() { } stop() { - [ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh + [ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH" del_firewall_rule destroy_nftset $NFTSET_PROXY_LAN destroy_nftset $NFTSET_PROXY_LAN6 diff --git a/luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh b/luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh index b009002b..310a2070 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh @@ -6,7 +6,7 @@ APP_FILE=${APP_PATH}/app.sh flag=0 check_process() { - while pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null; do + while busybox pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null; do sleep 6s done } @@ -63,7 +63,7 @@ test_node() { # Kill the SS plugin process local pid_file="/tmp/etc/${CONFIG}/test_node_${node_id}_plugin.pid" [ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1 - pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 + busybox pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 rm -rf /tmp/etc/${CONFIG}/test_node_${node_id}*.* if [ "${_proxy_status}" -eq 200 ]; then return 0 diff --git a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua index ab9aa6e5..a1dd677b 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua +++ b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua @@ -1688,22 +1688,19 @@ local function processData(szType, content, add_mode, group, sub_cfg) local insecure = params.allowinsecure or params.insecure result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0") result.hysteria2_hop = params.mport + if params["obfs-password"] or params["obfs_password"] then + result.hysteria2_obfs_type = "salamander" + result.hysteria2_obfs_password = params["obfs-password"] or params["obfs_password"] + end if (sub_hysteria2_type == "sing-box" and has_singbox) or (sub_hysteria2_type == "xray" and has_xray) then local is_singbox = sub_hysteria2_type == "sing-box" and has_singbox result.type = is_singbox and 'sing-box' or 'Xray' result.protocol = "hysteria2" - if params["obfs-password"] or params["obfs_password"] then - result.hysteria2_obfs_type = "salamander" - result.hysteria2_obfs_password = params["obfs-password"] or params["obfs_password"] - end result.use_finalmask = (params.fm and params.fm ~= "") and "1" or nil result.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil elseif has_hysteria2 then result.type = "Hysteria2" - if params["obfs-password"] or params["obfs_password"] then - result.hysteria2_obfs = params["obfs-password"] or params["obfs_password"] - end else log(2, i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "Hysteria2", "Hysteria2")) return nil @@ -1761,7 +1758,7 @@ local function processData(szType, content, add_mode, group, sub_cfg) end result.tls_serverName = params.sni result.tls_disable_sni = params.disable_sni - result.tuic_alpn = params.alpn or "default" + result.tuic_alpn = params.alpn or "h3" result.tuic_congestion_control = params.congestion_control or "cubic" result.tuic_udp_relay_mode = params.udp_relay_mode or "native" local insecure = params.allowinsecure or params.insecure or params.allow_insecure @@ -2049,12 +2046,12 @@ local function select_node(nodes, config, parentConfig) end end end - -- First priority: Type + Notes + IP + Port + -- First priority: Type + Notes + IP + Port + Group if not server then for index, node in pairs(nodes) do if config.currentNode.type and config.currentNode.remarks and config.currentNode.address and config.currentNode.port then if node.type and node.remarks and node.address and node.port then - if node.type == config.currentNode.type and node.remarks == config.currentNode.remarks and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then + if node.type == config.currentNode.type and node.remarks == config.currentNode.remarks and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) and node.group == config.currentNode.group then if config.log == nil or config.log == true then log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("First Matching node:") .. " " .. node.remarks) end @@ -2065,12 +2062,12 @@ local function select_node(nodes, config, parentConfig) end end end - -- Second priority: Type + IP + Port + -- Second priority: Type + IP + Port + Group if not server then for index, node in pairs(nodes) do if config.currentNode.type and config.currentNode.address and config.currentNode.port then if node.type and node.address and node.port then - if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then + if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) and node.group == config.currentNode.group then if config.log == nil or config.log == true then log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Second Matching node:") .. " " .. node.remarks) end @@ -2081,12 +2078,12 @@ local function select_node(nodes, config, parentConfig) end end end - -- Third priority: IP + Port + -- Third priority: IP + Port + Group if not server then for index, node in pairs(nodes) do if config.currentNode.address and config.currentNode.port then if node.address and node.port then - if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port then + if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port and node.group == config.currentNode.group then if config.log == nil or config.log == true then log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Third Matching node:") .. " " .. node.remarks) end @@ -2097,12 +2094,12 @@ local function select_node(nodes, config, parentConfig) end end end - -- Fourth priority: IP + -- Fourth priority: IP + Group if not server then for index, node in pairs(nodes) do if config.currentNode.address then if node.address then - if node.address == config.currentNode.address then + if node.address == config.currentNode.address and node.group == config.currentNode.group then if config.log == nil or config.log == true then log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fourth Matching node:") .. " " .. node.remarks) end @@ -2113,12 +2110,12 @@ local function select_node(nodes, config, parentConfig) end end end - -- Fifth priority: remarks + -- Fifth priority: remarks + Group if not server then for index, node in pairs(nodes) do if config.currentNode.remarks then if node.remarks then - if node.remarks == config.currentNode.remarks then + if node.remarks == config.currentNode.remarks and node.group == config.currentNode.group then if config.log == nil or config.log == true then log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fifth Matching node:") .. " " .. node.remarks) end diff --git a/luci-app-passwall2/root/usr/share/passwall2/tasks.sh b/luci-app-passwall2/root/usr/share/passwall2/tasks.sh index 56b1f143..90d14512 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/tasks.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/tasks.sh @@ -22,7 +22,7 @@ do stop_interval_mode=$(expr "$stop_interval_mode" \* 60) if [ -n "$stop_week_mode" ]; then [ "$stop_week_mode" = "8" ] && { - [ "$(expr "$CFG_UPDATE_INT" % "$stop_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG stop > /dev/null 2>&1 & + [ "$(expr "$CFG_UPDATE_INT" % "$stop_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG stop > /dev/null 2>&1 & } } fi @@ -31,7 +31,7 @@ do start_interval_mode=$(expr "$start_interval_mode" \* 60) if [ -n "$start_week_mode" ]; then [ "$start_week_mode" = "8" ] && { - [ "$(expr "$CFG_UPDATE_INT" % "$start_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG start > /dev/null 2>&1 & + [ "$(expr "$CFG_UPDATE_INT" % "$start_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG start > /dev/null 2>&1 & } } fi @@ -40,7 +40,7 @@ do restart_interval_mode=$(expr "$restart_interval_mode" \* 60) if [ -n "$restart_week_mode" ]; then [ "$restart_week_mode" = "8" ] && { - [ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG restart > /dev/null 2>&1 & + [ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG restart > /dev/null 2>&1 & } } fi @@ -50,7 +50,7 @@ do hourupdate=$(expr "$hourupdate" \* 60) if [ "$autoupdate" = "1" ]; then [ "$weekupdate" = "8" ] && { - [ "$(expr "$CFG_UPDATE_INT" % "$hourupdate")" -eq 0 ] && lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 & + [ "$(expr "$CFG_UPDATE_INT" % "$hourupdate")" -eq 0 ] && { lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 & } } fi @@ -73,7 +73,7 @@ do hour_update=$(expr "$hour_update" \* 60) cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g') [ "$week_update" = "8" ] && { - [ "$(expr "$CFG_UPDATE_INT" % "$hour_update")" -eq 0 ] && lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & + [ "$(expr "$CFG_UPDATE_INT" % "$hour_update")" -eq 0 ] && { lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & } } done diff --git a/luci-app-passwall2/root/usr/share/passwall2/test.sh b/luci-app-passwall2/root/usr/share/passwall2/test.sh index f83f4a24..d23bceea 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/test.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/test.sh @@ -49,11 +49,13 @@ url_test_node() { local _tmp_port=$(get_new_port 48900 tcp,udp) NO_REC_PROCESS=1 /usr/share/${CONFIG}/app.sh run_socks flag="url_test_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=url_test_${node_id}.json sleep 2s - local curlx="socks5h://127.0.0.1:${_tmp_port}" - local url=$(config_t_get global_other url_test_url https://www.google.com/generate_204) - result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "${url}") - pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 - rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.json + local probeUrl=$(config_t_get global_other url_test_url https://www.google.com/generate_204) + result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}") + # End the SS plugin process + local pid_file="/tmp/etc/${CONFIG}/url_test_${node_id}_plugin.pid" + [ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1 + busybox pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 + rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.* } echo $result } diff --git a/luci-app-passwall2/root/usr/share/passwall2/utils.sh b/luci-app-passwall2/root/usr/share/passwall2/utils.sh index 0770d326..b345c7ee 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/utils.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/utils.sh @@ -412,7 +412,7 @@ run_process_queue() { for filename in $(ls ${TMP_PROCESS_LIST_PATH}); do cmd=$(cat ${TMP_PROCESS_LIST_PATH}/${filename}) cmd_check=$(echo $cmd | awk -F '>' '{print $1}') - icount=$(pgrep -f "$(echo $cmd_check)" | wc -l) + icount=$(busybox pgrep -f "$(echo $cmd_check)" | wc -l) if [ $icount = 0 ]; then eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 & fi